Use the instance service key for registry JWT signing
This commit is contained in:
parent
a4aa5cc02a
commit
8887f09ba8
26 changed files with 457 additions and 278 deletions
|
@ -1,29 +1,23 @@
|
|||
import logging
|
||||
import re
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from jsonschema import validate, ValidationError
|
||||
from flask import request, url_for
|
||||
from flask.ext.principal import identity_changed, Identity
|
||||
from cryptography.x509 import load_pem_x509_certificate
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cachetools import lru_cache
|
||||
|
||||
from app import app, get_app_url
|
||||
from app import app, get_app_url, instance_keys
|
||||
from .auth_context import set_grant_context, get_grant_context
|
||||
from .permissions import repository_read_grant, repository_write_grant
|
||||
from util.names import parse_namespace_repository
|
||||
from util.http import abort
|
||||
from util.security import strictjwt
|
||||
from util.security.registry_jwt import ANONYMOUS_SUB
|
||||
from util.security.registry_jwt import (ANONYMOUS_SUB, decode_bearer_token,
|
||||
InvalidBearerTokenException)
|
||||
from data import model
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
TOKEN_REGEX = re.compile(r'^Bearer (([a-zA-Z0-9+/]+\.)+[a-zA-Z0-9+-_/]+)$')
|
||||
CONTEXT_KINDS = ['user', 'token', 'oauth']
|
||||
|
||||
ACCESS_SCHEMA = {
|
||||
|
@ -142,34 +136,18 @@ def get_auth_headers(repository=None, scopes=None):
|
|||
return headers
|
||||
|
||||
|
||||
def identity_from_bearer_token(bearer_token, max_signed_s, public_key):
|
||||
def identity_from_bearer_token(bearer_token):
|
||||
""" Process a bearer token and return the loaded identity, or raise InvalidJWTException if an
|
||||
identity could not be loaded. Expects tokens and grants in the format of the Docker registry
|
||||
v2 auth spec: https://docs.docker.com/registry/spec/auth/token/
|
||||
"""
|
||||
logger.debug('Validating auth header: %s', bearer_token)
|
||||
|
||||
# Extract the jwt token from the header
|
||||
match = TOKEN_REGEX.match(bearer_token)
|
||||
if match is None:
|
||||
raise InvalidJWTException('Invalid bearer token format')
|
||||
|
||||
encoded = match.group(1)
|
||||
logger.debug('encoded JWT: %s', encoded)
|
||||
|
||||
# Load the JWT returned.
|
||||
try:
|
||||
expected_issuer = app.config['JWT_AUTH_TOKEN_ISSUER']
|
||||
audience = app.config['SERVER_HOSTNAME']
|
||||
max_exp = strictjwt.exp_max_s_option(max_signed_s)
|
||||
payload = strictjwt.decode(encoded, public_key, algorithms=['RS256'], audience=audience,
|
||||
issuer=expected_issuer, options=max_exp)
|
||||
except strictjwt.InvalidTokenError:
|
||||
logger.exception('Invalid token reason')
|
||||
raise InvalidJWTException('Invalid token')
|
||||
|
||||
if not 'sub' in payload:
|
||||
raise InvalidJWTException('Missing sub field in JWT')
|
||||
payload = decode_bearer_token(bearer_token, instance_keys)
|
||||
except InvalidBearerTokenException as bte:
|
||||
logger.exception('Invalid bearer token: %s', bte)
|
||||
raise InvalidJWTException(bte)
|
||||
|
||||
loaded_identity = Identity(payload['sub'], 'signed_jwt')
|
||||
|
||||
|
@ -203,13 +181,6 @@ def identity_from_bearer_token(bearer_token, max_signed_s, public_key):
|
|||
return loaded_identity, payload.get('context', default_context)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def load_public_key(certificate_file_path):
|
||||
with open(certificate_file_path) as cert_file:
|
||||
cert_obj = load_pem_x509_certificate(cert_file.read(), default_backend())
|
||||
return cert_obj.public_key()
|
||||
|
||||
|
||||
def process_registry_jwt_auth(scopes=None):
|
||||
def inner(func):
|
||||
@wraps(func)
|
||||
|
@ -217,14 +188,8 @@ def process_registry_jwt_auth(scopes=None):
|
|||
logger.debug('Called with params: %s, %s', args, kwargs)
|
||||
auth = request.headers.get('authorization', '').strip()
|
||||
if auth:
|
||||
max_signature_seconds = app.config.get('JWT_AUTH_MAX_FRESH_S', 3660)
|
||||
certificate_file_path = app.config['JWT_AUTH_CERTIFICATE_PATH']
|
||||
public_key = load_public_key(certificate_file_path)
|
||||
|
||||
try:
|
||||
extracted_identity, context = identity_from_bearer_token(auth, max_signature_seconds,
|
||||
public_key)
|
||||
|
||||
extracted_identity, context = identity_from_bearer_token(auth)
|
||||
identity_changed.send(app, identity=extracted_identity)
|
||||
set_grant_context(context)
|
||||
logger.debug('Identity changed to %s', extracted_identity.id)
|
||||
|
|
Reference in a new issue