summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore34
-rw-r--r--COPYING202
-rw-r--r--LICENSE191
-rw-r--r--MANIFEST.in4
-rw-r--r--README301
-rw-r--r--apachekerbauth/apachekerbauth.spec50
-rw-r--r--apachekerbauth/apachekerbauth/var/www/cgi-bin/memcached.py318
-rwxr-xr-xapachekerbauth/build.sh7
-rw-r--r--apachekerbauth/etc/httpd/conf.d/swift-auth.conf (renamed from apachekerbauth/apachekerbauth/etc/httpd/conf.d/swift-auth.conf)7
-rwxr-xr-xapachekerbauth/var/www/cgi-bin/swift-auth (renamed from apachekerbauth/apachekerbauth/var/www/cgi-bin/swift-auth)80
-rw-r--r--doc/DOCUMENTATION299
-rwxr-xr-xmakerpm.sh8
-rw-r--r--setup.py38
-rw-r--r--swiftkerbauth/__init__.py17
-rwxr-xr-xswiftkerbauth/build.sh7
-rw-r--r--swiftkerbauth/kerbauth.py (renamed from swiftkerbauth/swiftkerbauth/swiftkerbauth.py)165
-rw-r--r--swiftkerbauth/swiftkerbauth.spec54
17 files changed, 716 insertions, 1066 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..835fe8b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,34 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
diff --git a/COPYING b/COPYING
deleted file mode 100644
index d645695..0000000
--- a/COPYING
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..37ec93a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..cadec55
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,4 @@
+include LICENSE README
+recursive-include swiftkerbauth *.py
+graft doc
+graft apachekerbauth
diff --git a/README b/README
index 34b4ea7..5200e57 100644
--- a/README
+++ b/README
@@ -1,299 +1,4 @@
-Kerberos Authentication Filter for Red Hat Storage and OpenStack Swift
-----------------------------------------------------------------------
+swiftkerbauth
+=============
-Red Hat Storage not only provides file system access to its data, but
-also object-level access. The latter is implemented with OpenStack
-Swift, and allows containers and objects to be stored and retrieved
-with an HTTP-based API.
-
-Red Hat Storage 2.0 comes with a simple authentication filter that
-defines user accounts as a static list in the Swift configuration
-file. For this project, we implemented a new authentication filter
-that uses Kerberos tickets for single sign on authentication, and
-grants administrator permissions based on the user's group membership
-in a directory service like Red Hat Enterprise Linux Identity
-Management or Microsoft Active Directory.
-
-* Building
-
-To build the swiftkerbauth and apachekerbauth RPM packages, change
-into the respective directory and run
-
- ./build.sh
-
-* Installation
-
-** Swift Server
-
-Install the swiftkerbauth RPM on all Red Hat Storage nodes that will
-provide object-level access via Swift.
-
-To active the Kerberos authentication filter, add "kerbauth" in the
-/etc/swift/proxy-server.conf pipeline parameter:
-
- [pipeline:main]
- pipeline = healthcheck cache kerbauth proxy-server
-
-Set the URL of the Apache server that will be used for authentication
-with the ext_authentication_url parameter in the same file:
-
- [filter:kerbauth]
- paste.filter_factory = swiftkerbauth:filter_factory
- ext_authentication_url = http://AUTHENTICATION_SERVER/cgi-bin/swift-auth
-
-If the Swift server is not one of your Gluster nodes, edit
-/etc/swift/fs.conf and change the following lines in the DEFAULT
-section:
-
- mount_ip = RHS_NODE_HOSTNAME
- remote_cluster = yes
-
-Activate the changes by running
-
- swift-init main restart
-
-For troubleshooting, check /var/log/messages.
-
-** Authentication Server
-
-On the authentication server, install the apachekerbauth package.
-
-Edit /etc/httpd/conf.d/swift-auth.conf and set the KrbAuthRealms and
-Krb5KeyTab parameters.
-
-The keytab must contain a HTTP/$HOSTNAME principal. Usually, you will
-have to create the Kerberos principal on the KDC, export it, and copy
-it to a keytab file on the Apache server.
-
-If SELinux is enabled, allow Apache to connect to memcache and
-activate the changes by running
-
- setsebool -P httpd_can_network_connect 1
- setsebool -P httpd_can_network_memcache 1
-
- service httpd reload
-
-For troubleshooting, see /var/log/httpd/error_log.
-
-* Testing
-
-The tests were done with curl on a machine set up as an IDM client,
-using the Gluster volume rhs_ufo1.
-
-In IDM, we created the following user groups:
-
-- auth_reseller_admin
- Users in this group get full access to all Swift accounts.
-
-- auth_rhs_ufo1
- Users in this group get full access to the rhs_ufo1 Swift account.
-
-Next, we created the following users in IDM:
-
-- auth_admin
- Member of the auth_reseller_admin group
-
-- rhs_ufo1_admin
- Member of the auth_rhs_ufo1 group
-
-- jsmith
- No relevant group membership
-
-The authentication tokens were then retrieved with the following
-commands:
-
- kinit auth_admin
- curl -v -u : --negotiate --location-trusted \
- http://rhs1.example.com:8080/auth/v1.0
-
- kinit rhs_ufo1_admin
- curl -v -u : --negotiate --location-trusted \
- http://rhs1.example.com:8080/auth/v1.0
-
- kinit jsmith
- curl -v -u : --negotiate --location-trusted \
- http://rhs1.example.com:8080/auth/v1.0
-
-Each of these commands should output the following two lines:
-
-< X-Auth-Token: AUTH_tk4097146ed3814e026209556eeb121fe0
-...
-<pre>1365195860 / auth_admin,auth_reseller_admin</pre>
-
-The first line contains the authentication token that is used in
-subsequent requests.
-
-The second line is printed by the swift-auth CGI script for debugging
-- it lists the token expiration (in seconds since January 1, 1970) and
-the user's groups.
-
-Next, we try to get information about the Swift account, replacing the
-AUTH_tk* with one of the tokens we got with the commands above. This
-should display statistics, and the list of container names when used
-with the the admin users. For jsmith, you should get a 403 Forbidden
-error.
-
- curl -v -X GET \
- -H 'X-Auth-Token: AUTH_tk4097146ed3814e026209556eeb121fe0' \
- http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1
-
-With one of the admin accounts, create a new container and a new
-object in that container:
-
- curl -v -X PUT \
- -H 'X-Auth-Token: AUTH_tk4097146ed3814e026209556eeb121fe0' \
- http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures
-
- curl -v -X PUT \
- -H 'X-Auth-Token: AUTH_tk4097146ed3814e026209556eeb121fe0' \
- -H 'Content-Length: 0' \
- http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures/pic1.png
-
-Grant permission for jsmith to list and download objects from the
-pictures container:
-
- curl -v -X POST \
- -H 'X-Auth-Token: AUTH_tkdbf7725c1e4ad1ebe9ab0d7098d425f2' \
- -H 'X-Container-Read: jsmith' \
- http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures
-
-List the container contents using the authentication token for jsmith:
-
- curl -v -X GET \
- -H 'X-Auth-Token: AUTH_tkef8b417ac0c2a73a80ab3b8db85254e2' \
- http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures
-
-Try to access a resource without an authentication token. This will
-return a 303 redirect:
-
- curl -v -X GET \
- http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures/pic1.png
-
-For curl to follow the redirect, you need to specify additional
-options. With these, and with a current Kerberos ticket, you should
-get the Kerberos user's cached authentication token, or a new one if
-the previous token has expired.
-
- curl -v -u : --negotiate --location-trusted -X GET \
- http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures/pic1.png
-
-* Implementation Details
-
-** Architecture
-
-The Swift API is HTTP-based. As described in the Swift documentation
-[1], clients first make a request to an authentication URL, providing
-a username and password. The reply contains a token which is used in
-all subsequent requests.
-
-Swift has a chain of filters through which all client requests go. The
-filters to use are configured with the pipeline parameter in
-/etc/swift/proxy-server.conf:
-
- [pipeline:main]
- pipeline = healthcheck cache tempauth proxy-server
-
-For the single sign authentication, we added a new filter called
-"kerbauth" and put it into the filter pipeline in place of tempauth.
-
-The filter checks the URL for each client request. If it matches the
-authentication URL, the client is redirected to a URL on a different
-server. The URL is handled by a CGI script, which is set up to
-authenticate the client with Kerberos negotiation, retrieve the user's
-system groups [2], store them in a memcache ring shared with the Swift
-server, and return the authentication token to the client.
-
-When the client provides the token as part of a resource request, the
-kerbauth filter checks it against its memcache, grants administrator
-rights based on the group membership retrieved from memcache, and
-either grants or denies the resource access.
-
-[1] http://docs.openstack.org/api/openstack-object-storage/1.0/content/authentication-object-dev-guide.html
-
-[2] The user data and system groups are usually provided by Red Hat
- Enterprise Linux identity Management or Microsoft Active
- Directory. The script relies on the system configuration to be set
- accordingly (/etc/nsswitch.conf).
-
-** swiftkerbauth.py
-
-The script /usr/lib/python2.6/site-packages/swiftkerbauth.py began as
-a copy of the tempauth.py script from
-/usr/lib/python2.6/site-packages/swift/common/middleware. It contains
-the following modifications, among others:
-
-In the __init__ method, we read the ext_authentication_url parameter
-from /etc/swift/proxy-server.conf. This is the URL that clients are
-redirected to when they access either the Swift authentication URL, or
-when they request a resource without a valid authentication token.
-
-The configuration in proxy-server.conf looks like this:
-
- [filter:kerbauth]
- paste.filter_factory = swiftkerbauth:filter_factory
- ext_authentication_url = http://rhel6-4.localdomain/cgi-bin/swift-auth
-
-The authorize method was changed so that global administrator rights
-are granted if the user is a member of the auth_reseller_admin
-group. Administrator rights for a specific account like vol1 are
-granted if the user is a member of the auth_vol1 group. [3]
-
-The denied_response method was changed to return a HTTP redirect to
-the external authentication URL if no valid token was provided by the
-client.
-
-Most of the handle_get_token method was moved to the external
-authentication script. This method now returns a HTTP redirect.
-
-In the __call__ and get_groups method, we removed support for the
-HTTP_AUTHORIZATION header, which is only needed when Amazon S3 is
-used.
-
-Like tempauth.py, swiftkerbauth.py uses a Swift wrapper to access
-memcache. This wrapper converts the key to an MD5 hash and uses the
-hash value to determine on which of a pre-defined list of servers to
-store the data.
-
-[3] "auth" is the default reseller prefix, and would be different if
- the reseller_prefix parameter in proxy-server.conf was set.
-
-** swift-auth CGI Script
-
-swift-auth resides on an Apache server and assumes that Apache is
-configured to authenticate the user before this script is
-executed. The script retrieves the username from the REMOTE_USER
-environment variable, and checks if there already is a token for this
-user in the memcache ring. If not, it generates a new one, retrieves
-the user's system groups with "id -Gn USERNAME", stores this
-information in the memcache ring, and returns the token to the client.
-
-For the Swift filter to be able to find the information, it was
-important to use the Swift memcached module. Because we don't want to
-require a full Swift installation on the authentication server,
-/usr/lib/python2.6/site-packages/swift/common/memcached.py from the
-Swift server was copied to /var/www/cgi-bin on the Apache server.
-
-To allow the CGI script to connect to memcache, the SELinux booleans
-httpd_can_network_connect and httpd_can_network_memcache had to be
-set.
-
-The tempauth filter uses the uuid module to generate token
-strings. This module creates and runs temporary files, which leads to
-AVC denial messages in /var/log/audit/audit.log when used from an
-Apache CGI script. While the module still works, the audit log would
-grow quickly. Instead of writing an SELinux policy module to allow or
-to silently ignore these accesses, the swift-auth script uses the
-"random" module for generating token strings.
-
-Red Hat Enterprise Linux 6 comes with Python 2.6 which only provides
-method to list the locally defined user groups. To include groups from
-Red Hat Enterprise Linux Identity Management and in the future from
-Active Directory, the "id" command is run in a subprocess.
-
-* Reference Material
-
-Red Hat Storage Administration Guide:
-https://access.redhat.com/knowledge/docs/Red_Hat_Storage/
-
-Swift Documentation:
-http://docs.openstack.org/developer/swift/
+Kerberos Authentication filter for Openstack Swift
diff --git a/apachekerbauth/apachekerbauth.spec b/apachekerbauth/apachekerbauth.spec
deleted file mode 100644
index cc6210a..0000000
--- a/apachekerbauth/apachekerbauth.spec
+++ /dev/null
@@ -1,50 +0,0 @@
-Name: apachekerbauth
-Version: 1.0
-Release: 3
-Summary: Kerberos authentication filter for Swift
-
-Group: System Environment/Base
-License: GPL
-Source: %{name}.tar.gz
-BuildRoot: %{_tmppath}/%{name}-root
-
-Requires: httpd >= 2.2.15
-Requires: mod_auth_kerb >= 5.4
-
-%description
-Python CGI script which is used by the swiftkerbauth package to
-authenticate client requests using Kerberos.
-
-%prep
-%setup -q -n %{name}
-
-%build
-
-%install
-rm -rf $RPM_BUILD_ROOT
-
-mkdir -p \
- $RPM_BUILD_ROOT/etc/httpd/conf.d \
- $RPM_BUILD_ROOT/var/www/cgi-bin
-
-install -m 644 etc/httpd/conf.d/* \
- $RPM_BUILD_ROOT/etc/httpd/conf.d
-
-install -m 644 var/www/cgi-bin/memcached.py \
- $RPM_BUILD_ROOT/var/www/cgi-bin
-
-install var/www/cgi-bin/swift-auth \
- $RPM_BUILD_ROOT/var/www/cgi-bin
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%files
-%defattr(-,root,root,-)
-%config /etc/httpd/conf.d/swift-auth.conf
-/var/www/cgi-bin/memcached.py
-/var/www/cgi-bin/swift-auth
-
-%changelog
-* Fri Apr 5 2013 Carsten Clasohm <clasohm@redhat.com> - 1.0-1
-- initial build
diff --git a/apachekerbauth/apachekerbauth/var/www/cgi-bin/memcached.py b/apachekerbauth/apachekerbauth/var/www/cgi-bin/memcached.py
deleted file mode 100644
index ecd9332..0000000
--- a/apachekerbauth/apachekerbauth/var/www/cgi-bin/memcached.py
+++ /dev/null
@@ -1,318 +0,0 @@
-# Copyright (c) 2010-2012 OpenStack, LLC.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Lucid comes with memcached: v1.4.2. Protocol documentation for that
-version is at:
-
-http://github.com/memcached/memcached/blob/1.4.2/doc/protocol.txt
-"""
-
-import cPickle as pickle
-import logging
-import socket
-import time
-from bisect import bisect
-from hashlib import md5
-
-DEFAULT_MEMCACHED_PORT = 11211
-
-CONN_TIMEOUT = 0.3
-IO_TIMEOUT = 2.0
-PICKLE_FLAG = 1
-NODE_WEIGHT = 50
-PICKLE_PROTOCOL = 2
-TRY_COUNT = 3
-
-# if ERROR_LIMIT_COUNT errors occur in ERROR_LIMIT_TIME seconds, the server
-# will be considered failed for ERROR_LIMIT_DURATION seconds.
-ERROR_LIMIT_COUNT = 10
-ERROR_LIMIT_TIME = 60
-ERROR_LIMIT_DURATION = 60
-
-
-def md5hash(key):
- return md5(key).hexdigest()
-
-
-class MemcacheConnectionError(Exception):
- pass
-
-
-class MemcacheRing(object):
- """
- Simple, consistent-hashed memcache client.
- """
-
- def __init__(self, servers, connect_timeout=CONN_TIMEOUT,
- io_timeout=IO_TIMEOUT, tries=TRY_COUNT):
- self._ring = {}
- self._errors = dict(((serv, []) for serv in servers))
- self._error_limited = dict(((serv, 0) for serv in servers))
- for server in sorted(servers):
- for i in xrange(NODE_WEIGHT):
- self._ring[md5hash('%s-%s' % (server, i))] = server
- self._tries = tries if tries <= len(servers) else len(servers)
- self._sorted = sorted(self._ring.keys())
- self._client_cache = dict(((server, []) for server in servers))
- self._connect_timeout = connect_timeout
- self._io_timeout = io_timeout
-
- def _exception_occurred(self, server, e, action='talking'):
- if isinstance(e, socket.timeout):
- logging.error(_("Timeout %(action)s to memcached: %(server)s"),
- {'action': action, 'server': server})
- else:
- logging.exception(_("Error %(action)s to memcached: %(server)s"),
- {'action': action, 'server': server})
- now = time.time()
- self._errors[server].append(time.time())
- if len(self._errors[server]) > ERROR_LIMIT_COUNT:
- self._errors[server] = [err for err in self._errors[server]
- if err > now - ERROR_LIMIT_TIME]
- if len(self._errors[server]) > ERROR_LIMIT_COUNT:
- self._error_limited[server] = now + ERROR_LIMIT_DURATION
- logging.error(_('Error limiting server %s'), server)
-
- def _get_conns(self, key):
- """
- Retrieves a server conn from the pool, or connects a new one.
- Chooses the server based on a consistent hash of "key".
- """
- pos = bisect(self._sorted, key)
- served = []
- while len(served) < self._tries:
- pos = (pos + 1) % len(self._sorted)
- server = self._ring[self._sorted[pos]]
- if server in served:
- continue
- served.append(server)
- if self._error_limited[server] > time.time():
- continue
- try:
- fp, sock = self._client_cache[server].pop()
- yield server, fp, sock
- except IndexError:
- try:
- if ':' in server:
- host, port = server.split(':')
- else:
- host = server
- port = DEFAULT_MEMCACHED_PORT
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- sock.settimeout(self._connect_timeout)
- sock.connect((host, int(port)))
- sock.settimeout(self._io_timeout)
- yield server, sock.makefile(), sock
- except Exception, e:
- self._exception_occurred(server, e, 'connecting')
-
- def _return_conn(self, server, fp, sock):
- """ Returns a server connection to the pool """
- self._client_cache[server].append((fp, sock))
-
- def set(self, key, value, serialize=True, timeout=0):
- """
- Set a key/value pair in memcache
-
- :param key: key
- :param value: value
- :param serialize: if True, value is pickled before sending to memcache
- :param timeout: ttl in memcache
- """
- key = md5hash(key)
- if timeout > 0:
- timeout += time.time()
- flags = 0
- if serialize:
- value = pickle.dumps(value, PICKLE_PROTOCOL)
- flags |= PICKLE_FLAG
- for (server, fp, sock) in self._get_conns(key):
- try:
- sock.sendall('set %s %d %d %s noreply\r\n%s\r\n' % \
- (key, flags, timeout, len(value), value))
- self._return_conn(server, fp, sock)
- return
- except Exception, e:
- self._exception_occurred(server, e)
-
- def get(self, key):
- """
- Gets the object specified by key. It will also unpickle the object
- before returning if it is pickled in memcache.
-
- :param key: key
- :returns: value of the key in memcache
- """
- key = md5hash(key)
- value = None
- for (server, fp, sock) in self._get_conns(key):
- try:
- sock.sendall('get %s\r\n' % key)
- line = fp.readline().strip().split()
- while line[0].upper() != 'END':
- if line[0].upper() == 'VALUE' and line[1] == key:
- size = int(line[3])
- value = fp.read(size)
- if int(line[2]) & PICKLE_FLAG:
- value = pickle.loads(value)
- fp.readline()
- line = fp.readline().strip().split()
- self._return_conn(server, fp, sock)
- return value
- except Exception, e:
- self._exception_occurred(server, e)
-
- def incr(self, key, delta=1, timeout=0):
- """
- Increments a key which has a numeric value by delta.
- If the key can't be found, it's added as delta or 0 if delta < 0.
- If passed a negative number, will use memcached's decr. Returns
- the int stored in memcached
- Note: The data memcached stores as the result of incr/decr is
- an unsigned int. decr's that result in a number below 0 are
- stored as 0.
-
- :param key: key
- :param delta: amount to add to the value of key (or set as the value
- if the key is not found) will be cast to an int
- :param timeout: ttl in memcache
- :raises MemcacheConnectionError:
- """
- key = md5hash(key)
- command = 'incr'
- if delta < 0:
- command = 'decr'
- delta = str(abs(int(delta)))
- for (server, fp, sock) in self._get_conns(key):
- try:
- sock.sendall('%s %s %s\r\n' % (command, key, delta))
- line = fp.readline().strip().split()
- if line[0].upper() == 'NOT_FOUND':
- add_val = delta
- if command == 'decr':
- add_val = '0'
- sock.sendall('add %s %d %d %s\r\n%s\r\n' % \
- (key, 0, timeout, len(add_val), add_val))
- line = fp.readline().strip().split()
- if line[0].upper() == 'NOT_STORED':
- sock.sendall('%s %s %s\r\n' % (command, key, delta))
- line = fp.readline().strip().split()
- ret = int(line[0].strip())
- else:
- ret = int(add_val)
- else:
- ret = int(line[0].strip())
- self._return_conn(server, fp, sock)
- return ret
- except Exception, e:
- self._exception_occurred(server, e)
- raise MemcacheConnectionError("No Memcached connections succeeded.")
-
- def decr(self, key, delta=1, timeout=0):
- """
- Decrements a key which has a numeric value by delta. Calls incr with
- -delta.
-
- :param key: key
- :param delta: amount to subtract to the value of key (or set the
- value to 0 if the key is not found) will be cast to
- an int
- :param timeout: ttl in memcache
- :raises MemcacheConnectionError:
- """
- self.incr(key, delta=-delta, timeout=timeout)
-
- def delete(self, key):
- """
- Deletes a key/value pair from memcache.
-
- :param key: key to be deleted
- """
- key = md5hash(key)
- for (server, fp, sock) in self._get_conns(key):
- try:
- sock.sendall('delete %s noreply\r\n' % key)
- self._return_conn(server, fp, sock)
- return
- except Exception, e:
- self._exception_occurred(server, e)
-
- def set_multi(self, mapping, server_key, serialize=True, timeout=0):
- """
- Sets multiple key/value pairs in memcache.
-
- :param mapping: dictonary of keys and values to be set in memcache
- :param servery_key: key to use in determining which server in the ring
- is used
- :param serialize: if True, value is pickled before sending to memcache
- :param timeout: ttl for memcache
- """
- server_key = md5hash(server_key)
- if timeout > 0:
- timeout += time.time()
- msg = ''
- for key, value in mapping.iteritems():
- key = md5hash(key)
- flags = 0
- if serialize:
- value = pickle.dumps(value, PICKLE_PROTOCOL)
- flags |= PICKLE_FLAG
- msg += ('set %s %d %d %s noreply\r\n%s\r\n' %
- (key, flags, timeout, len(value), value))
- for (server, fp, sock) in self._get_conns(server_key):
- try:
- sock.sendall(msg)
- self._return_conn(server, fp, sock)
- return
- except Exception, e:
- self._exception_occurred(server, e)
-
- def get_multi(self, keys, server_key):
- """
- Gets multiple values from memcache for the given keys.
-
- :param keys: keys for values to be retrieved from memcache
- :param servery_key: key to use in determining which server in the ring
- is used
- :returns: list of values
- """
- server_key = md5hash(server_key)
- keys = [md5hash(key) for key in keys]
- for (server, fp, sock) in self._get_conns(server_key):
- try:
- sock.sendall('get %s\r\n' % ' '.join(keys))
- line = fp.readline().strip().split()
- responses = {}
- while line[0].upper() != 'END':
- if line[0].upper() == 'VALUE':
- size = int(line[3])
- value = fp.read(size)
- if int(line[2]) & PICKLE_FLAG:
- value = pickle.loads(value)
- responses[line[1]] = value
- fp.readline()
- line = fp.readline().strip().split()
- values = []
- for key in keys:
- if key in responses:
- values.append(responses[key])
- else:
- values.append(None)
- self._return_conn(server, fp, sock)
- return values
- except Exception, e:
- self._exception_occurred(server, e)
diff --git a/apachekerbauth/build.sh b/apachekerbauth/build.sh
deleted file mode 100755
index 6db04ac..0000000
--- a/apachekerbauth/build.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-tar -cz --exclude=.svn -f ~/rpmbuild/SOURCES/apachekerbauth.tar.gz apachekerbauth
-
-rpmbuild --target noarch --clean -bb apachekerbauth.spec
-
-rm ~/rpmbuild/SOURCES/apachekerbauth.tar.gz
diff --git a/apachekerbauth/apachekerbauth/etc/httpd/conf.d/swift-auth.conf b/apachekerbauth/etc/httpd/conf.d/swift-auth.conf
index ba2b249..68472d8 100644
--- a/apachekerbauth/apachekerbauth/etc/httpd/conf.d/swift-auth.conf
+++ b/apachekerbauth/etc/httpd/conf.d/swift-auth.conf
@@ -3,7 +3,10 @@
AuthName "Swift Authentication"
KrbMethodNegotiate On
KrbMethodK5Passwd On
+ KrbSaveCredentials On
+ KrbServiceName HTTP/client.example.com
KrbAuthRealms EXAMPLE.COM
- Krb5KeyTab /etc/httpd/conf/apache.keytab
- require valid-user
+ Krb5KeyTab /etc/httpd/conf/http.keytab
+ KrbVerifyKDC Off
+ Require valid-user
</Location>
diff --git a/apachekerbauth/apachekerbauth/var/www/cgi-bin/swift-auth b/apachekerbauth/var/www/cgi-bin/swift-auth
index 30f98b5..6173408 100755
--- a/apachekerbauth/apachekerbauth/var/www/cgi-bin/swift-auth
+++ b/apachekerbauth/var/www/cgi-bin/swift-auth
@@ -14,20 +14,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Requires the python-memcached package to be installed.
-#
# Requires the following command to be run:
# setsebool -P httpd_can_network_connect 1
# setsebool -P httpd_can_network_memcache 1
import cgi
-import json
-from memcached import MemcacheRing
+from swift.common.memcached import MemcacheRing
import os
+import grp
import random
import re
import subprocess
-from time import time
+from time import time, ctime
# After how many seconds the cached information about an authentication
# token is discarded.
@@ -38,14 +36,15 @@ TOKEN_LIFE = 86400
# into a configuration parameter.
RESELLER_PREFIX = 'AUTH_'
-MEMCACHE_SERVERS = ['rhs1.example.com:11211']
+MEMCACHE_SERVERS = ['127.0.0.1:11211']
+
+DEBUG_HEADERS = True
def main():
remote_user = os.environ['REMOTE_USER']
-
matches = re.match('([^@]+)@.*', remote_user)
if not matches:
- raise RuntimeError("Malformed REMOTE_USER \"%s\"" % remote_user)
+ raise RuntimeError("Malformed REMOTE_USER \"%s\"" % remote_user)
username = matches.group(1)
@@ -56,59 +55,66 @@ def main():
token = None
candidate_token = mc.get(memcache_user_key)
if candidate_token:
- memcache_token_key = '%s/token/%s' % (RESELLER_PREFIX, candidate_token)
- cached_auth_data = mc.get(memcache_token_key)
- if cached_auth_data:
- expires, groups = cached_auth_data
- if expires > time():
- token = candidate_token
+ memcache_token_key = '%s/token/%s' % (RESELLER_PREFIX, candidate_token)
+ cached_auth_data = mc.get(memcache_token_key)
+ if cached_auth_data:
+ expires, groups = cached_auth_data
+ if expires > time():
+ token = candidate_token
if not token:
- # We don't use uuid.uuid4() here because importing the uuid module
- # causes (harmless) SELinux denials in the audit log on RHEL 6. If this
- # is a security concern, a custom SELinux policy module could be written
- # to not log those denials.
+ # We don't use uuid.uuid4() here because importing the uuid module
+ # causes (harmless) SELinux denials in the audit log on RHEL 6. If this
+ # is a security concern, a custom SELinux policy module could be
+ # written to not log those denials.
r = random.SystemRandom()
- token = '%stk%s' % (RESELLER_PREFIX,
- ''.join(r.choice('abcdef0123456789') for x in range(32)))
+ token = '%stk%s' % \
+ (RESELLER_PREFIX,
+ ''.join(r.choice('abcdef0123456789') for x in range(32)))
# Retrieve the numerical group IDs. We cannot list the group names
# because group names from Active Directory may contain spaces, and
# we wouldn't be able to split the list of group names into its
# elements.
- p = subprocess.Popen(['id', '-G', username], stdout=subprocess.PIPE)
- if p.wait() != 0:
- raise RuntimeError("Failure running id -G for %s" % remote_user)
+ p = subprocess.Popen(['id', '-G', username], stdout=subprocess.PIPE)
+ if p.wait() != 0:
+ raise RuntimeError("Failure running id -G for %s" % remote_user)
- (p_stdout, p_stderr) = p.communicate()
+ (p_stdout, p_stderr) = p.communicate()
# Convert the group numbers into group names.
groups = []
for gid in p_stdout.strip().split(" "):
groups.append(grp.getgrgid(int(gid))[0])
- # The first element of the list is considered a unique identifier
- # for the user. We add the username to accomplish this.
+ # The first element of the list is considered a unique identifier
+ # for the user. We add the username to accomplish this.
if username in groups:
groups.remove(username)
- groups = [username] + groups
+ groups = [username] + groups
- groups = ','.join(groups)
+ groups = ','.join(groups)
- expires = time() + TOKEN_LIFE
- auth_data = (expires, groups)
+ expires = time() + TOKEN_LIFE
+ auth_data = (expires, groups)
- memcache_token_key = "%s/token/%s" % (RESELLER_PREFIX, token)
- mc.set(memcache_token_key, auth_data, timeout=TOKEN_LIFE)
+ memcache_token_key = "%s/token/%s" % (RESELLER_PREFIX, token)
+ mc.set(memcache_token_key, auth_data, timeout=TOKEN_LIFE)
- # Record the token with the user info for future use.
- memcache_user_key = '%s/user/%s' % (RESELLER_PREFIX, username)
- mc.set(memcache_user_key, token, timeout=TOKEN_LIFE)
+ # Record the token with the user info for future use.
+ memcache_user_key = '%s/user/%s' % (RESELLER_PREFIX, username)
+ mc.set(memcache_user_key, token, timeout=TOKEN_LIFE)
- print "X-Auth-Token: %s\n" % token
+ print "X-Auth-Token: %s" % token
# For debugging.
- print "<pre>%i / %s</pre>" % mc.get(memcache_token_key)
+ if DEBUG_HEADERS:
+ print "X-Debug-Remote-User: %s" % username
+ print "X-Debug-Groups: %s" % groups
+ print "X-Debug-Token-Life: %ss" % TOKEN_LIFE
+ print "X-Debug-Token-Expires: %s" % ctime(expires)
+
+ print ""
try:
print("Content-Type: text/html")
diff --git a/doc/DOCUMENTATION b/doc/DOCUMENTATION
new file mode 100644
index 0000000..5b36b24
--- /dev/null
+++ b/doc/DOCUMENTATION
@@ -0,0 +1,299 @@
+Kerberos Authentication Filter for Red Hat Storage and OpenStack Swift
+----------------------------------------------------------------------
+
+Red Hat Storage not only provides file system access to its data, but
+also object-level access. The latter is implemented with OpenStack
+Swift, and allows containers and objects to be stored and retrieved
+with an HTTP-based API.
+
+Red Hat Storage 2.0 comes with a simple authentication filter that
+defines user accounts as a static list in the Swift configuration
+file. For this project, we implemented a new authentication filter
+that uses Kerberos tickets for single sign on authentication, and
+grants administrator permissions based on the user's group membership
+in a directory service like Red Hat Enterprise Linux Identity
+Management or Microsoft Active Directory.
+
+* Building
+
+To build the swiftkerbauth and apachekerbauth RPM packages, change
+into the respective directory and run
+
+ ./build.sh
+
+* Installation
+
+** Swift Server
+
+Install the swiftkerbauth RPM on all Red Hat Storage nodes that will
+provide object-level access via Swift.
+
+To active the Kerberos authentication filter, add "kerbauth" in the
+/etc/swift/proxy-server.conf pipeline parameter:
+
+ [pipeline:main]
+ pipeline = healthcheck cache kerbauth proxy-server
+
+Set the URL of the Apache server that will be used for authentication
+with the ext_authentication_url parameter in the same file:
+
+ [filter:kerbauth]
+ paste.filter_factory = swiftkerbauth:filter_factory
+ ext_authentication_url = http://AUTHENTICATION_SERVER/cgi-bin/swift-auth
+
+If the Swift server is not one of your Gluster nodes, edit
+/etc/swift/fs.conf and change the following lines in the DEFAULT
+section:
+
+ mount_ip = RHS_NODE_HOSTNAME
+ remote_cluster = yes
+
+Activate the changes by running
+
+ swift-init main restart
+
+For troubleshooting, check /var/log/messages.
+
+** Authentication Server
+
+On the authentication server, install the apachekerbauth package.
+
+Edit /etc/httpd/conf.d/swift-auth.conf and set the KrbAuthRealms and
+Krb5KeyTab parameters.
+
+The keytab must contain a HTTP/$HOSTNAME principal. Usually, you will
+have to create the Kerberos principal on the KDC, export it, and copy
+it to a keytab file on the Apache server.
+
+If SELinux is enabled, allow Apache to connect to memcache and
+activate the changes by running
+
+ setsebool -P httpd_can_network_connect 1
+ setsebool -P httpd_can_network_memcache 1
+
+ service httpd reload
+
+For troubleshooting, see /var/log/httpd/error_log.
+
+* Testing
+
+The tests were done with curl on a machine set up as an IDM client,
+using the Gluster volume rhs_ufo1.
+
+In IDM, we created the following user groups:
+
+- auth_reseller_admin
+ Users in this group get full access to all Swift accounts.
+
+- auth_rhs_ufo1
+ Users in this group get full access to the rhs_ufo1 Swift account.
+
+Next, we created the following users in IDM:
+
+- auth_admin
+ Member of the auth_reseller_admin group
+
+- rhs_ufo1_admin
+ Member of the auth_rhs_ufo1 group
+
+- jsmith
+ No relevant group membership
+
+The authentication tokens were then retrieved with the following
+commands:
+
+ kinit auth_admin
+ curl -v -u : --negotiate --location-trusted \
+ http://rhs1.example.com:8080/auth/v1.0
+
+ kinit rhs_ufo1_admin
+ curl -v -u : --negotiate --location-trusted \
+ http://rhs1.example.com:8080/auth/v1.0
+
+ kinit jsmith
+ curl -v -u : --negotiate --location-trusted \
+ http://rhs1.example.com:8080/auth/v1.0
+
+Each of these commands should output the following two lines:
+
+< X-Auth-Token: AUTH_tk4097146ed3814e026209556eeb121fe0
+...
+<pre>1365195860 / auth_admin,auth_reseller_admin</pre>
+
+The first line contains the authentication token that is used in
+subsequent requests.
+
+The second line is printed by the swift-auth CGI script for debugging
+- it lists the token expiration (in seconds since January 1, 1970) and
+the user's groups.
+
+Next, we try to get information about the Swift account, replacing the
+AUTH_tk* with one of the tokens we got with the commands above. This
+should display statistics, and the list of container names when used
+with the the admin users. For jsmith, you should get a 403 Forbidden
+error.
+
+ curl -v -X GET \
+ -H 'X-Auth-Token: AUTH_tk4097146ed3814e026209556eeb121fe0' \
+ http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1
+
+With one of the admin accounts, create a new container and a new
+object in that container:
+
+ curl -v -X PUT \
+ -H 'X-Auth-Token: AUTH_tk4097146ed3814e026209556eeb121fe0' \
+ http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures
+
+ curl -v -X PUT \
+ -H 'X-Auth-Token: AUTH_tk4097146ed3814e026209556eeb121fe0' \
+ -H 'Content-Length: 0' \
+ http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures/pic1.png
+
+Grant permission for jsmith to list and download objects from the
+pictures container:
+
+ curl -v -X POST \
+ -H 'X-Auth-Token: AUTH_tkdbf7725c1e4ad1ebe9ab0d7098d425f2' \
+ -H 'X-Container-Read: jsmith' \
+ http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures
+
+List the container contents using the authentication token for jsmith:
+
+ curl -v -X GET \
+ -H 'X-Auth-Token: AUTH_tkef8b417ac0c2a73a80ab3b8db85254e2' \
+ http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures
+
+Try to access a resource without an authentication token. This will
+return a 303 redirect:
+
+ curl -v -X GET \
+ http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures/pic1.png
+
+For curl to follow the redirect, you need to specify additional
+options. With these, and with a current Kerberos ticket, you should
+get the Kerberos user's cached authentication token, or a new one if
+the previous token has expired.
+
+ curl -v -u : --negotiate --location-trusted -X GET \
+ http://rhs1.example.com:8080/v1/AUTH_rhs_ufo1/pictures/pic1.png
+
+* Implementation Details
+
+** Architecture
+
+The Swift API is HTTP-based. As described in the Swift documentation
+[1], clients first make a request to an authentication URL, providing
+a username and password. The reply contains a token which is used in
+all subsequent requests.
+
+Swift has a chain of filters through which all client requests go. The
+filters to use are configured with the pipeline parameter in
+/etc/swift/proxy-server.conf:
+
+ [pipeline:main]
+ pipeline = healthcheck cache tempauth proxy-server
+
+For the single sign authentication, we added a new filter called
+"kerbauth" and put it into the filter pipeline in place of tempauth.
+
+The filter checks the URL for each client request. If it matches the
+authentication URL, the client is redirected to a URL on a different
+server. The URL is handled by a CGI script, which is set up to
+authenticate the client with Kerberos negotiation, retrieve the user's
+system groups [2], store them in a memcache ring shared with the Swift
+server, and return the authentication token to the client.
+
+When the client provides the token as part of a resource request, the
+kerbauth filter checks it against its memcache, grants administrator
+rights based on the group membership retrieved from memcache, and
+either grants or denies the resource access.
+
+[1] http://docs.openstack.org/api/openstack-object-storage/1.0/content/authentication-object-dev-guide.html
+
+[2] The user data and system groups are usually provided by Red Hat
+ Enterprise Linux identity Management or Microsoft Active
+ Directory. The script relies on the system configuration to be set
+ accordingly (/etc/nsswitch.conf).
+
+** swiftkerbauth.py
+
+The script /usr/lib/python2.6/site-packages/swiftkerbauth.py began as
+a copy of the tempauth.py script from
+/usr/lib/python2.6/site-packages/swift/common/middleware. It contains
+the following modifications, among others:
+
+In the __init__ method, we read the ext_authentication_url parameter
+from /etc/swift/proxy-server.conf. This is the URL that clients are
+redirected to when they access either the Swift authentication URL, or
+when they request a resource without a valid authentication token.
+
+The configuration in proxy-server.conf looks like this:
+
+ [filter:kerbauth]
+ paste.filter_factory = swiftkerbauth:filter_factory
+ ext_authentication_url = http://rhel6-4.localdomain/cgi-bin/swift-auth
+
+The authorize method was changed so that global administrator rights
+are granted if the user is a member of the auth_reseller_admin
+group. Administrator rights for a specific account like vol1 are
+granted if the user is a member of the auth_vol1 group. [3]
+
+The denied_response method was changed to return a HTTP redirect to
+the external authentication URL if no valid token was provided by the
+client.
+
+Most of the handle_get_token method was moved to the external
+authentication script. This method now returns a HTTP redirect.
+
+In the __call__ and get_groups method, we removed support for the
+HTTP_AUTHORIZATION header, which is only needed when Amazon S3 is
+used.
+
+Like tempauth.py, swiftkerbauth.py uses a Swift wrapper to access
+memcache. This wrapper converts the key to an MD5 hash and uses the
+hash value to determine on which of a pre-defined list of servers to
+store the data.
+
+[3] "auth" is the default reseller prefix, and would be different if
+ the reseller_prefix parameter in proxy-server.conf was set.
+
+** swift-auth CGI Script
+
+swift-auth resides on an Apache server and assumes that Apache is
+configured to authenticate the user before this script is
+executed. The script retrieves the username from the REMOTE_USER
+environment variable, and checks if there already is a token for this
+user in the memcache ring. If not, it generates a new one, retrieves
+the user's system groups with "id -Gn USERNAME", stores this
+information in the memcache ring, and returns the token to the client.
+
+For the Swift filter to be able to find the information, it was
+important to use the Swift memcached module. Because we don't want to
+require a full Swift installation on the authentication server,
+/usr/lib/python2.6/site-packages/swift/common/memcached.py from the
+Swift server was copied to /var/www/cgi-bin on the Apache server.
+
+To allow the CGI script to connect to memcache, the SELinux booleans
+httpd_can_network_connect and httpd_can_network_memcache had to be
+set.
+
+The tempauth filter uses the uuid module to generate token
+strings. This module creates and runs temporary files, which leads to
+AVC denial messages in /var/log/audit/audit.log when used from an
+Apache CGI script. While the module still works, the audit log would
+grow quickly. Instead of writing an SELinux policy module to allow or
+to silently ignore these accesses, the swift-auth script uses the
+"random" module for generating token strings.
+
+Red Hat Enterprise Linux 6 comes with Python 2.6 which only provides
+method to list the locally defined user groups. To include groups from
+Red Hat Enterprise Linux Identity Management and in the future from
+Active Directory, the "id" command is run in a subprocess.
+
+* Reference Material
+
+Red Hat Storage Administration Guide:
+https://access.redhat.com/knowledge/docs/Red_Hat_Storage/
+
+Swift Documentation:
+http://docs.openstack.org/developer/swift/
diff --git a/makerpm.sh b/makerpm.sh
new file mode 100755
index 0000000..9635e14
--- /dev/null
+++ b/makerpm.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# Creates swiftkerbauth RPMs in dist/
+
+rm -rf dist/ swiftkerbauth.egg-info/ build/
+python setup.py bdist_rpm --requires="httpd >= 2.2.15, mod_auth_kerb >= 5.4"
+rm -rf swiftkerbauth.egg-info/ build/
+echo "RPMS are now available in $PWD/dist/"
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..ce03189
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+from setuptools import setup
+from swiftkerbauth import __version__
+
+setup(
+ name='swiftkerbauth',
+ version=__version__,
+ description='Kerberos authentication filter for Openstack Swift',
+ license='Apache License (2.0)',
+ author='Red Hat, Inc.',
+ author_email='gluster-users@gluster.org',
+ url='https://forge.gluster.org/swiftkerbauth',
+ packages=['swiftkerbauth'],
+ keywords='openstack swift kerberos',
+ install_requires=['swift>=1.9.1'],
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Environment :: OpenStack',
+ 'Intended Audience :: Information Technology',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: POSIX :: Linux',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ ],
+ data_files=[
+ ('/var/www/cgi-bin', ['apachekerbauth/var/www/cgi-bin/swift-auth']),
+ ('/etc/httpd/conf.d', ['apachekerbauth/etc/httpd/conf.d/swift-auth.conf']),
+ ],
+ entry_points={
+ 'paste.filter_factory': [
+ 'kerbauth=swiftkerbauth.kerbauth:filter_factory',
+ ],
+ },
+ )
diff --git a/swiftkerbauth/__init__.py b/swiftkerbauth/__init__.py
new file mode 100644
index 0000000..eaa1d88
--- /dev/null
+++ b/swiftkerbauth/__init__.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+__version__ = "1.0.0"
diff --git a/swiftkerbauth/build.sh b/swiftkerbauth/build.sh
deleted file mode 100755
index 0daff96..0000000
--- a/swiftkerbauth/build.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-tar -cz --exclude=.svn -f ~/rpmbuild/SOURCES/swiftkerbauth.tar.gz swiftkerbauth
-
-rpmbuild --target noarch --clean -bb swiftkerbauth.spec
-
-rm ~/rpmbuild/SOURCES/swiftkerbauth.tar.gz
diff --git a/swiftkerbauth/swiftkerbauth/swiftkerbauth.py b/swiftkerbauth/kerbauth.py
index ca9cfc7..a37f1f6 100644
--- a/swiftkerbauth/swiftkerbauth/swiftkerbauth.py
+++ b/swiftkerbauth/kerbauth.py
@@ -12,22 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from time import gmtime, strftime, time
+from time import time
from traceback import format_exc
-from urllib import quote, unquote
-
from eventlet import Timeout
-from pkg_resources import require
-require("WebOb>=1.0.8")
-
-from webob import Response, Request
-from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
+from swift.common.swob import Request
+from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
HTTPSeeOther
from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed
-from swift.common.utils import cache_from_env, get_logger, get_remote_client, \
- split_path, TRUE_VALUES
+from swift.common.utils import cache_from_env, get_logger, \
+ split_path, config_true_value
+
class KerbAuth(object):
"""
@@ -58,26 +54,30 @@ class KerbAuth(object):
self.app = app
self.conf = conf
self.logger = get_logger(conf, log_route='kerbauth')
- self.log_headers = conf.get('log_headers') == 'True'
+ self.log_headers = config_true_value(conf.get('log_headers', 'f'))
self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip()
if self.reseller_prefix and self.reseller_prefix[-1] != '_':
self.reseller_prefix += '_'
+ self.logger.set_statsd_prefix('kerbauth.%s' % (
+ self.reseller_prefix if self.reseller_prefix else 'NONE',))
self.auth_prefix = conf.get('auth_prefix', '/auth/')
- if not self.auth_prefix:
+ if not self.auth_prefix or not self.auth_prefix.strip('/'):
+ self.logger.warning('Rewriting invalid auth prefix "%s" to '
+ '"/auth/" (Non-empty auth prefix path '
+ 'is required)' % self.auth_prefix)
self.auth_prefix = '/auth/'
if self.auth_prefix[0] != '/':
self.auth_prefix = '/' + self.auth_prefix
if self.auth_prefix[-1] != '/':
self.auth_prefix += '/'
self.token_life = int(conf.get('token_life', 86400))
- self.allowed_sync_hosts = [h.strip()
- for h in conf.get('allowed_sync_hosts', '127.0.0.1').split(',')
- if h.strip()]
- self.allow_overrides = \
- conf.get('allow_overrides', 't').lower() in TRUE_VALUES
+ self.allow_overrides = config_true_value(
+ conf.get('allow_overrides', 't'))
+ self.storage_url_scheme = conf.get('storage_url_scheme', 'default')
self.ext_authentication_url = conf.get('ext_authentication_url')
if not self.ext_authentication_url:
- raise RuntimeError("Missing filter parameter ext_authentication_url in /etc/swift/proxy-server.conf")
+ raise RuntimeError("Missing filter parameter ext_authentication_"
+ "url in /etc/swift/proxy-server.conf")
def __call__(self, env, start_response):
"""
@@ -98,16 +98,19 @@ class KerbAuth(object):
if token and token.startswith(self.reseller_prefix):
groups = self.get_groups(env, token)
if groups:
- env['REMOTE_USER'] = groups
user = groups and groups.split(',', 1)[0] or ''
- # We know the proxy logs the token, so we augment it just a bit
- # to also log the authenticated user.
- env['HTTP_X_AUTH_TOKEN'] = '%s,%s' % (user, token)
+ trans_id = env.get('swift.trans_id')
+ self.logger.debug('User: %s uses token %s (trans_id %s)' %
+ (user, token, trans_id))
+ env['REMOTE_USER'] = groups
env['swift.authorize'] = self.authorize
env['swift.clean_acl'] = clean_acl
+ if '.reseller_admin' in groups:
+ env['reseller_request'] = True
else:
# Invalid token (may be expired)
- return HTTPSeeOther(location=self.ext_authentication_url)(env, start_response)
+ return HTTPSeeOther(
+ location=self.ext_authentication_url)(env, start_response)
else:
# With a non-empty reseller_prefix, I would like to be called
# back for anonymous access to accounts I know I'm the
@@ -116,7 +119,8 @@ class KerbAuth(object):
version, rest = split_path(env.get('PATH_INFO', ''),
1, 2, True)
except ValueError:
- return HTTPNotFound()(env, start_response)
+ version, rest = None, None
+ self.logger.increment('errors')
# Not my token, not my account, I can't authorize this request,
# deny all is a good idea if not already set...
if 'swift.authorize' not in env:
@@ -158,12 +162,19 @@ class KerbAuth(object):
Enterprise Linux Identity Management is used.
"""
try:
- version, account, container, obj = split_path(req.path, 1, 4, True)
+ version, account, container, obj = req.split_path(1, 4, True)
except ValueError:
+ self.logger.increment('errors')
return HTTPNotFound(request=req)
+
if not account or not account.startswith(self.reseller_prefix):
+ self.logger.debug("Account name: %s doesn't start with "
+ "reseller_prefix: %s."
+ % (account, self.reseller_prefix))
return self.denied_response(req)
+
user_groups = (req.remote_user or '').split(',')
+ account_user = user_groups[1] if len(user_groups) > 1 else None
# If the user is in the reseller_admin group for our prefix, he gets
# full access to all accounts we manage. For the default reseller
# prefix, the group name is auth_reseller_admin.
@@ -173,6 +184,7 @@ class KerbAuth(object):
account[len(self.reseller_prefix)] != '.':
req.environ['swift_owner'] = True
return None
+
# The "account" is part of the request URL, and already contains the
# reseller prefix, like in "/v1/AUTH_vol1/pictures/pic1.png".
if account.lower() in user_groups and \
@@ -180,24 +192,37 @@ class KerbAuth(object):
# If the user is admin for the account and is not trying to do an
# account DELETE or PUT...
req.environ['swift_owner'] = True
+ self.logger.debug("User %s has admin authorizing."
+ % account_user)
return None
- if (req.environ.get('swift_sync_key') and
- req.environ['swift_sync_key'] ==
- req.headers.get('x-container-sync-key', None) and
- 'x-timestamp' in req.headers and
- (req.remote_addr in self.allowed_sync_hosts or
- get_remote_client(req) in self.allowed_sync_hosts)):
+
+ if (req.environ.get('swift_sync_key')
+ and (req.environ['swift_sync_key'] ==
+ req.headers.get('x-container-sync-key', None))
+ and 'x-timestamp' in req.headers):
+ self.logger.debug("Allow request with container sync-key: %s."
+ % req.environ['swift_sync_key'])
return None
+
+ if req.method == 'OPTIONS':
+ #allow OPTIONS requests to proceed as normal
+ self.logger.debug("Allow OPTIONS request.")
+ return None
+
referrers, groups = parse_acl(getattr(req, 'acl', None))
+
if referrer_allowed(req.referer, referrers):
if obj or '.rlistings' in groups:
+ self.logger.debug("Allow authorizing %s via referer ACL."
+ % req.referer)
return None
- return self.denied_response(req)
- if not req.remote_user:
- return self.denied_response(req)
+
for user_group in user_groups:
if user_group in groups:
+ self.logger.debug("User %s allowed in ACL: %s authorizing."
+ % (account_user, user_group))
return None
+
return self.denied_response(req)
def denied_response(self, req):
@@ -206,6 +231,7 @@ class KerbAuth(object):
depending on whether the REMOTE_USER is set or not.
"""
if req.remote_user:
+ self.logger.increment('forbidden')
return HTTPForbidden(request=req)
else:
return HTTPSeeOther(location=self.ext_authentication_url)
@@ -214,7 +240,7 @@ class KerbAuth(object):
"""
WSGI entry point for auth requests (ones that match the
self.auth_prefix).
- Wraps env in webob.Request object and passes it down.
+ Wraps env in swob.Request object and passes it down.
:param env: WSGI environment dictionary
:param start_response: WSGI callable
@@ -228,20 +254,10 @@ class KerbAuth(object):
if 'x-storage-token' in req.headers and \
'x-auth-token' not in req.headers:
req.headers['x-auth-token'] = req.headers['x-storage-token']
- if 'eventlet.posthooks' in env:
- env['eventlet.posthooks'].append(
- (self.posthooklogger, (req,), {}))
- return self.handle_request(req)(env, start_response)
- else:
- # Lack of posthook support means that we have to log on the
- # start of the response, rather than after all the data has
- # been sent. This prevents logging client disconnects
- # differently than full transmissions.
- response = self.handle_request(req)(env, start_response)
- self.posthooklogger(env, req)
- return response
+ return self.handle_request(req)(env, start_response)
except (Exception, Timeout):
print "EXCEPTION IN handle: %s: %s" % (format_exc(), env)
+ self.logger.increment('errors')
start_response('500 Server Error',
[('Content-Type', 'text/plain')])
return ['Internal server error.\n']
@@ -251,19 +267,20 @@ class KerbAuth(object):
Entry point for auth requests (ones that match the self.auth_prefix).
Should return a WSGI-style callable (such as webob.Response).
- :param req: webob.Request object
+ :param req: swob.Request object
"""
req.start_time = time()
handler = None
try:
- version, account, user, _junk = split_path(req.path_info,
- minsegs=1, maxsegs=4, rest_with_last=True)
+ version, account, user, _junk = req.split_path(1, 4, True)
except ValueError:
+ self.logger.increment('errors')
return HTTPNotFound(request=req)
if version in ('v1', 'v1.0', 'auth'):
if req.method == 'GET':
handler = self.handle_get_token
if not handler:
+ self.logger.increment('errors')
req.response = HTTPBadRequest(request=req)
else:
req.response = handler(req)
@@ -284,56 +301,22 @@ class KerbAuth(object):
On successful authentication, the response will have X-Auth-Token
set to the token to use with Swift.
- :param req: The webob.Request to process.
- :returns: webob.Response, 2xx on success with data set as explained
+ :param req: The swob.Request to process.
+ :returns: swob.Response, 2xx on success with data set as explained
above.
"""
# Validate the request info
try:
- pathsegs = split_path(req.path_info, minsegs=1, maxsegs=3,
- rest_with_last=True)
+ pathsegs = split_path(req.path_info, 1, 3, True)
except ValueError:
+ self.logger.increment('errors')
return HTTPNotFound(request=req)
- if not ((pathsegs[0] == 'v1' and pathsegs[2] == 'auth') or pathsegs[0] in ('auth', 'v1.0')):
- return HTTPBadRequest(request=req)
+ if not ((pathsegs[0] == 'v1' and pathsegs[2] == 'auth')
+ or pathsegs[0] in ('auth', 'v1.0')):
+ return HTTPBadRequest(request=req)
return HTTPSeeOther(location=self.ext_authentication_url)
- def posthooklogger(self, env, req):
- if not req.path.startswith(self.auth_prefix):
- return
- response = getattr(req, 'response', None)
- if not response:
- return
- trans_time = '%.4f' % (time() - req.start_time)
- the_request = quote(unquote(req.path))
- if req.query_string:
- the_request = the_request + '?' + req.query_string
- # remote user for zeus
- client = req.headers.get('x-cluster-client-ip')
- if not client and 'x-forwarded-for' in req.headers:
- # remote user for other lbs
- client = req.headers['x-forwarded-for'].split(',')[0].strip()
- logged_headers = None
- if self.log_headers:
- logged_headers = '\n'.join('%s: %s' % (k, v)
- for k, v in req.headers.items())
- status_int = response.status_int
- if getattr(req, 'client_disconnect', False) or \
- getattr(response, 'client_disconnect', False):
- status_int = 499
- self.logger.info(' '.join(quote(str(x)) for x in (client or '-',
- req.remote_addr or '-', strftime('%d/%b/%Y/%H/%M/%S', gmtime()),
- req.method, the_request, req.environ['SERVER_PROTOCOL'],
- status_int, req.referer or '-', req.user_agent or '-',
- req.headers.get('x-auth-token',
- req.headers.get('x-auth-admin-user', '-')),
- getattr(req, 'bytes_transferred', 0) or '-',
- getattr(response, 'bytes_transferred', 0) or '-',
- req.headers.get('etag', '-'),
- req.environ.get('swift.trans_id', '-'), logged_headers or '-',
- trans_time)))
-
def filter_factory(global_conf, **local_conf):
"""Returns a WSGI filter app for use with paste.deploy."""
diff --git a/swiftkerbauth/swiftkerbauth.spec b/swiftkerbauth/swiftkerbauth.spec
deleted file mode 100644
index f01dc8d..0000000
--- a/swiftkerbauth/swiftkerbauth.spec
+++ /dev/null
@@ -1,54 +0,0 @@
-Name: swiftkerbauth
-Version: 1.0
-Release: 1
-Summary: Kerberos authentication filter for Swift
-
-Group: System Environment/Base
-License: GPL
-Source: %{name}.tar.gz
-BuildRoot: %{_tmppath}/%{name}-root
-
-Requires: gluster-swift >= 1.4.8
-Requires: python-webob1.0 >= 1.0.8
-
-%description
-Python script which implements an authentication filter for Swift, the
-object-level access layer of Red Hat Storage.
-
-Relies on an external authentication server, which comes with the
-apachekerbauth package.
-
-%prep
-%setup -q -n %{name}
-
-%build
-
-%install
-rm -rf $RPM_BUILD_ROOT
-
-mkdir -p \
- $RPM_BUILD_ROOT/usr/lib/python2.6/site-packages
-
-install swiftkerbauth.py \
- $RPM_BUILD_ROOT/usr/lib/python2.6/site-packages
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%files
-%defattr(-,root,root,-)
-/usr/lib/python2.6/site-packages/swiftkerbauth.py
-
-%post
-if ! grep -q "filter:kerbauth" /etc/swift/proxy-server.conf; then
-cat >>/etc/swift/proxy-server.conf <<EOF
-
-[filter:kerbauth]
-paste.filter_factory = swiftkerbauth:filter_factory
-ext_authentication_url = http://AUTHENTICATION_SERVER/cgi-bin/swift-auth
-EOF
-fi
-
-%changelog
-* Fri Apr 5 2013 Carsten Clasohm <clasohm@redhat.com> - 1.0-1
-- initial build