X.509 client authentication with Keystone. Implements blueprint 2-way-ssl
Change-Id: I5648acb8980782d2f1f932ee4947dd2deb490de5
This commit is contained in:
parent
37ff5b6883
commit
96f2fc18ee
34
bin/keystone
34
bin/keystone
|
@ -70,19 +70,35 @@ if __name__ == '__main__':
|
||||||
print "Using config file:", config_file
|
print "Using config file:", config_file
|
||||||
|
|
||||||
# Load Service API server
|
# Load Service API server
|
||||||
server = wsgi.Server()
|
if conf['service_ssl'] == 'True':
|
||||||
server.start(app, int(conf['service_port']), conf['service_host'])
|
server = wsgi.SslServer()
|
||||||
|
server.start(app, int(conf['service_port']), conf['service_host'],
|
||||||
|
certfile=conf['certfile'], keyfile=conf['keyfile'],
|
||||||
|
ca_certs=conf['ca_certs'],
|
||||||
|
cert_required=conf['cert_required'])
|
||||||
|
else:
|
||||||
|
server = wsgi.Server()
|
||||||
|
server.start(app, int(conf['service_port']), conf['service_host'])
|
||||||
|
|
||||||
print "Service API listening on %s:%s" % (
|
|
||||||
conf['service_host'], conf['service_port'])
|
print "Service API (ssl=%s) listening on %s:%s" % (
|
||||||
|
conf['service_ssl'], conf['service_host'], conf['service_port'])
|
||||||
|
|
||||||
# Load Admin API server
|
# Load Admin API server
|
||||||
admin_server = wsgi.Server()
|
if conf['admin_ssl'] == 'True':
|
||||||
admin_server.start(admin_app,
|
admin_server = wsgi.SslServer()
|
||||||
int(conf['admin_port']), conf['admin_host'])
|
admin_server.start(admin_app,
|
||||||
|
int(conf['admin_port']), conf['admin_host'],
|
||||||
|
certfile=conf['certfile'], keyfile=conf['keyfile'],
|
||||||
|
ca_certs=conf['ca_certs'],
|
||||||
|
cert_required=conf['cert_required'])
|
||||||
|
else:
|
||||||
|
admin_server = wsgi.Server()
|
||||||
|
admin_server.start(admin_app,
|
||||||
|
int(conf['admin_port']), conf['admin_host'])
|
||||||
|
|
||||||
print "Admin API listening on %s:%s" % (
|
print "Admin API (ssl=%s) listening on %s:%s" % (
|
||||||
conf['admin_host'], conf['admin_port'])
|
conf['admin_ssl'], conf['admin_host'], conf['admin_port'])
|
||||||
|
|
||||||
# Wait until done
|
# Wait until done
|
||||||
server.wait()
|
server.wait()
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
..
|
||||||
|
Copyright 2011 OpenStack, LLC
|
||||||
|
All Rights Reserved.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
=====================================================
|
||||||
|
Instructions for Keystone x.509 client authentication
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
Purpose
|
||||||
|
-------
|
||||||
|
Allows the Keystone middleware to authenticate itself with the Keystone server
|
||||||
|
via an x.509 client certificate. Both Service API and Admin API may be secured
|
||||||
|
with this feature.
|
||||||
|
|
||||||
|
Certificates
|
||||||
|
------------
|
||||||
|
The following types of certificates are required. A set of certficates is provided
|
||||||
|
in the examples/ssl directory with the Keystone distribution for testing. Here
|
||||||
|
is the description of each of them and their purpose:
|
||||||
|
|
||||||
|
1. ca.pem : Certificate Authority chain to validate against.
|
||||||
|
2. keystone.pem : Public certificate for Keystone server.
|
||||||
|
3. middleware-key.pem: Public and private certificate for Keystone middleware.
|
||||||
|
4. cakey.pem : Private key for the CA.
|
||||||
|
5. keystonekey.pem : Private key for the Keystone server.
|
||||||
|
|
||||||
|
Note that you may choose whatever names you want for these certificates, or combine
|
||||||
|
the public/private keys in the same file if you wish. These certificates are just
|
||||||
|
provided as an example.
|
||||||
|
|
||||||
|
Keystone server
|
||||||
|
---------------
|
||||||
|
By default, the Keystone server does not use SSL. To enable SSL with client authentication,
|
||||||
|
modify the etc/keystone.conf file accordingly:
|
||||||
|
|
||||||
|
1. To enable SSL for Service API:
|
||||||
|
service_ssl = True
|
||||||
|
2. To enable SSL for Admin API:
|
||||||
|
admin_ssl = True
|
||||||
|
3. To enable SSL client authentication:
|
||||||
|
cert_required = True
|
||||||
|
4. Set the location of the Keystone certificate file (example):
|
||||||
|
certfile = /etc/keystone/ca/certs/keystone.pem
|
||||||
|
5. Set the location of the Keystone private file (example):
|
||||||
|
keyfile = /etc/keystone/ca/private/keystonekey.pem
|
||||||
|
6. Set the location of the CA chain:
|
||||||
|
ca_certs = /etc/keystone/ca/certs/ca.pem
|
||||||
|
|
||||||
|
Middleware
|
||||||
|
----------
|
||||||
|
Add the following to your middleware configuration to support x.509 client authentication.
|
||||||
|
If cert_required is set to False on the keystone server, the certfile and keyfile parameters
|
||||||
|
in steps 3) and 4) may be commented out.
|
||||||
|
|
||||||
|
1. Specify 'https' as the auth_protocol:
|
||||||
|
auth_protocol = https
|
||||||
|
2. Modify the protocol in 'auth_uri' to be 'https' as well, if the service API is configured
|
||||||
|
for SSL:
|
||||||
|
auth_uri = https://localhost:5000/
|
||||||
|
3. Set the location of the middleware certificate file (example):
|
||||||
|
certfile = /etc/keystone/ca/certs/middleware-key.pem
|
||||||
|
4. Set the location of the Keystone private file (example):
|
||||||
|
keyfile = /etc/keystone/ca/certs/middleware-key.pem
|
||||||
|
|
||||||
|
For an example, take a look at the 'echo.ini' middleware configuration for the 'echo' example
|
||||||
|
service in the examples/echo directory.
|
||||||
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
You can test out how it works by using the 'echo' example service in the examples/echo directory
|
||||||
|
and the certficates included in the examples/ssl directory. Invoke the echo_client.py with
|
||||||
|
the path to the client certificate:
|
||||||
|
|
||||||
|
python echo_client.py -s <path to client certificate>
|
|
@ -32,12 +32,34 @@ service_host = 0.0.0.0
|
||||||
# Port the bind the API server to
|
# Port the bind the API server to
|
||||||
service_port = 5000
|
service_port = 5000
|
||||||
|
|
||||||
|
# SSL for API server
|
||||||
|
service_ssl = False
|
||||||
|
|
||||||
# Address to bind the Admin API server
|
# Address to bind the Admin API server
|
||||||
admin_host = 0.0.0.0
|
admin_host = 0.0.0.0
|
||||||
|
|
||||||
# Port the bind the Admin API server to
|
# Port the bind the Admin API server to
|
||||||
admin_port = 35357
|
admin_port = 35357
|
||||||
|
|
||||||
|
# SSL for API Admin server
|
||||||
|
admin_ssl = False
|
||||||
|
|
||||||
|
# Keystone certificate file (modify as needed)
|
||||||
|
# Only required if *_ssl is set to True
|
||||||
|
certfile = /etc/keystone/ssl/certs/keystone.pem
|
||||||
|
|
||||||
|
# Keystone private key file (modify as needed)
|
||||||
|
# Only required if *_ssl is set to True
|
||||||
|
keyfile = /etc/keystone/ssl/private/keystonekey.pem
|
||||||
|
|
||||||
|
# Keystone trusted CA certificates (modify as needed)
|
||||||
|
# Only required if *_ssl is set to True
|
||||||
|
ca_certs = /etc/keystone/ssl/certs/ca.pem
|
||||||
|
|
||||||
|
# Client certificate required
|
||||||
|
# Only relevant if *_ssl is set to True
|
||||||
|
cert_required = True
|
||||||
|
|
||||||
#Role that allows to perform admin operations.
|
#Role that allows to perform admin operations.
|
||||||
keystone-admin-role = Admin
|
keystone-admin-role = Admin
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@ service_port = 35357
|
||||||
;used to verify this component with the OpenStack service (or PAPIAuth)
|
;used to verify this component with the OpenStack service (or PAPIAuth)
|
||||||
service_pass = dTpw
|
service_pass = dTpw
|
||||||
|
|
||||||
|
;where to find x.509 client certificates
|
||||||
|
certfile = ../../ssl/certs/middleware-key.pem
|
||||||
|
keyfile = ../../ssl/certs/middleware-key.pem
|
||||||
|
|
||||||
[app:echo]
|
[app:echo]
|
||||||
paste.app_factory = echo:app_factory
|
paste.app_factory = echo:app_factory
|
||||||
|
@ -26,6 +29,10 @@ auth_host = 127.0.0.1
|
||||||
auth_port = 35357
|
auth_port = 35357
|
||||||
auth_protocol = http
|
auth_protocol = http
|
||||||
auth_uri = http://localhost:5000/
|
auth_uri = http://localhost:5000/
|
||||||
|
;Uncomment the following out for SSL connections
|
||||||
|
;auth_protocol = https
|
||||||
|
;auth_uri = https://localhost:5000/
|
||||||
|
|
||||||
;how to authenticate to the auth service for priviledged operations
|
;how to authenticate to the auth service for priviledged operations
|
||||||
;like validate token
|
;like validate token
|
||||||
admin_token = 999888777666
|
admin_token = 999888777666
|
||||||
|
|
|
@ -20,15 +20,29 @@ Implement a client for Echo service using Identity service
|
||||||
|
|
||||||
import httplib
|
import httplib
|
||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def keystone_conn():
|
||||||
|
""" Get a connection. If it is SSL, needs the '-s' option, optionally
|
||||||
|
followed by the location of the cert_file with private key. """
|
||||||
|
if '-s' in sys.argv:
|
||||||
|
cert_file = None
|
||||||
|
if len(sys.argv) > sys.argv.index('-s') + 1:
|
||||||
|
cert_file = sys.argv[sys.argv.index('-s') + 1]
|
||||||
|
conn = httplib.HTTPSConnection("localhost:5000", cert_file=cert_file)
|
||||||
|
else:
|
||||||
|
conn = httplib.HTTPConnection("localhost:5000")
|
||||||
|
return conn
|
||||||
|
|
||||||
|
|
||||||
def get_auth_token(username, password, tenant):
|
def get_auth_token(username, password, tenant):
|
||||||
headers = {"Content-type": "application/json",
|
headers = {"Content-type": "application/json",
|
||||||
"Accept": "application/json"}
|
"Accept": "application/json"}
|
||||||
params = {"passwordCredentials": {"username": username,
|
params = {"auth": {"passwordCredentials":
|
||||||
"password": password,
|
{"username": username, "password": password},
|
||||||
"tenantId": tenant}}
|
"tenantName": tenant}}
|
||||||
conn = httplib.HTTPConnection("localhost:5000")
|
conn = keystone_conn()
|
||||||
conn.request("POST", "/v2.0/tokens", json.dumps(params), headers=headers)
|
conn.request("POST", "/v2.0/tokens", json.dumps(params), headers=headers)
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
data = response.read()
|
data = response.read()
|
||||||
|
@ -72,7 +86,7 @@ if __name__ == '__main__':
|
||||||
print "\033[91mTrying with valid test credentials...\033[0m"
|
print "\033[91mTrying with valid test credentials...\033[0m"
|
||||||
auth = get_auth_token("joeuser", "secrete", "customer-x")
|
auth = get_auth_token("joeuser", "secrete", "customer-x")
|
||||||
obj = json.loads(auth)
|
obj = json.loads(auth)
|
||||||
token = obj["auth"]["token"]["id"]
|
token = obj["access"]["token"]["id"]
|
||||||
print "Token obtained:", token
|
print "Token obtained:", token
|
||||||
|
|
||||||
# Use that token to call an OpenStack service (echo)
|
# Use that token to call an OpenStack service (echo)
|
||||||
|
@ -94,5 +108,5 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
#Supply bad credentials
|
#Supply bad credentials
|
||||||
print "\033[91mTrying with bad credentials...\033[0m"
|
print "\033[91mTrying with bad credentials...\033[0m"
|
||||||
auth = get_auth_token("joeuser", "wrongpass", "1")
|
auth = get_auth_token("joeuser", "wrongpass", "customer-x")
|
||||||
print "Response:", auth
|
print "Response:", auth
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDmTCCAwKgAwIBAgIJALMGu1g0q5GjMA0GCSqGSIb3DQEBBQUAMIGQMQswCQYD
|
||||||
|
VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVJvc2V2aWxsZTESMBAGA1UE
|
||||||
|
ChMJT3BlbnN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTESMBAGA1UEAxMJbG9jYWxo
|
||||||
|
b3N0MSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3JnMB4XDTEx
|
||||||
|
MTAyMDE2MDQ0MloXDTIxMTAxNzE2MDQ0MlowgZAxCzAJBgNVBAYTAlVTMQswCQYD
|
||||||
|
VQQIEwJDQTESMBAGA1UEBxMJUm9zZXZpbGxlMRIwEAYDVQQKEwlPcGVuc3RhY2sx
|
||||||
|
ETAPBgNVBAsTCEtleXN0b25lMRIwEAYDVQQDEwlsb2NhbGhvc3QxJTAjBgkqhkiG
|
||||||
|
9w0BCQEWFmtleXN0b25lQG9wZW5zdGFjay5vcmcwgZ8wDQYJKoZIhvcNAQEBBQAD
|
||||||
|
gY0AMIGJAoGBAMfYcS0Fs7DRqdGSMVyrLk91vdzs+K6a6NOgppxhETqrOMAjW5yL
|
||||||
|
ajE2Ly48qfO/BRZR0kgTGSpnv7oiFzWLCvPf63nUnCalkE+uBpksY7BpphnTCJ8F
|
||||||
|
IsZ6aggAGKto9mmADpiKxt1uSQ6DDpPm8quXbMdSZTFOOVQNPYhwPMYvAgMBAAGj
|
||||||
|
gfgwgfUwHQYDVR0OBBYEFGA/MhYYUnjIdH9FWFVVo/YODkZBMIHFBgNVHSMEgb0w
|
||||||
|
gbqAFGA/MhYYUnjIdH9FWFVVo/YODkZBoYGWpIGTMIGQMQswCQYDVQQGEwJVUzEL
|
||||||
|
MAkGA1UECBMCQ0ExEjAQBgNVBAcTCVJvc2V2aWxsZTESMBAGA1UEChMJT3BlbnN0
|
||||||
|
YWNrMREwDwYDVQQLEwhLZXlzdG9uZTESMBAGA1UEAxMJbG9jYWxob3N0MSUwIwYJ
|
||||||
|
KoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3JnggkAswa7WDSrkaMwDAYD
|
||||||
|
VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBoeuR/pRznAtStj4Axe8Xq1ivL
|
||||||
|
jXFt2G9Pj+MwLs2wokcUBYz6/rJdSTjW21s4/FQCHiw9K7HA63c4mbjkRRgtJlXo
|
||||||
|
F5PiQqv4F1KqZmWeIDGxOGStQbgc77unsYYXILI27pSqQLKc9xlli77LekY+BzTK
|
||||||
|
tr5JYtKMaby4lJTg3A==
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,62 @@
|
||||||
|
Certificate:
|
||||||
|
Data:
|
||||||
|
Version: 3 (0x2)
|
||||||
|
Serial Number: 1 (0x1)
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
Issuer: C=US, ST=CA, L=Roseville, O=Openstack, OU=Keystone, CN=localhost/emailAddress=keystone@openstack.org
|
||||||
|
Validity
|
||||||
|
Not Before: Oct 20 16:34:17 2011 GMT
|
||||||
|
Not After : Oct 19 16:34:17 2012 GMT
|
||||||
|
Subject: C=US, ST=CA, L=Roseville, O=Openstack, OU=Keystone, CN=localhost/emailAddress=keystone@openstack.org
|
||||||
|
Subject Public Key Info:
|
||||||
|
Public Key Algorithm: rsaEncryption
|
||||||
|
RSA Public Key: (1024 bit)
|
||||||
|
Modulus (1024 bit):
|
||||||
|
00:9e:5a:5c:be:dc:20:d4:af:36:5c:33:6d:72:44:
|
||||||
|
94:59:c6:a9:24:ed:fa:8b:2c:53:ab:24:7d:79:46:
|
||||||
|
cc:a6:45:05:b0:57:b4:0d:d6:8f:f4:d9:a5:11:64:
|
||||||
|
e4:78:b1:26:30:de:fb:4a:72:c8:97:e7:31:4f:55:
|
||||||
|
bb:5b:16:d7:22:1b:13:ca:fc:6b:04:bd:15:9c:09:
|
||||||
|
51:d6:f9:14:51:67:a3:42:4a:81:ce:98:0f:6e:5c:
|
||||||
|
ac:7f:36:be:0f:79:ad:07:81:75:a2:21:a8:5f:e5:
|
||||||
|
9c:22:71:4c:db:63:b6:44:29:65:22:76:6e:07:98:
|
||||||
|
de:be:58:3f:b2:fe:cd:27:f7
|
||||||
|
Exponent: 65537 (0x10001)
|
||||||
|
X509v3 extensions:
|
||||||
|
X509v3 Basic Constraints:
|
||||||
|
CA:FALSE
|
||||||
|
Netscape Comment:
|
||||||
|
OpenSSL Generated Certificate
|
||||||
|
X509v3 Subject Key Identifier:
|
||||||
|
C1:E3:A1:36:45:3F:B5:3B:11:A1:23:A4:7E:3A:A0:F9:BC:F6:93:A3
|
||||||
|
X509v3 Authority Key Identifier:
|
||||||
|
keyid:60:3F:32:16:18:52:78:C8:74:7F:45:58:55:55:A3:F6:0E:0E:46:41
|
||||||
|
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
06:86:d7:5d:93:11:94:ce:23:ae:74:b2:16:09:99:32:63:3d:
|
||||||
|
d9:be:8f:99:87:43:7c:0d:27:25:5c:08:c2:d6:18:37:3c:4e:
|
||||||
|
b9:06:51:53:a9:d7:93:da:14:a1:25:96:2b:eb:8d:81:9d:68:
|
||||||
|
8d:ec:b8:1f:9e:09:80:25:fb:be:f8:20:5b:fc:ca:6c:3d:38:
|
||||||
|
c7:09:36:aa:dd:f8:0c:01:35:3e:c5:c5:3b:60:24:8c:5f:c5:
|
||||||
|
44:e7:7f:9b:ce:b6:d5:85:b7:93:e4:8a:a5:a9:90:ff:2d:09:
|
||||||
|
56:8c:e6:17:1f:07:33:0a:46:73:b1:65:13:d8:6f:39:76:3a:
|
||||||
|
93:87
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQUFADCBkDELMAkGA1UEBhMCVVMx
|
||||||
|
CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlSb3NldmlsbGUxEjAQBgNVBAoTCU9wZW5z
|
||||||
|
dGFjazERMA8GA1UECxMIS2V5c3RvbmUxEjAQBgNVBAMTCWxvY2FsaG9zdDElMCMG
|
||||||
|
CSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzAeFw0xMTEwMjAxNjM0
|
||||||
|
MTdaFw0xMjEwMTkxNjM0MTdaMIGQMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
|
||||||
|
EjAQBgNVBAcTCVJvc2V2aWxsZTESMBAGA1UEChMJT3BlbnN0YWNrMREwDwYDVQQL
|
||||||
|
EwhLZXlzdG9uZTESMBAGA1UEAxMJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZr
|
||||||
|
ZXlzdG9uZUBvcGVuc3RhY2sub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
|
||||||
|
gQCeWly+3CDUrzZcM21yRJRZxqkk7fqLLFOrJH15RsymRQWwV7QN1o/02aURZOR4
|
||||||
|
sSYw3vtKcsiX5zFPVbtbFtciGxPK/GsEvRWcCVHW+RRRZ6NCSoHOmA9uXKx/Nr4P
|
||||||
|
ea0HgXWiIahf5ZwicUzbY7ZEKWUidm4HmN6+WD+y/s0n9wIDAQABo3sweTAJBgNV
|
||||||
|
HRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZp
|
||||||
|
Y2F0ZTAdBgNVHQ4EFgQUweOhNkU/tTsRoSOkfjqg+bz2k6MwHwYDVR0jBBgwFoAU
|
||||||
|
YD8yFhhSeMh0f0VYVVWj9g4ORkEwDQYJKoZIhvcNAQEFBQADgYEABobXXZMRlM4j
|
||||||
|
rnSyFgmZMmM92b6PmYdDfA0nJVwIwtYYNzxOuQZRU6nXk9oUoSWWK+uNgZ1ojey4
|
||||||
|
H54JgCX7vvggW/zKbD04xwk2qt34DAE1PsXFO2AkjF/FROd/m8621YW3k+SKpamQ
|
||||||
|
/y0JVozmFx8HMwpGc7FlE9hvOXY6k4c=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,77 @@
|
||||||
|
Certificate:
|
||||||
|
Data:
|
||||||
|
Version: 3 (0x2)
|
||||||
|
Serial Number: 1 (0x1)
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
Issuer: C=US, ST=CA, L=Roseville, O=Openstack, OU=Keystone, CN=localhost/emailAddress=keystone@openstack.org
|
||||||
|
Validity
|
||||||
|
Not Before: Oct 20 17:22:02 2011 GMT
|
||||||
|
Not After : Oct 19 17:22:02 2012 GMT
|
||||||
|
Subject: C=US, ST=CA, O=Openstack, OU=Middleware, CN=localhost/emailAddress=middleware@openstack.org
|
||||||
|
Subject Public Key Info:
|
||||||
|
Public Key Algorithm: rsaEncryption
|
||||||
|
RSA Public Key: (1024 bit)
|
||||||
|
Modulus (1024 bit):
|
||||||
|
00:cb:8d:ff:0a:f8:1f:da:0b:65:d9:15:86:e7:4a:
|
||||||
|
89:07:81:26:7a:2e:ef:67:30:bb:5b:88:3e:73:31:
|
||||||
|
0e:c9:d9:eb:84:55:7c:57:1b:07:8a:29:7f:41:ed:
|
||||||
|
1a:47:b2:c4:74:3c:dc:52:81:81:ba:6c:43:b8:44:
|
||||||
|
bd:83:20:28:4a:82:03:34:f2:1e:88:89:1c:f3:d6:
|
||||||
|
ef:02:27:9f:7b:4b:dc:ed:50:91:7a:13:a0:8f:5f:
|
||||||
|
44:10:a6:17:01:6f:7d:7a:3a:a2:1a:28:4e:6e:c5:
|
||||||
|
b6:06:0b:ba:5c:c9:e9:15:39:95:54:63:bb:40:90:
|
||||||
|
5d:5d:76:f6:ae:ed:ee:ed:85
|
||||||
|
Exponent: 65537 (0x10001)
|
||||||
|
X509v3 extensions:
|
||||||
|
X509v3 Basic Constraints:
|
||||||
|
CA:FALSE
|
||||||
|
Netscape Comment:
|
||||||
|
OpenSSL Generated Certificate
|
||||||
|
X509v3 Subject Key Identifier:
|
||||||
|
5A:34:DE:19:11:FF:77:19:2E:E5:6C:36:FA:42:17:6B:46:AF:6A:61
|
||||||
|
X509v3 Authority Key Identifier:
|
||||||
|
keyid:60:3F:32:16:18:52:78:C8:74:7F:45:58:55:55:A3:F6:0E:0E:46:41
|
||||||
|
|
||||||
|
Signature Algorithm: sha1WithRSAEncryption
|
||||||
|
a2:1b:e0:d3:e5:c5:35:ad:18:cb:79:a4:fc:f3:d6:7b:53:1e:
|
||||||
|
dd:28:95:e0:6c:b0:db:fe:aa:30:04:19:c8:99:7a:eb:cb:ed:
|
||||||
|
dd:74:29:ad:f8:89:6a:ed:d0:10:35:b3:62:36:a2:b0:cc:9f:
|
||||||
|
86:e8:96:fd:d7:1b:5e:2c:64:b5:5d:f3:bf:1a:1a:07:8b:01:
|
||||||
|
1f:5f:09:c3:e1:62:cd:30:35:1a:08:e1:cd:71:be:8c:87:de:
|
||||||
|
f6:7d:40:1b:c6:5f:f0:80:a0:68:55:01:00:74:86:08:52:7e:
|
||||||
|
c7:fd:62:f9:e3:d0:f8:0b:b0:64:d9:20:70:80:ec:95:11:74:
|
||||||
|
fb:0b
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDAzCCAmygAwIBAgIBATANBgkqhkiG9w0BAQUFADCBkDELMAkGA1UEBhMCVVMx
|
||||||
|
CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlSb3NldmlsbGUxEjAQBgNVBAoTCU9wZW5z
|
||||||
|
dGFjazERMA8GA1UECxMIS2V5c3RvbmUxEjAQBgNVBAMTCWxvY2FsaG9zdDElMCMG
|
||||||
|
CSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzAeFw0xMTEwMjAxNzIy
|
||||||
|
MDJaFw0xMjEwMTkxNzIyMDJaMIGAMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
|
||||||
|
EjAQBgNVBAoTCU9wZW5zdGFjazETMBEGA1UECxMKTWlkZGxld2FyZTESMBAGA1UE
|
||||||
|
AxMJbG9jYWxob3N0MScwJQYJKoZIhvcNAQkBFhhtaWRkbGV3YXJlQG9wZW5zdGFj
|
||||||
|
ay5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMuN/wr4H9oLZdkVhudK
|
||||||
|
iQeBJnou72cwu1uIPnMxDsnZ64RVfFcbB4opf0HtGkeyxHQ83FKBgbpsQ7hEvYMg
|
||||||
|
KEqCAzTyHoiJHPPW7wInn3tL3O1QkXoToI9fRBCmFwFvfXo6ohooTm7FtgYLulzJ
|
||||||
|
6RU5lVRju0CQXV129q7t7u2FAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4
|
||||||
|
QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRa
|
||||||
|
NN4ZEf93GS7lbDb6QhdrRq9qYTAfBgNVHSMEGDAWgBRgPzIWGFJ4yHR/RVhVVaP2
|
||||||
|
Dg5GQTANBgkqhkiG9w0BAQUFAAOBgQCiG+DT5cU1rRjLeaT889Z7Ux7dKJXgbLDb
|
||||||
|
/qowBBnImXrry+3ddCmt+Ilq7dAQNbNiNqKwzJ+G6Jb91xteLGS1XfO/GhoHiwEf
|
||||||
|
XwnD4WLNMDUaCOHNcb6Mh972fUAbxl/wgKBoVQEAdIYIUn7H/WL549D4C7Bk2SBw
|
||||||
|
gOyVEXT7Cw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXAIBAAKBgQDLjf8K+B/aC2XZFYbnSokHgSZ6Lu9nMLtbiD5zMQ7J2euEVXxX
|
||||||
|
GweKKX9B7RpHssR0PNxSgYG6bEO4RL2DIChKggM08h6IiRzz1u8CJ597S9ztUJF6
|
||||||
|
E6CPX0QQphcBb316OqIaKE5uxbYGC7pcyekVOZVUY7tAkF1ddvau7e7thQIDAQAB
|
||||||
|
AoGAITSpzV1KvOQtGiuz1RlIn0vHPhlX/opplfX00g/HrM/65pyXaxJCuZwpYVTP
|
||||||
|
e7DC8X9YJbFwuzucFHxKOhDN4YbnW145bgfHbI9KLXtZiDvXvHg2MGKjpL/S3Lp3
|
||||||
|
zzWBo8gknmFGLK41WbYCCWKcvikEb3/KowcooznY5X5BjWECQQD6NC9Bi2EUUyPR
|
||||||
|
B2ZT3C3h2Hj53yqLkJzP0PaxTC+j7rsycy5r7UiOK8+8aC1T9EsaJrmEKlYBmlbd
|
||||||
|
lVdhohpNAkEA0EUphaVGURlNmXZgYdSZ1rrpJTvKbFtXmUCowi7Ml2h/oTuHDFHf
|
||||||
|
i4P8//79YB1uJ4Ll9edjJsZqtAErUTnMGQJBAJcKp7hutqU5Z3bJe8mGMqCTOLzH
|
||||||
|
LvzfyPpfkH0Jm/zfolxbUhvPO6yv4BFB5pM295uK4xVZJWCEVoofnIeQ/0UCQHuK
|
||||||
|
ex3esv5KTyCX+oYtkW+xgbjnZaSu7iBnHXPKROwPPZ4LbIlfS4Y7rejAfdX0vzHK
|
||||||
|
0NP0BHmsuwC5rNNKwIkCQBZqTnLVcisz1FRM2g/OKfWMx+lhVf5fIng+jUEJCdNE
|
||||||
|
fGjCUu4BRs+nXq6EzoijLvtrmRmFL7VYAKdabSVeLRc=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,18 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: DES-EDE3-CBC,116D8984CC1AC50A
|
||||||
|
|
||||||
|
PnDGqu3+5ITsGtOwCucdQBs7UmPpJKk3x+UuBdpJuygMEgGM70P+eN+RLTH/vaAl
|
||||||
|
GFWV9wvlL7j+azrEYlbiKhHn4+6SmDSWjjSVM5wGclzH/UYhyhVe/GZsJ8axW278
|
||||||
|
6EdwzmrbvIjuPTN/dJjyXdeOlFFpoCST8TI03+qYo9T0L86560Y3SjTr/hHhlVyL
|
||||||
|
PgwfcN3wdarhPloJvFoV10kNH3MBpgGeclNQcNVRH7+Z2DwzHgV3bW28w1h4dOI7
|
||||||
|
RrPpa1YaAi0lTltuiZYLUtTBI/+xEDf3kFkeSNSdl3sLp9faHUoosVObdFfLCmV1
|
||||||
|
+66MdqgesPFipkfGPlTGuUX9CmYMooCn+hs7+tVZUqCl/fcErFWeW8iS5+nrat7f
|
||||||
|
HBiAsOTZ96AEvy/FksYPymrdaK085aODgPqSfR2pvMuF66iKS1xRZiTpMnDApTVN
|
||||||
|
A6BOZdJgTqGX4yny7ORxQ90xkv39oZYS9cc10Hqec1DG1LWy9dfvavEPk7/GejiT
|
||||||
|
Z5SMbIHHiNe5tNTomGqtgLIhjfoRXH14zbPGbJ5bI0REJ+sdUM3ItH75tTHYQUIb
|
||||||
|
S8UQBkHzU+ExK4q5E3BvKR7UH0KD5z6B6QhAyCB6mQp+63nsIP5cImXuAY9u0s1a
|
||||||
|
3tOmvUpXWDpqJLeAShb3DAPz5+FMx4mbT5oZq1Y8q5RDqMSSrB7XilAroCqasUIb
|
||||||
|
LoLNMri7WKcrCT1dKjN4y17ucwU8wLPo7Lpo+x5/XWqQA5qSB83YG9nh6nuzYNyo
|
||||||
|
aUsLH4cfAj3vCPU+KQux5jJfpcma9fyxVfCfa55dmakmGM8ww6ZXXQ==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,15 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXAIBAAKBgQCeWly+3CDUrzZcM21yRJRZxqkk7fqLLFOrJH15RsymRQWwV7QN
|
||||||
|
1o/02aURZOR4sSYw3vtKcsiX5zFPVbtbFtciGxPK/GsEvRWcCVHW+RRRZ6NCSoHO
|
||||||
|
mA9uXKx/Nr4Pea0HgXWiIahf5ZwicUzbY7ZEKWUidm4HmN6+WD+y/s0n9wIDAQAB
|
||||||
|
AoGAf6eY3MPYM5yL1ggfUt62OSlNcdfnAgrZ6D2iaQIKOH+r9ly9aepuYpSR3VPY
|
||||||
|
WvN0NjGLopil3M8jkTEruGLRSgin8+v+qlcRFsoXamegc3NV4XtxJhSmSIocKIIK
|
||||||
|
14w5YxcDz1QGqoati4LxQ1D6V5eNhiO65YhdcUDarGnlcAECQQDK4vcBGLY7H91f
|
||||||
|
lGT/oFJ0crqF4V+bLxMO28NhtS0G+GoM0MKrPfIu+nZDlKQzzHUlEZMNXSLz1T+T
|
||||||
|
po92UVe3AkEAx87ZKDK4xZZRNz0dAe29a3gQ6PmVkav1+NIxr0MP7Ff4tH6K/uoz
|
||||||
|
96OZpZg+TxdaoxSeNltuUelt3/xPs9AxwQJBAI01t1FuD7fLD9ssf7djsMAX8jao
|
||||||
|
jFCITS10S+K/pR1K3RUaX8OsE9oavSGAXWEoFwi72KvefStU6zErJoLlTrUCQCG5
|
||||||
|
wmHMne+L/c1rHVhT/qMDMyd/6UUbV3tWT1ib4zYraylcKq34bikgjjCrT+kdsgjQ
|
||||||
|
1BustyRQWGF0PyfEvoECQGhVOY2byAOEau+GeTC0c3LIDoErx6WaW3d9ty3Tmx3G
|
||||||
|
Y81XHlbO4Lw2q8fWZ8Ah2ptjv2IpKj0GAGRiJ5NnPTM=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
|
@ -30,7 +30,7 @@ class ServiceClient(object):
|
||||||
|
|
||||||
_default_port = 5000
|
_default_port = 5000
|
||||||
|
|
||||||
def __init__(self, host, port=None):
|
def __init__(self, host, port=None, is_ssl=False, cert_file=None):
|
||||||
"""Initialize client.
|
"""Initialize client.
|
||||||
|
|
||||||
:param host: The hostname or IP of the Keystone service to use
|
:param host: The hostname or IP of the Keystone service to use
|
||||||
|
@ -39,6 +39,8 @@ class ServiceClient(object):
|
||||||
"""
|
"""
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port or self._default_port
|
self.port = port or self._default_port
|
||||||
|
self.is_ssl = is_ssl
|
||||||
|
self.cert_file = cert_file
|
||||||
|
|
||||||
def _http_request(self, verb, path, body=None, headers=None):
|
def _http_request(self, verb, path, body=None, headers=None):
|
||||||
"""Perform an HTTP request and return the HTTP response.
|
"""Perform an HTTP request and return the HTTP response.
|
||||||
|
@ -50,7 +52,11 @@ class ServiceClient(object):
|
||||||
:returns: httplib.HTTPResponse object
|
:returns: httplib.HTTPResponse object
|
||||||
|
|
||||||
"""
|
"""
|
||||||
connection = httplib.HTTPConnection(self.auth_address)
|
if (self.is_ssl):
|
||||||
|
connection = httplib.HTTPSConnection(self.auth_address,
|
||||||
|
cert_file=self.cert_file)
|
||||||
|
else:
|
||||||
|
connection = httplib.HTTPConnection(self.auth_address)
|
||||||
connection.request(verb, path, body=body, headers=headers)
|
connection.request(verb, path, body=body, headers=headers)
|
||||||
|
|
||||||
response = connection.getresponse()
|
response = connection.getresponse()
|
||||||
|
@ -109,7 +115,8 @@ class AdminClient(ServiceClient):
|
||||||
_default_admin_name = "admin"
|
_default_admin_name = "admin"
|
||||||
_default_admin_pass = "password"
|
_default_admin_pass = "password"
|
||||||
|
|
||||||
def __init__(self, host, port=None, admin_name=None, admin_pass=None):
|
def __init__(self, host, port=None, is_ssl=False, cert_file=None,
|
||||||
|
admin_name=None, admin_pass=None):
|
||||||
"""Initialize client.
|
"""Initialize client.
|
||||||
|
|
||||||
:param host: The hostname or IP of the Keystone service to use
|
:param host: The hostname or IP of the Keystone service to use
|
||||||
|
@ -118,7 +125,8 @@ class AdminClient(ServiceClient):
|
||||||
:param admin_pass: The password to use for the admin account
|
:param admin_pass: The password to use for the admin account
|
||||||
|
|
||||||
"""
|
"""
|
||||||
super(AdminClient, self).__init__(host, port=port)
|
super(AdminClient, self).__init__(host, port=port, is_ssl=is_ssl,
|
||||||
|
cert_file=cert_file)
|
||||||
self.admin_name = admin_name or self._default_admin_name
|
self.admin_name = admin_name or self._default_admin_name
|
||||||
self.admin_pass = admin_pass or self._default_admin_pass
|
self.admin_pass = admin_pass or self._default_admin_pass
|
||||||
self._admin_token = None
|
self._admin_token = None
|
||||||
|
|
|
@ -101,7 +101,8 @@ class BufferedHTTPConnection(HTTPConnection):
|
||||||
|
|
||||||
|
|
||||||
def http_connect(ipaddr, port, device, partition, method, path,
|
def http_connect(ipaddr, port, device, partition, method, path,
|
||||||
headers=None, query_string=None, ssl=False):
|
headers=None, query_string=None, ssl=False, key_file=None,
|
||||||
|
cert_file=None):
|
||||||
"""
|
"""
|
||||||
Helper function to create an HTTPConnection object. If ssl is set True,
|
Helper function to create an HTTPConnection object. If ssl is set True,
|
||||||
HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection
|
HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection
|
||||||
|
@ -116,26 +117,18 @@ def http_connect(ipaddr, port, device, partition, method, path,
|
||||||
:param headers: dictionary of headers
|
:param headers: dictionary of headers
|
||||||
:param query_string: request query string
|
:param query_string: request query string
|
||||||
:param ssl: set True if SSL should be used (default: False)
|
:param ssl: set True if SSL should be used (default: False)
|
||||||
|
:param key_file Private key file (not needed if cert_file has private key)
|
||||||
|
:param cert_file Certificate file (Keystore)
|
||||||
:returns: HTTPConnection object
|
:returns: HTTPConnection object
|
||||||
"""
|
"""
|
||||||
if ssl:
|
|
||||||
conn = HTTPSConnection('%s:%s' % (ipaddr, port))
|
|
||||||
else:
|
|
||||||
conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port))
|
|
||||||
path = quote('/' + device + '/' + str(partition) + path)
|
path = quote('/' + device + '/' + str(partition) + path)
|
||||||
if query_string:
|
return http_connect_raw(ipaddr, port, device, partition, method, path,
|
||||||
path += '?' + query_string
|
headers, query_string, ssl, key_file, cert_file)
|
||||||
conn.path = path
|
|
||||||
conn.putrequest(method, path)
|
|
||||||
if headers:
|
|
||||||
for header, value in headers.iteritems():
|
|
||||||
conn.putheader(header, value)
|
|
||||||
conn.endheaders()
|
|
||||||
return conn
|
|
||||||
|
|
||||||
|
|
||||||
def http_connect_raw(ipaddr, port, method, path, headers=None,
|
def http_connect_raw(ipaddr, port, method, path, headers=None,
|
||||||
query_string=None, ssl=False):
|
query_string=None, ssl=False, key_file=None,
|
||||||
|
cert_file=None):
|
||||||
"""
|
"""
|
||||||
Helper function to create an HTTPConnection object. If ssl is set True,
|
Helper function to create an HTTPConnection object. If ssl is set True,
|
||||||
HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection
|
HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection
|
||||||
|
@ -148,10 +141,13 @@ def http_connect_raw(ipaddr, port, method, path, headers=None,
|
||||||
:param headers: dictionary of headers
|
:param headers: dictionary of headers
|
||||||
:param query_string: request query string
|
:param query_string: request query string
|
||||||
:param ssl: set True if SSL should be used (default: False)
|
:param ssl: set True if SSL should be used (default: False)
|
||||||
|
:param key_file Private key file (not needed if cert_file has private key)
|
||||||
|
:param cert_file Certificate file (Keystore)
|
||||||
:returns: HTTPConnection object
|
:returns: HTTPConnection object
|
||||||
"""
|
"""
|
||||||
if ssl:
|
if ssl:
|
||||||
conn = HTTPSConnection('%s:%s' % (ipaddr, port))
|
conn = HTTPSConnection('%s:%s' % (ipaddr, port), key_file=key_file,
|
||||||
|
cert_file=cert_file)
|
||||||
else:
|
else:
|
||||||
conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port))
|
conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port))
|
||||||
if query_string:
|
if query_string:
|
||||||
|
|
|
@ -25,6 +25,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import datetime
|
import datetime
|
||||||
|
import ssl
|
||||||
|
|
||||||
import eventlet.wsgi
|
import eventlet.wsgi
|
||||||
eventlet.patcher.monkey_patch(all=False, socket=True)
|
eventlet.patcher.monkey_patch(all=False, socket=True)
|
||||||
|
@ -111,6 +112,24 @@ class Server(object):
|
||||||
log=WritableLogger(logger, logging.root.level))
|
log=WritableLogger(logger, logging.root.level))
|
||||||
|
|
||||||
|
|
||||||
|
class SslServer(Server):
|
||||||
|
"""SSL Server class to manage multiple WSGI sockets and applications."""
|
||||||
|
def start(self, application, port, host='0.0.0.0', backlog=128,
|
||||||
|
certfile=None, keyfile=None, ca_certs=None,
|
||||||
|
cert_required='True'):
|
||||||
|
"""Run a 2-way SSL WSGI server with the given application."""
|
||||||
|
socket = eventlet.listen((host, port), backlog=backlog)
|
||||||
|
if cert_required == 'True':
|
||||||
|
cert_reqs = ssl.CERT_REQUIRED
|
||||||
|
else:
|
||||||
|
cert_reqs = ssl.CERT_NONE
|
||||||
|
sslsocket = eventlet.wrap_ssl(socket, certfile=certfile,
|
||||||
|
keyfile=keyfile,
|
||||||
|
server_side=True, cert_reqs=cert_reqs,
|
||||||
|
ca_certs=ca_certs)
|
||||||
|
self.pool.spawn_n(self._run, application, sslsocket)
|
||||||
|
|
||||||
|
|
||||||
class Middleware(object):
|
class Middleware(object):
|
||||||
"""
|
"""
|
||||||
Base WSGI middleware wrapper. These classes require an application to be
|
Base WSGI middleware wrapper. These classes require an application to be
|
||||||
|
|
|
@ -127,6 +127,10 @@ class AuthProtocol(object):
|
||||||
# Credentials used to verify this component with the Auth service since
|
# Credentials used to verify this component with the Auth service since
|
||||||
# validating tokens is a privileged call
|
# validating tokens is a privileged call
|
||||||
self.admin_token = conf.get('admin_token')
|
self.admin_token = conf.get('admin_token')
|
||||||
|
# Certificate file and key file used to authenticate with Keystone
|
||||||
|
# server
|
||||||
|
self.cert_file = conf.get('certfile', None)
|
||||||
|
self.key_file = conf.get('keyfile', None)
|
||||||
|
|
||||||
def __init__(self, app, conf):
|
def __init__(self, app, conf):
|
||||||
""" Common initialization code """
|
""" Common initialization code """
|
||||||
|
@ -204,26 +208,6 @@ class AuthProtocol(object):
|
||||||
#Send request downstream
|
#Send request downstream
|
||||||
return self._forward_request(env, start_response, proxy_headers)
|
return self._forward_request(env, start_response, proxy_headers)
|
||||||
|
|
||||||
# NOTE(todd): unused
|
|
||||||
def get_admin_auth_token(self, username, password):
|
|
||||||
"""
|
|
||||||
This function gets an admin auth token to be used by this service to
|
|
||||||
validate a user's token. Validate_token is a priviledged call so
|
|
||||||
it needs to be authenticated by a service that is calling it
|
|
||||||
"""
|
|
||||||
headers = {"Content-type": "application/json",
|
|
||||||
"Accept": "application/json"}
|
|
||||||
params = {"passwordCredentials": {"username": username,
|
|
||||||
"password": password,
|
|
||||||
"tenantId": "1"}}
|
|
||||||
conn = httplib.HTTPConnection("%s:%s" \
|
|
||||||
% (self.auth_host, self.auth_port))
|
|
||||||
conn.request("POST", "/v2.0/tokens", json.dumps(params), \
|
|
||||||
headers=headers)
|
|
||||||
response = conn.getresponse()
|
|
||||||
data = response.read()
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _get_claims(self, env):
|
def _get_claims(self, env):
|
||||||
"""Get claims from request"""
|
"""Get claims from request"""
|
||||||
claims = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
|
claims = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
|
||||||
|
@ -262,8 +246,10 @@ class AuthProtocol(object):
|
||||||
#Khaled's version uses creds to get a token
|
#Khaled's version uses creds to get a token
|
||||||
# "X-Auth-Token": admin_token}
|
# "X-Auth-Token": admin_token}
|
||||||
# we're using a test token from the ini file for now
|
# we're using a test token from the ini file for now
|
||||||
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
conn = http_connect(self.auth_host, self.auth_port, 'HEAD',
|
||||||
'/v2.0/tokens/%s' % claims, headers=headers)
|
'/v2.0/tokens/%s' % claims, headers=headers,
|
||||||
|
ssl=(self.auth_protocol == 'https'),
|
||||||
|
key_file=self.key_file, cert_file=self.cert_file)
|
||||||
resp = conn.getresponse()
|
resp = conn.getresponse()
|
||||||
# data = resp.read()
|
# data = resp.read()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -289,7 +275,9 @@ class AuthProtocol(object):
|
||||||
# "X-Auth-Token": admin_token}
|
# "X-Auth-Token": admin_token}
|
||||||
# we're using a test token from the ini file for now
|
# we're using a test token from the ini file for now
|
||||||
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
||||||
'/v2.0/tokens/%s' % claims, headers=headers)
|
'/v2.0/tokens/%s' % claims, headers=headers,
|
||||||
|
ssl=(self.auth_protocol == 'https'),
|
||||||
|
key_file=self.key_file, cert_file=self.cert_file)
|
||||||
resp = conn.getresponse()
|
resp = conn.getresponse()
|
||||||
data = resp.read()
|
data = resp.read()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
|
@ -5,9 +5,12 @@ import tempfile
|
||||||
import time
|
import time
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
|
from functional.common import HttpTestCase
|
||||||
|
|
||||||
|
|
||||||
TEST_DIR = os.path.abspath(os.path.dirname(__file__))
|
TEST_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
BASE_DIR = os.path.abspath(os.path.join(TEST_DIR, '..', '..'))
|
BASE_DIR = os.path.abspath(os.path.join(TEST_DIR, '..', '..'))
|
||||||
|
TEST_CERT = os.path.join(BASE_DIR, 'examples/ssl/certs/middleware-key.pem')
|
||||||
|
|
||||||
|
|
||||||
def execute(cmd, raise_error=True):
|
def execute(cmd, raise_error=True):
|
||||||
|
@ -26,7 +29,6 @@ def execute(cmd, raise_error=True):
|
||||||
# Make sure that we use the programs in the
|
# Make sure that we use the programs in the
|
||||||
# current source directory's bin/ directory.
|
# current source directory's bin/ directory.
|
||||||
env['PATH'] = os.path.join(BASE_DIR, 'bin') + ':' + env['PATH']
|
env['PATH'] = os.path.join(BASE_DIR, 'bin') + ':' + env['PATH']
|
||||||
|
|
||||||
process = subprocess.Popen(cmd,
|
process = subprocess.Popen(cmd,
|
||||||
shell=True,
|
shell=True,
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
|
@ -58,7 +60,8 @@ class KeystoneTest(object):
|
||||||
end of test execution from the temporary space used to run these
|
end of test execution from the temporary space used to run these
|
||||||
tests).
|
tests).
|
||||||
"""
|
"""
|
||||||
CONF_PARAMS = {'test_dir': TEST_DIR}
|
CONF_PARAMS = {'test_dir': TEST_DIR, 'base_dir': BASE_DIR}
|
||||||
|
isSsl = False
|
||||||
|
|
||||||
def clear_database(self):
|
def clear_database(self):
|
||||||
"""Remove any test databases or files generated by previous tests."""
|
"""Remove any test databases or files generated by previous tests."""
|
||||||
|
@ -81,6 +84,10 @@ class KeystoneTest(object):
|
||||||
self.clear_database()
|
self.clear_database()
|
||||||
self.construct_temp_conf_file()
|
self.construct_temp_conf_file()
|
||||||
|
|
||||||
|
# Set client certificate for test client
|
||||||
|
if (self.isSsl == True):
|
||||||
|
os.environ['cert_file'] = TEST_CERT
|
||||||
|
|
||||||
# run the keystone server
|
# run the keystone server
|
||||||
print "Starting the keystone server..."
|
print "Starting the keystone server..."
|
||||||
self.server = subprocess.Popen(
|
self.server = subprocess.Popen(
|
||||||
|
@ -115,7 +122,6 @@ class KeystoneTest(object):
|
||||||
execute('coverage run %s discover -t %s -s %s' %
|
execute('coverage run %s discover -t %s -s %s' %
|
||||||
('/usr/bin/unit2', BASE_DIR, TEST_DIR))
|
('/usr/bin/unit2', BASE_DIR, TEST_DIR))
|
||||||
else:
|
else:
|
||||||
execute('unit2 discover -f -t %s -s %s' %
|
execute('unit2 discover -f -t %s -s %s' % (BASE_DIR, TEST_DIR))
|
||||||
(BASE_DIR, TEST_DIR))
|
|
||||||
finally:
|
finally:
|
||||||
self.tearDown()
|
self.tearDown()
|
||||||
|
|
|
@ -10,8 +10,10 @@ service-header-mappings = {
|
||||||
'cdn' : 'X-CDN-Manageent-Url'}
|
'cdn' : 'X-CDN-Manageent-Url'}
|
||||||
service_host = 0.0.0.0
|
service_host = 0.0.0.0
|
||||||
service_port = 5000
|
service_port = 5000
|
||||||
|
service_ssl = False
|
||||||
admin_host = 0.0.0.0
|
admin_host = 0.0.0.0
|
||||||
admin_port = 35357
|
admin_port = 35357
|
||||||
|
admin_ssl = False
|
||||||
keystone-admin-role = Admin
|
keystone-admin-role = Admin
|
||||||
keystone-service-admin-role = KeystoneServiceAdmin
|
keystone-service-admin-role = KeystoneServiceAdmin
|
||||||
hash-password = True
|
hash-password = True
|
||||||
|
|
|
@ -10,8 +10,10 @@ service-header-mappings = {
|
||||||
'cdn' : 'X-CDN-Manageent-Url'}
|
'cdn' : 'X-CDN-Manageent-Url'}
|
||||||
service_host = 0.0.0.0
|
service_host = 0.0.0.0
|
||||||
service_port = 5000
|
service_port = 5000
|
||||||
|
service_ssl = False
|
||||||
admin_host = 0.0.0.0
|
admin_host = 0.0.0.0
|
||||||
admin_port = 35357
|
admin_port = 35357
|
||||||
|
admin_ssl = False
|
||||||
keystone-admin-role = Admin
|
keystone-admin-role = Admin
|
||||||
keystone-service-admin-role = KeystoneServiceAdmin
|
keystone-service-admin-role = KeystoneServiceAdmin
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,10 @@ service-header-mappings = {
|
||||||
'cdn' : 'X-CDN-Manageent-Url'}
|
'cdn' : 'X-CDN-Manageent-Url'}
|
||||||
service_host = 0.0.0.0
|
service_host = 0.0.0.0
|
||||||
service_port = 5000
|
service_port = 5000
|
||||||
|
service_ssl = False
|
||||||
admin_host = 0.0.0.0
|
admin_host = 0.0.0.0
|
||||||
admin_port = 35357
|
admin_port = 35357
|
||||||
|
admin_ssl = False
|
||||||
keystone-admin-role = Admin
|
keystone-admin-role = Admin
|
||||||
keystone-service-admin-role = KeystoneServiceAdmin
|
keystone-service-admin-role = KeystoneServiceAdmin
|
||||||
hash-password = True
|
hash-password = True
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
[DEFAULT]
|
||||||
|
verbose = False
|
||||||
|
debug = False
|
||||||
|
default_store = sqlite
|
||||||
|
log_file = %(test_dir)s/keystone.ssl.log
|
||||||
|
backends = keystone.backends.sqlalchemy
|
||||||
|
service-header-mappings = {
|
||||||
|
'nova' : 'X-Server-Management-Url',
|
||||||
|
'swift' : 'X-Storage-Url',
|
||||||
|
'cdn' : 'X-CDN-Manageent-Url'}
|
||||||
|
service_host = 0.0.0.0
|
||||||
|
service_port = 5000
|
||||||
|
service_ssl = True
|
||||||
|
admin_host = 0.0.0.0
|
||||||
|
admin_port = 35357
|
||||||
|
admin_ssl = True
|
||||||
|
keystone-admin-role = Admin
|
||||||
|
keystone-service-admin-role = KeystoneServiceAdmin
|
||||||
|
hash-password = True
|
||||||
|
certfile = %(base_dir)s/examples/ssl/certs/keystone.pem
|
||||||
|
keyfile = %(base_dir)s/examples/ssl/private/keystonekey.pem
|
||||||
|
ca_certs = %(base_dir)s/examples/ssl/certs/ca.pem
|
||||||
|
cert_required = True
|
||||||
|
|
||||||
|
[keystone.backends.sqlalchemy]
|
||||||
|
sql_connection = for_testing_only
|
||||||
|
sql_idle_timeout = 30
|
||||||
|
backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Token', 'Service']
|
||||||
|
|
||||||
|
[pipeline:admin]
|
||||||
|
pipeline =
|
||||||
|
urlrewritefilter
|
||||||
|
admin_api
|
||||||
|
|
||||||
|
[pipeline:keystone-legacy-auth]
|
||||||
|
pipeline =
|
||||||
|
urlrewritefilter
|
||||||
|
legacy_auth
|
||||||
|
RAX-KEY-extension
|
||||||
|
service_api
|
||||||
|
|
||||||
|
[app:service_api]
|
||||||
|
paste.app_factory = keystone.server:service_app_factory
|
||||||
|
|
||||||
|
[app:admin_api]
|
||||||
|
paste.app_factory = keystone.server:admin_app_factory
|
||||||
|
|
||||||
|
[filter:urlrewritefilter]
|
||||||
|
paste.filter_factory = keystone.middleware.url:filter_factory
|
||||||
|
|
||||||
|
[filter:legacy_auth]
|
||||||
|
paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory
|
||||||
|
|
||||||
|
[filter:RAX-KEY-extension]
|
||||||
|
paste.filter_factory = keystone.contrib.extensions.service.raxkey.frontend:filter_factory
|
|
@ -2,9 +2,17 @@ import unittest2 as unittest
|
||||||
import httplib
|
import httplib
|
||||||
import uuid
|
import uuid
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
|
|
||||||
|
def isSsl():
|
||||||
|
""" See if we are testing with SSL. If cert is non-empty, we are! """
|
||||||
|
if 'cert_file' in os.environ:
|
||||||
|
return os.environ['cert_file']
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class HttpTestCase(unittest.TestCase):
|
class HttpTestCase(unittest.TestCase):
|
||||||
"""Performs generic HTTP request testing.
|
"""Performs generic HTTP request testing.
|
||||||
|
|
||||||
|
@ -23,7 +31,13 @@ class HttpTestCase(unittest.TestCase):
|
||||||
headers = {} if not headers else headers
|
headers = {} if not headers else headers
|
||||||
|
|
||||||
# Initialize a connection
|
# Initialize a connection
|
||||||
connection = httplib.HTTPConnection(host, port, timeout=20)
|
cert_file = isSsl()
|
||||||
|
if (cert_file != None):
|
||||||
|
connection = httplib.HTTPSConnection(host, port,
|
||||||
|
cert_file=cert_file,
|
||||||
|
timeout=20)
|
||||||
|
else:
|
||||||
|
connection = httplib.HTTPConnection(host, port, timeout=20)
|
||||||
|
|
||||||
# Perform the request
|
# Perform the request
|
||||||
connection.request(method, path, body, headers)
|
connection.request(method, path, body, headers)
|
||||||
|
@ -906,8 +920,7 @@ class FunctionalTestCase(ApiTestCase):
|
||||||
"global": is_global,
|
"global": is_global,
|
||||||
"versionId": version_id,
|
"versionId": version_id,
|
||||||
"versionInfo": version_info,
|
"versionInfo": version_info,
|
||||||
"versionList": version_list
|
"versionList": version_list}}
|
||||||
}}
|
|
||||||
return self.post_endpoint_template(as_json=data, **kwargs)
|
return self.post_endpoint_template(as_json=data, **kwargs)
|
||||||
|
|
||||||
def remove_endpoint_template(self, endpoint_template_id=None, **kwargs):
|
def remove_endpoint_template(self, endpoint_template_id=None, **kwargs):
|
||||||
|
|
|
@ -2,6 +2,7 @@ import unittest
|
||||||
|
|
||||||
import keystone.common.exception
|
import keystone.common.exception
|
||||||
import keystone.client
|
import keystone.client
|
||||||
|
from common import isSsl
|
||||||
|
|
||||||
|
|
||||||
class TestAdminClient(unittest.TestCase):
|
class TestAdminClient(unittest.TestCase):
|
||||||
|
@ -13,7 +14,10 @@ class TestAdminClient(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
Run before each test.
|
Run before each test.
|
||||||
"""
|
"""
|
||||||
|
cert_file = isSsl()
|
||||||
self.client = keystone.client.AdminClient("127.0.0.1",
|
self.client = keystone.client.AdminClient("127.0.0.1",
|
||||||
|
is_ssl=(cert_file != None),
|
||||||
|
cert_file=cert_file,
|
||||||
admin_name="admin",
|
admin_name="admin",
|
||||||
admin_pass="secrete")
|
admin_pass="secrete")
|
||||||
|
|
||||||
|
@ -71,7 +75,10 @@ class TestServiceClient(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
Run before each test.
|
Run before each test.
|
||||||
"""
|
"""
|
||||||
self.client = keystone.client.ServiceClient("127.0.0.1")
|
cert_file = isSsl()
|
||||||
|
self.client = keystone.client.ServiceClient("127.0.0.1",
|
||||||
|
is_ssl=(cert_file != None),
|
||||||
|
cert_file=cert_file)
|
||||||
|
|
||||||
def test_admin_get_token(self):
|
def test_admin_get_token(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -10,6 +10,12 @@ class SQLTest(KeystoneTest):
|
||||||
test_files = ('keystone.db',)
|
test_files = ('keystone.db',)
|
||||||
|
|
||||||
|
|
||||||
|
class SSLTest(KeystoneTest):
|
||||||
|
config_name = 'ssl.conf.template'
|
||||||
|
test_files = ('keystone.db',)
|
||||||
|
isSsl = True
|
||||||
|
|
||||||
|
|
||||||
class MemcacheTest(KeystoneTest):
|
class MemcacheTest(KeystoneTest):
|
||||||
"""Test defined using only SQLAlchemy and Memcache back-end"""
|
"""Test defined using only SQLAlchemy and Memcache back-end"""
|
||||||
config_name = 'memcache.conf.template'
|
config_name = 'memcache.conf.template'
|
||||||
|
@ -25,6 +31,7 @@ TESTS = [
|
||||||
SQLTest,
|
SQLTest,
|
||||||
# currently failing, and has yet to pass in jenkins: MemcacheTest,
|
# currently failing, and has yet to pass in jenkins: MemcacheTest,
|
||||||
LDAPTest,
|
LDAPTest,
|
||||||
|
SSLTest,
|
||||||
]
|
]
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -42,11 +42,11 @@ addlargs=
|
||||||
wrapper=""
|
wrapper=""
|
||||||
just_pep8=0
|
just_pep8=0
|
||||||
just_pylint=0
|
just_pylint=0
|
||||||
RUNTESTS="python run_tests.py $addlargs"
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
process_option $arg
|
process_option $arg
|
||||||
done
|
done
|
||||||
|
RUNTESTS="python run_tests.py $addlargs"
|
||||||
|
|
||||||
function run_tests {
|
function run_tests {
|
||||||
# Just run the test suites in current environment
|
# Just run the test suites in current environment
|
||||||
|
|
Loading…
Reference in New Issue