Initial import of the swiftkerbauth

Imported code till commit f64a3354185f32928e2568d9ece4a52fa4746c05
Changed a code bit to import correct definitions.
kerbauth unit tests do run along with gluster-swift.
Install script does install swiftkerbauth.
import swiftkerbauth from http://review.gluster.org/swiftkrbauth.git

Change-Id: Ia89f2b77cc68df10dee2f41ce074f3381ac3c408
Signed-off-by: Chetan Risbud <crisbud@redhat.com>
Reviewed-on: http://review.gluster.org/6597
Reviewed-by: Prashanth Pai <ppai@redhat.com>
Reviewed-by: Luis Pabon <lpabon@redhat.com>
Tested-by: Luis Pabon <lpabon@redhat.com>
This commit is contained in:
Chetan Risbud 2013-12-23 15:46:22 +05:30 committed by Luis Pabon
parent 6a8e9a70e9
commit 4b988ce3c5
16 changed files with 2058 additions and 7 deletions

View File

@ -2,24 +2,25 @@
## Contents
* [Keystone](#keystone)
* [Swiftkerbauth](#swiftkerbauth)
* [GSwauth](#gswauth)
* [Overview](#gswauth_overview)
* [Installing GSwauth](#gswauth_install)
* [User roles](#gswauth_user_roles)
* [GSwauth Tools](#gswauth_tools)
* [Authenticating a user](#gswauth_authenticate)
* [Swiftkerbauth](#swiftkerbauth)
* [Architecture](swiftkerbauth/architecture.md)
* [RHEL IPA Server Guide](swiftkerbauth/ipa_server.md)
* [RHEL IPA Client Guide](swiftkerbauth/ipa_client.md)
* [Windows AD Server Guide](swiftkerbauth/AD_server.md)
* [Windows AD Client Guide](swiftkerbauth/AD_client.md)
* [Swiftkerbauth Guide](swiftkerbauth/swiftkerbauth_guide.md)
## <a name="keystone" />Keystone ##
The Standard Openstack authentication service
TBD
## <a name="swiftkerbauth" />Swiftkerbauth ##
Kerberos authentication filter for Swift
TBD
## <a name="gswauth" />GSwauth ##
### <a name="gswauth_overview" />Overview ###
@ -278,3 +279,12 @@ bash-4.2$
bash-4.2$ swift --os-auth-token=AUTH_tk7e68ef4698f14c7f95af07ab7b298610 --os-storage-url=http://127.0.0.1:8080/v1/AUTH_test list container1
README.md
~~~
## <a name="swiftkerbauth" />Swiftkerbauth ##
Kerberos authentication filter
Carsten Clasohm implemented a new authentication filter for swift
that uses Kerberos tickets for single sign on authentication, and
grants administrator permissions based on the users group membership
in a directory service like Red Hat Enterprise Linux Identity Management
or Microsoft Active Directory.

View File

@ -0,0 +1,206 @@
#AD client setup guide
###Contents
* [Setup Overview] (#setup)
* [Configure Network] (#network)
* [Installing AD Client] (#AD-client)
<a name="setup" />
###Setup Overview
This guide talks about adding fedora linux client to windows domain.
The test setup included a client machine with Fedora 19 installed
on it with all the latest packages updated. The crux is to add this linux
machine to Windows Domain. This linux box is expected to act as RHS node and on which swiftkerbauth,
apachekerbauth code would run.
Set hostname (FQDN) to fcclient.winad.com
# hostnamectl set-hostname "fcclient.winad.com"
# hostname "fcclient.winad.com"
<a name="network" />
### Configure client
* Deploy Fedora linux 19.
* Update the system with latest packages.
* Configure SELinux security parameters.
* Install & configure samba
* Configure DNS
* Synchronize the time services
* Join Domain
* Install / Configure Kerberos Client
The document assumes the installing Fedora Linux and configuring SELinux
parameters to 'permissive' is known already.
###Install & Configure Samba:
# yum -y install samba samba-client samba-common samba-winbind
samba-winbind-clients
# service start smb
# ps -aef | grep smb
# chkconfig smb on
###Synchronize time services
The kerberos authentication and most of the DNS functionality could fail with
clock skew if times are not synchronized.
# cat /etc/ntp.conf
server ns1.bos.redhat.com
server 10.5.26.10
# service ntpd stop
# ntpdate 10.16.255.2
# service ntpd start
#chkconfig ntpd on
Check if Windows server in the whole environment is also time synchronized with
same source.
# C:\Users\Administrator>w32tm /query /status | find "Source"
Source: ns1.xxx.xxx.com
###Configure DNS on client
Improperly resolved hostname is the leading cause in authentication failures.
Best practice is to configure fedora client to use Windows DNS.
'nameserver' below is the IP address of the windows server.
# cat /etc/resolve.conf
domain server.winad.com
search server.winad.com
nameserver 10.nn.nnn.3
###Set the hostname of the client properly (FQDN)
# cat /etc/sysconfig/network
HOSTNAME=fcclient.winad.com
###Install & Configure kerberos client
# yum -y install krb5-workstation
Edit the /etc/krb5.conf as follows:
# cat /etc/krb5.conf
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
[libdefaults]
default_realm = WINAD.COM
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
[realms]
WINAD.COM = {
kdc = server.winad.com
admin_server = server.winad.com
}
[domain_realm]
.demo = server.winad.com
demo = server.winad.com
###Join Domain
Fire command 'system-config-authentication' on client. This should display a
graphical wizard. Below inputs would help configure this wizard.
- User account data base = winbind
- winbind domain = winad
- security model = ads
- winbind ads realm = winad.com
- winbind controller = server.winad.com
- template shell = /bin/bash
- let the other options be as is to default.
- Perform Join domain and appy settings and quit. Please note this join should
not see any errors. This makes the client fedora box to join the windows
domain.
###Configure the kerberos client
This would bring the users/groups from Windows Active directory to this
fedora client.
Edit /etc/samba/smb.conf file to have below parameters in the global section.
# cat /etc/samba/smb.conf
[global]
workgroup = winad
realm = winad.com
server string = Samba Server Version %v
security = ADS
allow trusted domains = No
password server = server.winad.com
log file = /var/log/samba/log.%m
max log size = 50
idmap uid = 10000­19999
idmap gid = 10000­19999
template shell = /bin/bash
winbind separator = +
winbind use default domain = Yes
idmap config REFARCH­AD:range = 10000000­19999999
idmap config REFARCH­AD:backend = rid
cups options = raw
# service smb stop
# service winbind stop
# tar -cvf /var/tmp/samba-cache-backup.tar /var/lib/samba
# ls -la /var/tmp/samba-cache-backup.tar
# rm ­-f /var/lib/samba/*
Verify that no kerberos ticket available and cached.
# kdestroy
# klist
Rejoin the domain.
# net join -S server -U Administrstor
Test that client rejoined the domain.
# net ads info
Restart smb and winbind service.
# wbinfo --domain-users
Perform kinit for the domain users prepared on active directory. This is obtain
the kerberos ticket for user 'auth_admin'
# kinit auth_admin
# id -Gn auth_admin
###Notes
Obtaining the HTTP service principal & keytab file and installing it with
swiftkerbauth is added to swiftkerbauth_guide
###References
Reference Document for adding Linux box to windows domain :
Integrating Red Hat Enterprise Linux 6
with Active Directory

View File

@ -0,0 +1,107 @@
#Windows Active Directory & Domain Controller Server Guide
###Contents
* [Setup Overview] (#Setup)
* [Installing Active Directory Services] (#AD-server)
* [Configuring DNS] (#DNS)
* [Adding Users and Groups] (#users-groups)
<a name="Setup" />
###Setup Overview
The setup includes a server machine installed with Windows 2008 R2 Server, with
Domain Controller, Active Directory services & DNS server installed alongwith.
The steps to install windows operating system and above servers can be found
on MicroSoft Documentation. This windows Active Directory server would act as an
authentication server in the whole setup. This would provide the access control
and permissions for users on certain data objects.
Windows 2008 R2 deployment:
http://technet.microsoft.com/en-us/library/dd283085.aspx
Configuring Active Directory, Domain Services, DNS server:
http://technet.microsoft.com/en-us/library/cc770946.aspx
<a name="AD-server" />
###Installing AD Server
Administrators need to follow simple instructions in Server Manager on Windows
2008, and should add Active Directory Domain Services & DNS server. It is
recommended to use static IP for DNS server. Preferred Hostname(FQDN) for
Windows server could be of format hostname 'server.winad.com' where
'winad.com' is a domain name.
Following tips would help prepare a test setup neatly.
- Select Active Directory Domain services wizard in Server Manager
- Move on to install it with all the pre-requisits, e.g. .NET framework etc.
- Configure Active directory after installtion via exapanding the 'Roles'
section in the server manager.
- Create a new Domain in the New Forest.
- Type the FQDN, winad.com
- Set Forest functional level Windows 2008 R2.
- Selct additional options for this domain controller as DNS server.
- Leave the log locations to default provided by wizard.
- Set the Administrator Password carefully.
- Thats it. You are done configuring active directory.
<a name="dns" />
###Configuring DNS
This section explains configuring the DNS server installed on Windows 2008 R2
server. You must know know about
- Forward lookup zone
- Reverse lookup zone
- Zone type
A forward lookup zone is simply a way to resolve hostnames to IP address.
A reverse lookup zone is to lookup DNS hostname of the host IP.
Following tips would help configure the Zones on DNS server.
- Create a Forward lookup zone.
- Create it a primary zone.
- Add the Clients using their ip addresses and FQDN to this forward lookup
zones.
- This would add type 'A' record for that host on DNS server.
- Similarly create a Reverser lookup zone.
- Add clients 'PTR' record to this zone via browsing through the forward
zones clients.
The above setup can be tested on client once it joins the domain using 'dig'
command as mentioned below.
On client:
# dig fcclient.winad.com
This should yield you a Answer section mentioning its IP address.
Reverse lookup can be tested using
# 'dig -t ptr 101.56.168.192.in-addr.arpa.'
The answer section should state the FQDN of the client.
Repeat the above steps on client for Windows AD server as well.
<a name="users-groups" />
###Adding users and groups
Adding groups and users to the Windows domain is easy task.
- Start -> Administrative Tools -> Active Directory Users & Computers
- Expand the domain name which was prepared earlier. e.g winad.com
- Add groups with appropreate access rights.
- Add users to the group with appropreate permissions.
- Make sure you set password for users prepared on AD server.

View File

@ -0,0 +1,105 @@
# 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 (on the same machine). 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).
*****
## kerbauth.py
The script kerbauth.py began as a copy of the tempauth.py script from
from tempauth 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]
use = egg:swiftkerbauth#kerbauth
ext_authentication_url = http://client.rhelbox.com/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, kerbauth.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.
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.

View File

@ -0,0 +1,80 @@
#IPA Client Guide
##Contents
* [Setup Overview] (#setup)
* [Configure Network] (#network)
* [Installing IPA Client] (#ipa-client)
<a name="setup" />
##Setup Overview
We have used a F18 box as IPA client machine and used FreeIPA client.
This document borrows instructions from the following more detailed guide.
[RHEL 6 Identity Management Guide][]
<a name="network" />
## Configure network
Set hostname (FQDN) to client.rhelbox.com
> hostnamectl set-hostname "client.rhelbox.com"
>
> hostname "client.rhelbox.com"
Add following to /etc/sysconfig/network:
HOSTNAME=client.rhelbox.com
Add the following to /etc/hostname
client.rhelbox.com
Add the following to /etc/hosts
192.168.56.110 server.rhelbox.com server
192.168.56.101 client.rhelbox.com client
Logout and login again and verify hostname :
> hostname --fqdn
Edit */etc/resolv.conf* to add this at beginning of file
nameserver 192.168.56.110
Warning: NetworkManager changes resolv.conf on restart
Turn off firewall
> service iptables stop
>
> chkconfig iptables off
<a name="ipa-client" />
## Installing IPA Client
Install IPA client packages:
For RHEL:
> yum install ipa-client ipa-admintools
For Fedora:
> yum install freeipa-client freeipa-admintools
Install IPA client and add to domain:
>ipa-client-install --enable-dns-updates
Discovery was successful!
Hostname: client.rhelbox.com
Realm: RHELBOX.COM
DNS Domain: rhelbox.com
IPA Server: server.rhelbox.com
BaseDN: dc=rhelbox,dc=com
Continue to configure the system with these values? [no]: yes
User authorized to enroll computers: admin
Check if client is configured correctly:
> kinit admin
>
> getent passwd admin
[RHEL 6 Identity Management Guide]: https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Identity_Management_Guide/

View File

@ -0,0 +1,134 @@
#IPA Server Guide
##Contents
* [Setup Overview] (#setup)
* [Configure Network] (#network)
* [Installing IPA Server] (#ipa-server)
* [Configuring DNS] (#dns)
* [Adding Users and Groups] (#users-groups)
<a name="setup" />
##Setup Overview
We have used a RHEL 6.4 box as IPA and DNS server. This document borrows
instructions from the following more detailed guide.
[RHEL 6 Identity Management Guide][]
<a name="network" />
## Configure network
Change hostname (FQDN) to server.rhelbox.com
> hostname "server.rhelbox.com"
Add following to */etc/sysconfig/network* file
HOSTNAME=server.rhelbox.com
Add the following to */etc/hosts* file
192.168.56.110 server.rhelbox.com server
192.168.56.101 client.rhelbox.com client
Logout and login again and verify new hostname
> hostname --fqdn
Turn off firewall
> service iptables stop
>
> chkconfig iptables off
<a name="ipa-server" />
## Installing IPA Server
Install IPA server packages and DNS dependencies
> yum install ipa-server bind bind-dyndb-ldap
Run the following interactive setup to install IPA server with DNS
> ipa-server-install --setup-dns
The IPA Master Server will be configured with:
Hostname: server.rhelbox.com
IP address: 192.168.56.110
Domain name: rhelbox.com
Realm name: RHELBOX.COM
BIND DNS server will be configured to serve IPA domain with:
Forwarders: No forwarders
Reverse zone: 56.168.192.in-addr.arpa.
The installation may take some time.
Check if IPA is installed correctly :
> kinit admin
>
> ipa user-find admin
<a name="dns" />
## Configuring DNS
Edit */etc/resolv.conf* to add this at beginning of file :
nameserver 192.168.56.110
Warning: NetworkManager changes resolv.conf on restart
Add a DNS A record and PTR record for the client under rhelbox.com zone
> ipa dnsrecord-add rhelbox.com client --a-rec=192.168.56.101 --a-create-reverse
Check if DNS resolution is working by running :
> dig server.rhelbox.com
;; ANSWER SECTION:
server.rhelbox.com. 1200 IN A 192.168.56.110
> dig client.rhelbox.com
;; ANSWER SECTION:
client.rhelbox.com. 86400 IN A 192.168.56.101
Check if reverse resolution works :
> dig -t ptr 101.56.168.192.in-addr.arpa.
;; ANSWER SECTION:
101.56.168.192.in-addr.arpa. 86400 IN PTR client.rhelbox.com.
> dig -t ptr 110.56.168.192.in-addr.arpa.
;; ANSWER SECTION:
110.56.168.192.in-addr.arpa. 86400 IN PTR server.rhelbox.com.
<a name="users-groups" />
## Adding users and groups
Create *auth_reseller_admin* user group
> ipa group-add auth_reseller_admin --desc="Full access to all Swift accounts"
Create *auth_rhs_test* user group
> ipa group-add auth_rhs_test --desc="Full access to rhs_test account"
Create user *auth_admin* user as member of *auth_reseller_admin* user group
> ipa user-add auth_admin --first=Auth --last=Admin --password
>
> ipa group-add-member auth_reseller_admin --users=auth_admin
Create user *rhs_test_admin* as member of *auth_rhs_test* user group
> ipa user-add rhs_test_admin --first=RHS --last=Admin --password
>
> ipa group-add-member auth_rhs_test --users=rhs_test_admin
Create user *jsmith* with no relevant group membership
> ipa user-add rhs_test_admin --first=RHS --last=Admin --password
You can verify users have been added by running
>ipa user-find admin
NOTE: Every user has to change password on first login.
[RHEL 6 Identity Management Guide]: https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Identity_Management_Guide/

View File

@ -0,0 +1,435 @@
#swiftkerbauth
* [Installing Kerberos module for Apache on IPA client] (#httpd-kerb-install)
* [Creating HTTP Service Principal on IPA server] (#http-principal)
* [Installing and configuring swiftkerbauth on IPA client] (#install-swiftkerbauth)
* [Using swiftkerbauth] (#use-swiftkerbauth)
<a name="httpd-kerb-install" />
## Installing Kerberos module for Apache on IPA client
Install httpd server with kerberos module:
> yum install httpd mod_auth_kerb
>
> service httpd restart
Check if auth_kerb_module is loaded :
> httpd -M | grep kerb
Change httpd log level to debug by adding/changing the following in
*/etc/httpd/conf/httpd.conf* file
LogLevel debug
httpd logs are at */var/log/httpd/error_log* for troubleshooting
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
*****
<a name="http-principal" />
## Creating HTTP Service Principal on IPA server
Add a HTTP Kerberos service principal :
> ipa service-add HTTP/client.rhelbox.com@RHELBOX.COM
Retrieve the HTTP service principal to a keytab file:
> ipa-getkeytab -s server.rhelbox.com -p HTTP/client.rhelbox.com@RHELBOX.COM -k /tmp/http.keytab
Copy keytab file to client:
> scp /tmp/http.keytab root@192.168.56.101:/etc/httpd/conf/http.keytab
## Creating HTTP Service Principal on Windows AD server
Add a HTTP Kerberos service principal:
> c:\>ktpass.exe -princ HTTP/fcclient.winad.com@WINAD.COM -mapuser
> auth_admin@WINAD.COM -pass Redhat*123 -out c:\HTTP.keytab
Use winscp to copy HTTP.ketab file to /etc/httpd/conf/http.keytab
*****
<a name="install-swiftkerbauth" />
##Installing and configuring swiftkerbauth on IPA client
Prerequisites for installing swiftkerbauth
* swift (havana)
* gluster-swift (optional)
You can install swiftkerbauth using one of these three ways:
Installing swiftkerbauth from source:
> python setup.py install
Installing swiftkerbauth using pip:
> pip install swiftkerbauth
Installing swiftkerbauth from RPMs:
> ./makerpm.sh
>
> rpm -ivh dist/swiftkerbauth-1.0.0-1.noarch.rpm
Edit */etc/httpd/conf.d/swift-auth.conf* and change KrbServiceName, KrbAuthRealms and Krb5KeyTab parameters accordingly.
More detail on configuring kerberos for apache can be found at:
[auth_kerb_module Configuration][]
Make /etc/httpd/conf/http.keytab readable by any user :
> chmod 644 /etc/httpd/conf/http.keytab
And preferably change owner of keytab file to apache :
> chown apache:apache /etc/httpd/conf/http.keytab
Reload httpd
> service httpd reload
Make authentication script executable:
> chmod +x /var/www/cgi-bin/swift-auth
*****
<a name="#use-swiftkerbauth" />
##Using swiftkerbauth
### Adding kerbauth filter in swift pipeline
Edit */etc/swift/proxy-server.conf* and add a new filter section as follows:
[filter:kerbauth]
use = egg:swiftkerbauth#kerbauth
ext_authentication_url = http://client.rhelbox.com/cgi-bin/swift-auth
Add kerbauth to pipeline
[pipeline:main]
pipeline = catch_errors healthcheck proxy-logging cache proxy-logging kerbauth proxy-server
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
Restart swift to activate kerbauth filer
> swift-init main restart
###Examples
####Authenticate user and get Kerberos ticket
> kinit auth_admin
NOTE: curl ignores user specified in -u option. All further curl commands
will use the currently authenticated auth_admin user.
####Get an authentication token:
> curl -v -u : --negotiate --location-trusted http://client.rhelbox.com:8080/auth/v1.0
* About to connect() to client.rhelbox.com port 8080 (#0)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
> GET /auth/v1.0 HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com:8080
> Accept: */*
>
< HTTP/1.1 303 See Other
< Content-Type: text/html; charset=UTF-8
< Location: http://client.rhelbox.com/cgi-bin/swift-auth
< Content-Length: 0
< X-Trans-Id: txecd415aae89b4320b6145-0052417ea5
< Date: Tue, 24 Sep 2013 11:59:33 GMT
<
* Connection #0 to host client.rhelbox.com left intact
* Issue another request to this URL: 'http://client.rhelbox.com/cgi-bin/swift-auth'
* About to connect() to client.rhelbox.com port 80 (#1)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 80 (#1)
> GET /cgi-bin/swift-auth HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Tue, 24 Sep 2013 11:59:33 GMT
< Server: Apache/2.4.6 (Fedora) mod_auth_kerb/5.4
< WWW-Authenticate: Negotiate
< WWW-Authenticate: Basic realm="Swift Authentication"
< Content-Length: 381
< Content-Type: text/html; charset=iso-8859-1
<
* Ignoring the response-body
* Connection #1 to host client.rhelbox.com left intact
* Issue another request to this URL: 'http://client.rhelbox.com/cgi-bin/swift-auth'
* Re-using existing connection! (#1) with host (nil)
* Connected to (nil) (192.168.56.101) port 80 (#1)
* Server auth using GSS-Negotiate with user ''
> GET /cgi-bin/swift-auth HTTP/1.1
> Authorization: Negotiate YIICYgYJKoZIhvcSAQICAQBuggJRMIICTaADAgEFoQMCAQ6iBwMFACAAAACjggFgYYIBXDCCAVigAwIBBaENGwtSSEVMQk9YLkNPTaIlMCOgAwIBA6EcMBobBEhUVFAbEmNsaWVudC5yaGVsYm94LmNvbaOCARkwggEVoAMCARKhAwIBAaKCAQcEggEDx9SH2R90RO4eAkhsNKow/DYfjv1rWhgxNRqj/My3yslASSgefls48VdDNHVVWqr1Kd6mB/9BIoumpA+of+KSAg2QfPtcWiVFj5n5Fa8fyCHyQPvV8c92KzUdrBPc8OVn0aldFp0I4P1MsYZbnddDRSH3kjVA5oSucHF59DhZWiGJV/F6sVimBSeoTBHQD38Cs5RhyDHNyUad9v3gZERVGCJXC76i7+yyaoIDA+N9s0hasHajhTnjs3XQBYfZFwp8lWl3Ub+sOtPO1Ng7mFlSAYXCM6ljlKTEaxRwaYoXUC1EoIqEOG/8pC9SJThS2M1G7MW1c5xm4lksNss72OH4gtPns6SB0zCB0KADAgESooHIBIHFrLtai5U8ajEWo1J9B26PnIUqLd+uA0KPd2Y2FjrH6rx4xT8qG2p8i36SVGubvwBVmfQ7lSJcXt6wUvb43qyPs/fMiSY7QxHxt7/btMgxQl6JWMagvXMhCNXnhEHNNaTdBcG5KFERDGeo0txaAD1bzZ4mnxCQmoqusGzZ6wdDw6+5wq1tK/hQTQUgk2NwxfXAg2J5K02/3fKjFR2h7zewI1pEyhhpeONRkkRETcyojkK2EbVzZ8kc3RsuwzFYsJ+9u5Qj3E4=
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 24 Sep 2013 11:59:33 GMT
< Server: Apache/2.4.6 (Fedora) mod_auth_kerb/5.4
< WWW-Authenticate: Negotiate YIGZBgkqhkiG9xIBAgICAG+BiTCBhqADAgEFoQMCAQ+iejB4oAMCARKicQRveeZTV/QRJSIOoOWPbZkEmtdug9V5ZcMGXWqAJvCAnrvw9gHbklMyLl8f8jU2e0wU3ehtchLEL4dVeAYgKsnUgw4wGhHu59AZBwSbHRKSpv3I6gWEZqC4NAEuZJFW9ipdUHOiclBQniVXXCsRF/5Y
< X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
< X-Debug-Remote-User: auth_admin
< X-Debug-Groups: auth_admin,auth_reseller_admin
< X-Debug-Token-Life: 86400s
< X-Debug-Token-Expires: Wed Sep 25 17:29:33 2013
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
<
* Connection #1 to host (nil) left intact
* Closing connection #0
* Closing connection #1
The header *X-Auth-Token* in response contains the token *AUTH_tk083b8abc92f4a514f34224a181ed568a*.
####PUT a container
>curl -v -X PUT -H 'X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1
* About to connect() to client.rhelbox.com port 8080 (#0)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
> PUT /v1/AUTH_myvolume/c1 HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com:8080
> Accept: */*
> X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
>
< HTTP/1.1 201 Created
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
< X-Trans-Id: txc420b0ebf9714445900e8-0052418863
< Date: Tue, 24 Sep 2013 12:41:07 GMT
<
* Connection #0 to host client.rhelbox.com left intact
* Closing connection #0
####GET a container listing
> curl -v -X GET -H 'X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a' http://client.rhelbox.com:8080/v1/AUTH_myvolume
* About to connect() to client.rhelbox.com port 8080 (#0)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
> GET /v1/AUTH_myvolume HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com:8080
> Accept: */*
> X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
>
< HTTP/1.1 200 OK
< Content-Length: 3
< X-Account-Container-Count: 0
< Accept-Ranges: bytes
< X-Account-Object-Count: 0
< X-Bytes-Used: 0
< X-Timestamp: 1379997117.09468
< X-Object-Count: 0
< X-Account-Bytes-Used: 0
< X-Type: Account
< Content-Type: text/plain; charset=utf-8
< X-Container-Count: 0
< X-Trans-Id: tx89826736a1ab4d6aae6e3-00524188dc
< Date: Tue, 24 Sep 2013 12:43:08 GMT
<
c1
* Connection #0 to host client.rhelbox.com left intact
* Closing connection #0
####PUT an object in container
> curl -v -X PUT -H 'X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1/object1 -d'Hello world'
* About to connect() to client.rhelbox.com port 8080 (#0)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
> PUT /v1/AUTH_myvolume/c1/object1 HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com:8080
> Accept: */*
> X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
> Content-Length: 11
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 11 out of 11 bytes
< HTTP/1.1 201 Created
< Last-Modified: Wed, 25 Sep 2013 06:08:00 GMT
< Content-Length: 0
< Etag: 3e25960a79dbc69b674cd4ec67a72c62
< Content-Type: text/html; charset=UTF-8
< X-Trans-Id: tx01f1b5a430cf4af3897be-0052427dc0
< Date: Wed, 25 Sep 2013 06:08:01 GMT
<
* Connection #0 to host client.rhelbox.com left intact
* Closing connection #0
####Give permission to jsmith to list and download objects from c1 container
> curl -v -X POST -H 'X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a' -H 'X-Container-Read: jsmith' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1
* About to connect() to client.rhelbox.com port 8080 (#0)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
> POST /v1/AUTH_myvolume/c1 HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com:8080
> Accept: */*
> X-Auth-Token: AUTH_tk083b8abc92f4a514f34224a181ed568a
> X-Container-Read: jsmith
>
< HTTP/1.1 204 No Content
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
< X-Trans-Id: txcedea3e2557d463eb591d-0052427f60
< Date: Wed, 25 Sep 2013 06:14:56 GMT
<
* Connection #0 to host client.rhelbox.com left intact
* Closing connection #0
####Access container as jsmith
> kinit jsmith
Get token for jsmith
> curl -v -u : --negotiate --location-trusted http://client.rhelbox.com:8080/auth/v1.0
* About to connect() to client.rhelbox.com port 8080 (#0)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
> GET /auth/v1.0 HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com:8080
> Accept: */*
>
< HTTP/1.1 303 See Other
< Content-Type: text/html; charset=UTF-8
< Location: http://client.rhelbox.com/cgi-bin/swift-auth
< Content-Length: 0
< X-Trans-Id: txf51e1bf7f8c5496f8cc93-005242800b
< Date: Wed, 25 Sep 2013 06:17:47 GMT
<
* Connection #0 to host client.rhelbox.com left intact
* Issue another request to this URL: 'http://client.rhelbox.com/cgi-bin/swift-auth'
* About to connect() to client.rhelbox.com port 80 (#1)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 80 (#1)
> GET /cgi-bin/swift-auth HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Wed, 25 Sep 2013 06:17:47 GMT
< Server: Apache/2.4.6 (Fedora) mod_auth_kerb/5.4
< WWW-Authenticate: Negotiate
< WWW-Authenticate: Basic realm="Swift Authentication"
< Content-Length: 381
< Content-Type: text/html; charset=iso-8859-1
<
* Ignoring the response-body
* Connection #1 to host client.rhelbox.com left intact
* Issue another request to this URL: 'http://client.rhelbox.com/cgi-bin/swift-auth'
* Re-using existing connection! (#1) with host (nil)
* Connected to (nil) (192.168.56.101) port 80 (#1)
* Server auth using GSS-Negotiate with user ''
> GET /cgi-bin/swift-auth HTTP/1.1
> Authorization: Negotiate YIICWAYJKoZIhvcSAQICAQBuggJHMIICQ6ADAgEFoQMCAQ6iBwMFACAAAACjggFbYYIBVzCCAVOgAwIBBaENGwtSSEVMQk9YLkNPTaIlMCOgAwIBA6EcMBobBEhUVFAbEmNsaWVudC5yaGVsYm94LmNvbaOCARQwggEQoAMCARKhAwIBAaKCAQIEgf/+3OaXYCSEjcsjU3t3lOLcYG84GBP9Kj9YTHc7yVMlcam4ivCwMqCkzxgvNo2E3a5KSWyFwngeX4b/QFbCKPXA4sfBibZRkeMk5gr2f0MLI3gWEAIYq7bJLre04bnkD2F0MzijPJrOLIx1KmFe08UGWCEmnG2uj07lvIR1RwV/7dMM4J1B+KKvDVKA0LxahwPIpx8oOON2yMGcstrBAHBBk5pmpt1Gg9Lh7xdNPsjP0IfI5Q0zkGCRBKpvpXymP1lQpQXlHbqkdBYOmG4+p/R+vIosO4ui1G6GWE9t71h3AqW61CcCj3/oOjZsG56k8HMSNk/+3mfUTP86nzLRGkekgc4wgcugAwIBEqKBwwSBwPsG9nGloEnOsA1abP4R1/yUDcikjjwKiacvZ+cu7bWEzu3L376k08U8C2YIClyUJy3Grt68LxhnfZ65VCZ5J5IOLiXOJnHBIoJ1L4GMYp4EgZzHvI7R3U3DApMzNWZwc1MsSF5UGhmLwxSevDLetJHjgKzKNteRyVN/8CFgjSBEjGSN1Qgy1RZHuQR9d3JHPczONZ4+ZgStfy+I1m2IUIgW3+4JGFVafHiBQVwSWRNfdXFgI3wBz7slntd7r3qMWA==
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 25 Sep 2013 06:17:47 GMT
< Server: Apache/2.4.6 (Fedora) mod_auth_kerb/5.4
< WWW-Authenticate: Negotiate YIGYBgkqhkiG9xIBAgICAG+BiDCBhaADAgEFoQMCAQ+ieTB3oAMCARKicARuH2YpjFrtgIhGr5nO7gh/21EvGH9tayRo5A3pw5pxD1B1036ePLG/x98OdMrSflse5s8ttz8FmvRphCFJa8kfYtnWULgoFLF2F2a1zBdSo2oCA0R05YFwArNhkg6ou5o7wWZkERHK33CKlhudSj8=
< X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447
< X-Debug-Remote-User: jsmith
< X-Debug-Groups: jsmith
< X-Debug-Token-Life: 86400s
< X-Debug-Token-Expires: Thu Sep 26 11:47:47 2013
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
<
* Connection #1 to host (nil) left intact
* Closing connection #0
* Closing connection #1
List the container using authentication token for jsmith:
> curl -v -X GET -H 'X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1
* About to connect() to client.rhelbox.com port 8080 (#0)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
> GET /v1/AUTH_myvolume/c1 HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com:8080
> Accept: */*
> X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447
>
< HTTP/1.1 200 OK
< Content-Length: 8
< X-Container-Object-Count: 0
< Accept-Ranges: bytes
< X-Timestamp: 1
< X-Container-Bytes-Used: 0
< Content-Type: text/plain; charset=utf-8
< X-Trans-Id: tx575215929c654d9f9f284-00524280a4
< Date: Wed, 25 Sep 2013 06:20:20 GMT
<
object1
* Connection #0 to host client.rhelbox.com left intact
* Closing connection #0
Downloading the object as jsmith:
> curl -v -X GET -H 'X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447' http://client.rhelbox.com:8080/v1/AUTH_myvolume/c1/object1
* About to connect() to client.rhelbox.com port 8080 (#0)
* Trying 192.168.56.101...
* connected
* Connected to client.rhelbox.com (192.168.56.101) port 8080 (#0)
> GET /v1/AUTH_myvolume/c1/object1 HTTP/1.1
> User-Agent: curl/7.27.0
> Host: client.rhelbox.com:8080
> Accept: */*
> X-Auth-Token: AUTH_tkb5a20eb8207a819e76619431c8410447
>
< HTTP/1.1 200 OK
< Content-Length: 11
< Accept-Ranges: bytes
< Last-Modified: Wed, 25 Sep 2013 06:08:00 GMT
< Etag: 3e25960a79dbc69b674cd4ec67a72c62
< X-Timestamp: 1380089280.98829
< Content-Type: application/x-www-form-urlencoded
< X-Trans-Id: tx19b5cc3847854f40a6ca8-00524281aa
< Date: Wed, 25 Sep 2013 06:24:42 GMT
<
* Connection #0 to host client.rhelbox.com left intact
Hello world* Closing connection #0
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://client.rhelbox.com:8080/v1/AUTH_myvolume/c1/object1
The --negotiate option is for curl to perform Kerberos authentication and
--location-trusted is for curl to follow the redirect.
[auth_kerb_module Configuration]: http://modauthkerb.sourceforge.net/configure.html

View File

@ -0,0 +1,38 @@
#!/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.
from swift.common.utils import readconf, config_true_value
config_file = {}
try:
config_file = readconf("/etc/swift/proxy-server.conf",
section_name="filter:cache")
except SystemExit:
pass
MEMCACHE_SERVERS = config_file.get('memcache_servers', None)
config_file = {}
try:
config_file = readconf("/etc/swift/proxy-server.conf",
section_name="filter:kerbauth")
except SystemExit:
pass
TOKEN_LIFE = int(config_file.get('token_life', 86400))
RESELLER_PREFIX = config_file.get('reseller_prefix', "AUTH_")
DEBUG_HEADERS = config_true_value(config_file.get('debug_headers', 'yes'))

View File

@ -0,0 +1,12 @@
<Location /cgi-bin/swift-auth>
AuthType Kerberos
AuthName "Swift Authentication"
KrbMethodNegotiate On
KrbMethodK5Passwd On
KrbSaveCredentials On
KrbServiceName HTTP/client.example.com
KrbAuthRealms EXAMPLE.COM
Krb5KeyTab /etc/httpd/conf/http.keytab
KrbVerifyKDC Off
Require valid-user
</Location>

View File

@ -0,0 +1,70 @@
#!/usr/bin/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.
# Requires the following command to be run:
# setsebool -P httpd_can_network_connect 1
# setsebool -P httpd_can_network_memcache 1
import os
import cgi
from swift.common.memcached import MemcacheRing
from time import time, ctime
from swiftkerbauth import MEMCACHE_SERVERS, TOKEN_LIFE, DEBUG_HEADERS
from swiftkerbauth.kerbauth_utils import get_remote_user, get_auth_data, \
generate_token, set_auth_data, get_groups
def main():
try:
username = get_remote_user(os.environ)
except RuntimeError:
print "Status: 401 Unauthorized\n"
print "Malformed REMOTE_USER"
return
if not MEMCACHE_SERVERS:
print "Status: 500 Internal Server Error\n"
print "Memcache not configured in /etc/swift/proxy-server.conf"
return
mc_servers = [s.strip() for s in MEMCACHE_SERVERS.split(',') if s.strip()]
mc = MemcacheRing(mc_servers)
token, expires, groups = get_auth_data(mc, username)
if not token:
token = generate_token()
expires = time() + TOKEN_LIFE
groups = get_groups(username)
set_auth_data(mc, username, token, expires, groups)
print "X-Auth-Token: %s" % token
print "X-Storage-Token: %s" % token
# For debugging.
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")
main()
except:
cgi.print_exception()

View File

@ -0,0 +1,328 @@
# 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.
from time import time
from traceback import format_exc
from eventlet import Timeout
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, \
split_path, config_true_value
class KerbAuth(object):
"""
Test authentication and authorization system.
Add to your pipeline in proxy-server.conf, such as::
[pipeline:main]
pipeline = catch_errors cache kerbauth proxy-server
Set account auto creation to true in proxy-server.conf::
[app:proxy-server]
account_autocreate = true
And add a kerbauth filter section, such as::
[filter:kerbauth]
use = egg:swiftkerbauth#kerbauth
See the proxy-server.conf-sample for more information.
:param app: The next WSGI app in the pipeline
:param conf: The dict of configuration values
"""
def __init__(self, app, conf):
self.app = app
self.conf = conf
self.logger = get_logger(conf, log_route='kerbauth')
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 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.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")
def __call__(self, env, start_response):
"""
Accepts a standard WSGI application call, authenticating the request
and installing callback hooks for authorization and ACL header
validation. For an authenticated request, REMOTE_USER will be set to a
comma separated list of the user's groups.
If the request matches the self.auth_prefix, the request will be
routed through the internal auth request handler (self.handle).
This is to handle granting tokens, etc.
"""
if self.allow_overrides and env.get('swift.authorize_override', False):
return self.app(env, start_response)
if env.get('PATH_INFO', '').startswith(self.auth_prefix):
return self.handle(env, start_response)
token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
if token and token.startswith(self.reseller_prefix):
groups = self.get_groups(env, token)
if groups:
user = groups and groups.split(',', 1)[0] or ''
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)
else:
# With a non-empty reseller_prefix, I would like to be called
# back for anonymous access to accounts I know I'm the
# definitive auth for.
try:
version, rest = split_path(env.get('PATH_INFO', ''),
1, 2, True)
except ValueError:
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:
env['swift.authorize'] = self.denied_response
return self.app(env, start_response)
def get_groups(self, env, token):
"""
Get groups for the given token.
:param env: The current WSGI environment dictionary.
:param token: Token to validate and return a group string for.
:returns: None if the token is invalid or a string containing a comma
separated list of groups the authenticated user is a member
of. The first group in the list is also considered a unique
identifier for that user.
"""
groups = None
memcache_client = cache_from_env(env)
if not memcache_client:
raise Exception('Memcache required')
memcache_token_key = '%s/token/%s' % (self.reseller_prefix, token)
cached_auth_data = memcache_client.get(memcache_token_key)
if cached_auth_data:
expires, groups = cached_auth_data
if expires < time():
groups = None
return groups
def authorize(self, req):
"""
Returns None if the request is authorized to continue or a standard
WSGI response callable if not.
Assumes that user groups are all lower case, which is true when Red Hat
Enterprise Linux Identity Management is used.
"""
try:
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.
admin_group = ("%sreseller_admin" % self.reseller_prefix).lower()
if admin_group in user_groups and \
account != self.reseller_prefix and \
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 \
(req.method not in ('DELETE', 'PUT') or container):
# 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):
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
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):
"""
Returns a standard WSGI response callable with the status of 403 or 401
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)
def handle(self, env, start_response):
"""
WSGI entry point for auth requests (ones that match the
self.auth_prefix).
Wraps env in swob.Request object and passes it down.
:param env: WSGI environment dictionary
:param start_response: WSGI callable
"""
try:
req = Request(env)
if self.auth_prefix:
req.path_info_pop()
req.bytes_transferred = '-'
req.client_disconnect = False
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']
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']
def handle_request(self, req):
"""
Entry point for auth requests (ones that match the self.auth_prefix).
Should return a WSGI-style callable (such as webob.Response).
:param req: swob.Request object
"""
req.start_time = time()
handler = None
try:
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)
return req.response
def handle_get_token(self, req):
"""
Handles the various `request for token and service end point(s)` calls.
There are various formats to support the various auth servers in the
past. Examples::
GET <auth-prefix>/v1/<act>/auth
GET <auth-prefix>/auth
GET <auth-prefix>/v1.0
All formats require GSS (Kerberos) authentication.
On successful authentication, the response will have X-Auth-Token
set to the token to use with Swift.
: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, 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)
return HTTPSeeOther(location=self.ext_authentication_url)
def filter_factory(global_conf, **local_conf):
"""Returns a WSGI filter app for use with paste.deploy."""
conf = global_conf.copy()
conf.update(local_conf)
def auth_filter(app):
return KerbAuth(app, conf)
return auth_filter

View File

@ -0,0 +1,107 @@
# 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.
import re
import random
import grp
import subprocess
from time import time
from gluster.swift.common.middleware.swiftkerbauth \
import TOKEN_LIFE, RESELLER_PREFIX
def get_remote_user(env):
"""Retrieve REMOTE_USER set by Apache from environment."""
remote_user = env.get('REMOTE_USER', "")
matches = re.match('([^@]+)@.*', remote_user)
if not matches:
raise RuntimeError("Malformed REMOTE_USER \"%s\"" % remote_user)
return matches.group(1)
def get_auth_data(mc, username):
"""
Returns the token, expiry time and groups for the user if it already exists
on memcache. Returns None otherwise.
:param mc: MemcacheRing object
:param username: swift user
"""
token, expires, groups = None, None, None
memcache_user_key = '%s/user/%s' % (RESELLER_PREFIX, username)
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
else:
expires, groups = None, None
return (token, expires, groups)
def set_auth_data(mc, username, token, expires, groups):
"""
Stores the following key value pairs on Memcache:
(token, expires+groups)
(user, token)
"""
auth_data = (expires, groups)
memcache_token_key = "%s/token/%s" % (RESELLER_PREFIX, token)
mc.set(memcache_token_key, auth_data, time=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, time=TOKEN_LIFE)
def generate_token():
"""Generates a random 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.
r = random.SystemRandom()
token = '%stk%s' % \
(RESELLER_PREFIX,
''.join(r.choice('abcdef0123456789') for x in range(32)))
return token
def get_groups(username):
"""Return a set of groups to which the user belongs to."""
# 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" % username)
(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.
if username in groups:
groups.remove(username)
groups = [username] + groups
groups = ','.join(groups)
return groups

View File

@ -1,4 +1,3 @@
#!/usr/bin/python
# Copyright (c) 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@ -65,6 +64,8 @@ setup(
'paste.filter_factory': [
'gswauth=gluster.swift.common.middleware.gswauth.swauth.'
'middleware:filter_factory',
'kerbauth=gluster.swift.common.middleware.'
'swiftkerbauth.kerbauth:filter_factory',
],
},
)

View File

@ -0,0 +1,340 @@
# 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.
import os
import errno
import unittest
from time import time
from mock import patch, Mock
from gluster.swift.common.middleware.swiftkerbauth import kerbauth as auth
from test.unit import FakeMemcache
from swift.common.swob import Request, Response
EXT_AUTHENTICATION_URL = "127.0.0.1"
REDIRECT_STATUS = 303 # HTTPSeeOther
def my_filter_factory(global_conf, **local_conf):
if 'ext_authentication_url' not in global_conf:
global_conf['ext_authentication_url'] = EXT_AUTHENTICATION_URL
conf = global_conf.copy()
conf.update(local_conf)
def auth_filter(app):
return auth.KerbAuth(app, conf)
return auth_filter
# Monkey patching filter_factory to always pass ext_authentication_url
# as a parameter. Absence of ext_authentication_url raises a RuntimeError
def patch_filter_factory():
auth.filter_factory = my_filter_factory
def unpatch_filter_factory():
reload(auth)
class FakeApp(object):
def __init__(self, status_headers_body_iter=None, acl=None, sync_key=None):
self.calls = 0
self.status_headers_body_iter = status_headers_body_iter
if not self.status_headers_body_iter:
self.status_headers_body_iter = iter([('404 Not Found', {}, '')])
self.acl = acl
self.sync_key = sync_key
def __call__(self, env, start_response):
self.calls += 1
self.request = Request.blank('', environ=env)
if self.acl:
self.request.acl = self.acl
if self.sync_key:
self.request.environ['swift_sync_key'] = self.sync_key
if 'swift.authorize' in env:
resp = env['swift.authorize'](self.request)
if resp:
return resp(env, start_response)
status, headers, body = self.status_headers_body_iter.next()
return Response(status=status, headers=headers,
body=body)(env, start_response)
class TestKerbAuth(unittest.TestCase):
# Patch auth.filter_factory()
patch_filter_factory()
def setUp(self):
self.test_auth = auth.filter_factory({})(FakeApp())
self.test_auth_passive = \
auth.filter_factory({'auth_method': 'passive'})(FakeApp())
def _make_request(self, path, **kwargs):
req = Request.blank(path, **kwargs)
req.environ['swift.cache'] = FakeMemcache()
return req
def test_no_ext_authentication_url(self):
app = FakeApp()
try:
# Use original auth.filter_factory and NOT monkey patched version
unpatch_filter_factory()
auth.filter_factory({})(app)
except RuntimeError as e:
# Restore monkey patched version
patch_filter_factory()
self.assertTrue(e.args[0].startswith("Missing filter parameter "
"ext_authentication_url"))
def test_reseller_prefix_init(self):
app = FakeApp()
ath = auth.filter_factory({})(app)
self.assertEquals(ath.reseller_prefix, 'AUTH_')
def test_auth_prefix_init(self):
app = FakeApp()
ath = auth.filter_factory({})(app)
self.assertEquals(ath.auth_prefix, '/auth/')
ath = auth.filter_factory({'auth_prefix': ''})(app)
self.assertEquals(ath.auth_prefix, '/auth/')
ath = auth.filter_factory({'auth_prefix': '/'})(app)
self.assertEquals(ath.auth_prefix, '/auth/')
ath = auth.filter_factory({'auth_prefix': '/test/'})(app)
self.assertEquals(ath.auth_prefix, '/test/')
ath = auth.filter_factory({'auth_prefix': '/test'})(app)
self.assertEquals(ath.auth_prefix, '/test/')
ath = auth.filter_factory({'auth_prefix': 'test/'})(app)
self.assertEquals(ath.auth_prefix, '/test/')
ath = auth.filter_factory({'auth_prefix': 'test'})(app)
self.assertEquals(ath.auth_prefix, '/test/')
def test_top_level_redirect(self):
req = self._make_request('/')
resp = req.get_response(self.test_auth)
self.assertEquals(resp.status_int, REDIRECT_STATUS)
self.assertEquals(req.environ['swift.authorize'],
self.test_auth.denied_response)
def test_override_asked_for_and_allowed(self):
self.test_auth = \
auth.filter_factory({'allow_overrides': 'true'})(FakeApp())
req = self._make_request('/v1/AUTH_account',
environ={'swift.authorize_override': True})
resp = req.get_response(self.test_auth)
self.assertEquals(resp.status_int, 404)
self.assertTrue('swift.authorize' not in req.environ)
def test_override_default_allowed(self):
req = self._make_request('/v1/AUTH_account',
environ={'swift.authorize_override': True})
resp = req.get_response(self.test_auth)
self.assertEquals(resp.status_int, 404)
self.assertTrue('swift.authorize' not in req.environ)
def test_options_call(self):
req = self._make_request('/v1/AUTH_cfa/c/o',
environ={'REQUEST_METHOD': 'OPTIONS'})
resp = self.test_auth.authorize(req)
self.assertEquals(resp, None)
def test_auth_deny_non_reseller_prefix_no_override(self):
fake_authorize = lambda x: Response(status='500 Fake')
req = self._make_request('/v1/BLAH_account',
headers={'X-Auth-Token': 'BLAH_t'},
environ={'swift.authorize': fake_authorize}
)
resp = req.get_response(self.test_auth)
self.assertEquals(resp.status_int, 500)
self.assertEquals(req.environ['swift.authorize'], fake_authorize)
def test_authorize_acl_group_access(self):
req = self._make_request('/v1/AUTH_cfa')
req.remote_user = 'act:usr,act'
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, 403)
req = self._make_request('/v1/AUTH_cfa')
req.remote_user = 'act:usr,act'
req.acl = 'act'
self.assertEquals(self.test_auth.authorize(req), None)
req = self._make_request('/v1/AUTH_cfa')
req.remote_user = 'act:usr,act'
req.acl = 'act:usr'
self.assertEquals(self.test_auth.authorize(req), None)
req = self._make_request('/v1/AUTH_cfa')
req.remote_user = 'act:usr,act'
def test_deny_cross_reseller(self):
# Tests that cross-reseller is denied, even if ACLs/group names match
req = self._make_request('/v1/OTHER_cfa')
req.remote_user = 'act:usr,act,AUTH_cfa'
req.acl = 'act'
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, 403)
def test_authorize_acl_referer_after_user_groups(self):
req = self._make_request('/v1/AUTH_cfa/c')
req.remote_user = 'act:usr'
req.acl = '.r:*,act:usr'
self.assertEquals(self.test_auth.authorize(req), None)
def test_detect_reseller_request(self):
req = self._make_request('/v1/AUTH_admin',
headers={'X-Auth-Token': 'AUTH_t'})
cache_key = 'AUTH_/token/AUTH_t'
cache_entry = (time() + 3600, '.reseller_admin')
req.environ['swift.cache'].set(cache_key, cache_entry)
req.get_response(self.test_auth)
self.assertTrue(req.environ.get('reseller_request', False))
def test_regular_is_not_owner(self):
orig_authorize = self.test_auth.authorize
owner_values = []
def mitm_authorize(req):
rv = orig_authorize(req)
owner_values.append(req.environ.get('swift_owner', False))
return rv
self.test_auth.authorize = mitm_authorize
req = self._make_request(
'/v1/AUTH_cfa/c',
headers={'X-Auth-Token': 'AUTH_t'})
req.remote_user = 'act:usr'
self.test_auth.authorize(req)
self.assertEquals(owner_values, [False])
def test_no_memcache(self):
env = {'swift.cache': None}
try:
self.test_auth.get_groups(env, None)
except Exception as e:
self.assertTrue(e.args[0].startswith("Memcache required"))
def test_handle_request(self):
req = self._make_request('/auth/v1.0')
resp = self.test_auth.handle_request(req)
self.assertEquals(resp.status_int, REDIRECT_STATUS)
def test_handle_request_bad_request(self):
req = self._make_request('////')
resp = self.test_auth.handle_request(req)
self.assertEquals(resp.status_int, 404)
def test_handle_request_no_handler(self):
req = self._make_request('/blah/blah/blah/blah')
resp = self.test_auth.handle_request(req)
self.assertEquals(resp.status_int, 400)
def test_handle_get_token_bad_request(self):
req = self._make_request('/blah/blah')
resp = self.test_auth.handle_get_token(req)
self.assertEquals(resp.status_int, 400)
req = self._make_request('/////')
resp = self.test_auth.handle_get_token(req)
self.assertEquals(resp.status_int, 404)
def test_handle(self):
req = self._make_request('/auth/v1.0')
resp = req.get_response(self.test_auth)
self.assertEquals(resp.status_int, REDIRECT_STATUS)
def test_authorize_invalid_req(self):
req = self._make_request('/')
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, 404)
def test_authorize_set_swift_owner(self):
req = self._make_request('/v1/AUTH_test/c1/o1')
req.remote_user = 'test,auth_reseller_admin'
resp = self.test_auth.authorize(req)
self.assertEquals(req.environ['swift_owner'], True)
self.assertTrue(resp is None)
req = self._make_request('/v1/AUTH_test/c1/o1')
req.remote_user = 'test,auth_test'
resp = self.test_auth.authorize(req)
self.assertEquals(req.environ['swift_owner'], True)
self.assertTrue(resp is None)
def test_authorize_swift_sync_key(self):
req = self._make_request(
'/v1/AUTH_cfa/c/o',
environ={'swift_sync_key': 'secret'},
headers={'x-container-sync-key': 'secret',
'x-timestamp': '123.456'})
resp = self.test_auth.authorize(req)
self.assertTrue(resp is None)
def test_authorize_acl_referrer_access(self):
req = self._make_request('/v1/AUTH_cfa/c')
req.remote_user = 'act:usr,act'
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, 403)
req = self._make_request('/v1/AUTH_cfa/c')
req.remote_user = 'act:usr,act'
req.acl = '.r:*,.rlistings'
self.assertEquals(self.test_auth.authorize(req), None)
req = self._make_request('/v1/AUTH_cfa/c')
req.remote_user = 'act:usr,act'
req.acl = '.r:*' # No listings allowed
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, 403)
req = self._make_request('/v1/AUTH_cfa/c')
req.remote_user = 'act:usr,act'
req.acl = '.r:.example.com,.rlistings'
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, 403)
req = self._make_request('/v1/AUTH_cfa/c')
req.remote_user = 'act:usr,act'
req.referer = 'http://www.example.com/index.html'
req.acl = '.r:.example.com,.rlistings'
self.assertEquals(self.test_auth.authorize(req), None)
req = self._make_request('/v1/AUTH_cfa/c')
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, REDIRECT_STATUS)
req = self._make_request('/v1/AUTH_cfa/c')
req.acl = '.r:*,.rlistings'
self.assertEquals(self.test_auth.authorize(req), None)
req = self._make_request('/v1/AUTH_cfa/c')
req.acl = '.r:*' # No listings allowed
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, REDIRECT_STATUS)
req = self._make_request('/v1/AUTH_cfa/c')
req.acl = '.r:.example.com,.rlistings'
resp = self.test_auth.authorize(req)
self.assertEquals(resp.status_int, REDIRECT_STATUS)
req = self._make_request('/v1/AUTH_cfa/c')
req.referer = 'http://www.example.com/index.html'
req.acl = '.r:.example.com,.rlistings'
self.assertEquals(self.test_auth.authorize(req), None)
def test_handle_x_storage_token(self):
req = self._make_request(
'/auth/v1.0',
headers={'x-storage-token': 'blahblah', })
resp = req.get_response(self.test_auth)
self.assertEquals(resp.status_int, REDIRECT_STATUS)
def test_invalid_token(self):
req = self._make_request('/k1/test')
req.environ['HTTP_X_AUTH_TOKEN'] = 'AUTH_blahblahblah'
resp = req.get_response(self.test_auth)
self.assertEquals(resp.status_int, REDIRECT_STATUS)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,78 @@
# 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.
import unittest
import re
from time import time
from test.unit import FakeMemcache
from gluster.swift.common.middleware.swiftkerbauth import kerbauth as auth
from gluster.swift.common.middleware.swiftkerbauth import kerbauth_utils as ku
class TestKerbUtils(unittest.TestCase):
def test_get_remote_user(self):
env = {'REMOTE_USER': "auth_admin@EXAMPLE.COM"}
result = ku.get_remote_user(env)
self.assertEqual(result, "auth_admin")
def test_get_remote_user_err(self):
env = {'REMOTE_USER': "auth_admin"}
try:
ku.get_remote_user(env)
except RuntimeError as err:
self.assertTrue(err.args[0].startswith("Malformed REMOTE_USER"))
else:
self.fail("Expected RuntimeError")
def test_get_auth_data(self):
mc = FakeMemcache()
expiry = time() + 100
ku.set_auth_data(mc, "root", "AUTH_tk", expiry, "root,admin")
(token, expires, groups) = ku.get_auth_data(mc, "root")
self.assertEqual(("AUTH_tk", expiry, "root,admin"),
(token, expires, groups))
def test_get_auth_data_err(self):
mc = FakeMemcache()
(token, expires, groups) = ku.get_auth_data(mc, "root")
self.assertEqual((token, expires, groups), (None, None, None))
expiry = time() - 1
ku.set_auth_data(mc, "root", "AUTH_tk", expiry, "root,admin")
(token, expires, groups) = ku.get_auth_data(mc, "root")
self.assertEqual((token, expires, groups), (None, None, None))
def test_set_auth_data(self):
mc = FakeMemcache()
expiry = time() + 100
ku.set_auth_data(mc, "root", "AUTH_tk", expiry, "root,admin")
def test_generate_token(self):
token = ku.generate_token()
matches = re.match('AUTH_tk[a-f0-9]{32}', token)
self.assertNotEqual(matches, None)
def test_get_groups(self):
groups = ku.get_groups("root")
self.assertTrue("root" in groups)
def test_get_groups_err(self):
try:
ku.get_groups("Zroot")
except RuntimeError as err:
self.assertTrue(err.args[0].startswith("Failure running id -G"))
else:
self.fail("Expected RuntimeError")