Support 2-way SSL with Keystone server if it is configured to enforce

2-way SSL.  See also https://review.openstack.org/#/c/7706/ for the
corresponding review for the 2-way SSL addition to Keystone.

Change-Id: If0cb46a43d663687396d93604a7139d85a4e7114
This commit is contained in:
Liem Nguyen
2012-05-23 18:16:50 +00:00
committed by Adam Young
parent 29be6d081d
commit abc7c47c18
5 changed files with 132 additions and 6 deletions

View File

@@ -42,13 +42,32 @@ options, it is easier to just set them as environment variables:
The OpenStack Identity API version. The OpenStack Identity API version.
.. envvar:: OS_CA_CERT
The location for the CA truststore (PEM formatted) for this client.
.. envvar:: OS_CERT
The location for the keystore (PEM formatted) containing the public
key of this client. This keystore can also optionally contain the
private key of this client.
.. envvar:: OS_KEY
The location for the keystore (PEM formatted) containing the private
key of this client. This value can be empty if the private key is
included in the OS_CERT file.
For example, in Bash you'd use:: For example, in Bash you'd use::
export OS_USERNAME=yourname export OS_USERNAME=yourname
export OS_PASSWORD=yadayadayada export OS_PASSWORD=yadayadayada
export OS_TENANT_NAME=myproject export OS_TENANT_NAME=myproject
export OS_AUTH_URL=http://example.com:5000/v2.0/ export OS_AUTH_URL=http(s)://example.com:5000/v2.0/
export OS_IDENTITY_API_VERSION=2.0 export OS_IDENTITY_API_VERSION=2.0
export OS_CA_CERT=/etc/keystone/yourca.pem
export OS_CERT=/etc/keystone/yourpublickey.pem
export OS_KEY=/etc/keystone/yourprivatekey.pem
From there, all shell commands take the form:: From there, all shell commands take the form::

View File

@@ -38,8 +38,14 @@ class HTTPClient(httplib2.Http):
def __init__(self, username=None, tenant_id=None, tenant_name=None, def __init__(self, username=None, tenant_id=None, tenant_name=None,
password=None, auth_url=None, region_name=None, timeout=None, password=None, auth_url=None, region_name=None, timeout=None,
endpoint=None, token=None): endpoint=None, token=None, cacert=None, key=None,
super(HTTPClient, self).__init__(timeout=timeout) cert=None):
super(HTTPClient, self).__init__(timeout=timeout, ca_certs=cacert)
if cert:
if key:
self.add_certificate(key=key, cert=cert, domain='')
else:
self.add_certificate(key=cert, cert=cert, domain='')
self.username = username self.username = username
self.tenant_id = tenant_id self.tenant_id = tenant_id
self.tenant_name = tenant_name self.tenant_name = tenant_name

View File

@@ -39,7 +39,7 @@ class ServiceCatalog(object):
return token return token
def url_for(self, attr=None, filter_value=None, def url_for(self, attr=None, filter_value=None,
service_type='identity', endpoint_type='publicURL'): service_type='identity', endpoint_type='publicURL'):
"""Fetch an endpoint from the service catalog. """Fetch an endpoint from the service catalog.
Fetch the specified endpoint from the service catalog for Fetch the specified endpoint from the service catalog for

View File

@@ -127,6 +127,41 @@ class OpenStackIdentityShell(object):
default=env('SERVICE_ENDPOINT'), default=env('SERVICE_ENDPOINT'),
help='Defaults to env[SERVICE_ENDPOINT]') help='Defaults to env[SERVICE_ENDPOINT]')
parser.add_argument('--os_cacert', metavar='<ca-certificate>',
default=env('OS_CA_CERT'),
help='Defaults to env[OS_CA_CERT]')
parser.add_argument('--os_cert', metavar='<certificate>',
default=env('OS_CERT'),
help='Defaults to env[OS_CERT]')
parser.add_argument('--os_key', metavar='<key>',
default=env('OS_KEY'),
help='Defaults to env[OS_KEY]')
# FIXME(dtroyer): The args below are here for diablo compatibility,
# remove them in folsum cycle
parser.add_argument('--username',
metavar='<auth-user-name>',
help='Deprecated')
parser.add_argument('--password',
metavar='<auth-password>',
help='Deprecated')
parser.add_argument('--tenant_name',
metavar='<tenant-name>',
help='Deprecated')
parser.add_argument('--auth_url',
metavar='<auth-url>',
help='Deprecated')
parser.add_argument('--region_name',
metavar='<region-name>',
help='Deprecated')
return parser return parser
def get_subcommand_parser(self, version): def get_subcommand_parser(self, version):
@@ -246,7 +281,10 @@ class OpenStackIdentityShell(object):
'env[OS_AUTH_URL]') 'env[OS_AUTH_URL]')
if utils.isunauthenticated(args.func): if utils.isunauthenticated(args.func):
self.cs = shell_generic.CLIENT_CLASS(endpoint=args.os_auth_url) self.cs = shell_generic.CLIENT_CLASS(endpoint=args.os_auth_url,
cacert=args.os_cacert,
key=args.os_key,
cert=args.os_cert)
else: else:
token = None token = None
endpoint = None endpoint = None
@@ -262,7 +300,10 @@ class OpenStackIdentityShell(object):
endpoint=endpoint, endpoint=endpoint,
password=args.os_password, password=args.os_password,
auth_url=args.os_auth_url, auth_url=args.os_auth_url,
region_name=args.os_region_name) region_name=args.os_region_name,
cacert=args.os_cacert,
key=args.os_key,
cert=args.os_cert)
try: try:
args.func(self.cs, args) args.func(self.cs, args)

60
tests/test_https.py Normal file
View File

@@ -0,0 +1,60 @@
import httplib2
import mock
from keystoneclient import client
from tests import utils
FAKE_RESPONSE = httplib2.Response({"status": 200})
FAKE_BODY = '{"hi": "there"}'
MOCK_REQUEST = mock.Mock(return_value=(FAKE_RESPONSE, FAKE_BODY))
def get_client():
cl = client.HTTPClient(username="username", password="password",
tenant_id="tenant", auth_url="auth_test",
cacert="ca.pem", key="key.pem", cert="cert.pem")
return cl
def get_authed_client():
cl = get_client()
cl.management_url = "https://127.0.0.1:5000"
cl.auth_token = "token"
return cl
class ClientTest(utils.TestCase):
def test_get(self):
cl = get_authed_client()
@mock.patch.object(httplib2.Http, "request", MOCK_REQUEST)
@mock.patch('time.time', mock.Mock(return_value=1234))
def test_get_call():
resp, body = cl.get("/hi")
headers = {"X-Auth-Token": "token",
"User-Agent": cl.USER_AGENT}
MOCK_REQUEST.assert_called_with("https://127.0.0.1:5000/hi",
"GET", headers=headers)
# Automatic JSON parsing
self.assertEqual(body, {"hi": "there"})
test_get_call()
def test_post(self):
cl = get_authed_client()
@mock.patch.object(httplib2.Http, "request", MOCK_REQUEST)
def test_post_call():
cl.post("/hi", body=[1, 2, 3])
headers = {
"X-Auth-Token": "token",
"Content-Type": "application/json",
"User-Agent": cl.USER_AGENT
}
MOCK_REQUEST.assert_called_with("https://127.0.0.1:5000/hi",
"POST", headers=headers,
body='[1, 2, 3]')
test_post_call()