Support basic auth for image registry

If you stand up a docker-distribution registry, basic auth can be
configured with tls to lock down the registry. Currently the only auth
method that is supported is the bearer tokens used by the public
registries.  This change checks the www-authentication header to see if
we should try basic auth or the bearer token auth.

Change-Id: I57599ab3cd8773ae3312930145b9e84244940f41
Closes-Bug: #1893826
(cherry picked from commit 0c73e4f3eb)
This commit is contained in:
Alex Schultz 2020-09-01 14:17:12 -06:00
parent 974b967531
commit a023e2a776
2 changed files with 56 additions and 14 deletions

View File

@ -667,31 +667,48 @@ class BaseImageUploader(object):
raise ImageUploaderException(
'Unknown authentication method for headers: %s' % r.headers)
auth = None
www_auth = r.headers['www-authenticate']
if not www_auth.startswith('Bearer '):
raise ImageUploaderException(
'Unknown www-authenticate value: %s' % www_auth)
token_param = {}
realm = re.search('realm="(.*?)"', www_auth).group(1)
if 'service=' in www_auth:
token_param['service'] = re.search(
'service="(.*?)"', www_auth).group(1)
token_param['scope'] = 'repository:%s:pull' % image[1:]
if www_auth.startswith('Bearer '):
LOG.debug('Using bearer token auth')
realm = re.search('realm="(.*?)"', www_auth).group(1)
if 'service=' in www_auth:
token_param['service'] = re.search(
'service="(.*?)"', www_auth).group(1)
token_param['scope'] = 'repository:%s:pull' % image[1:]
auth = None
if username:
if username:
auth = requests_auth.HTTPBasicAuth(username, password)
LOG.debug('Token parameters: params {}'.format(token_param))
rauth = session.get(realm, params=token_param, auth=auth,
timeout=30)
rauth.raise_for_status()
auth_header = 'Bearer %s' % rauth.json()['token']
elif www_auth.startswith('Basic '):
LOG.debug('Using basic auth')
if not username or not password:
raise Exception('Authentication credentials required for '
'basic auth: %s' % url)
auth = requests_auth.HTTPBasicAuth(username, password)
LOG.debug('Token parameters: params {}'.format(token_param))
rauth = session.get(realm, params=token_param, auth=auth, timeout=30)
rauth.raise_for_status()
session.headers['Authorization'] = 'Bearer %s' % rauth.json()['token']
rauth = session.get(url, params=token_param, auth=auth, timeout=30)
rauth.raise_for_status()
auth_header = (
'Basic %s' % base64.b64encode(
six.b(username + ':' + password)).decode('ascii')
)
else:
raise ImageUploaderException(
'Unknown www-authenticate value: %s' % www_auth)
hash_request_id = hashlib.sha1(str(rauth.url).encode())
LOG.debug(
'Session authenticated: id {}'.format(
hash_request_id.hexdigest()
)
)
session.headers['Authorization'] = auth_header
setattr(session, 'reauthenticate', self.authenticate)
setattr(
session,

View File

@ -800,6 +800,31 @@ class TestBaseImageUploader(base.TestCase):
auth(url1).headers['Authorization']
)
def test_authenticate_basic_auth(self):
req = self.requests
auth = self.uploader.authenticate
url1 = urlparse('docker://myrepo.com/t/nova-api:latest')
# successful auth requests
headers = {
'www-authenticate': 'Basic realm="Some Realm"'
}
def req_match(request):
resp = requests.Response()
resp.headers = headers
resp.status_code = 401
# if we got sent an user/password, return 200
if 'Authorization' in request.headers:
resp.status_code = 200
return resp
req.add_matcher(req_match)
self.assertEqual(
'Basic Zm9vOmJhcg==',
auth(url1, username='foo', password='bar').headers['Authorization']
)
def test_authenticate_with_no_service(self):
req = self.requests
auth = self.uploader.authenticate