Add ability to restrict V1 push behind a namespace whitelist
Also enables the feature by default with an empty whitelist for QE Fixes https://jira.coreos.com/browse/QUAY-1342
This commit is contained in:
parent
d3dd2f7b7c
commit
b86d389c8e
9 changed files with 99 additions and 7 deletions
|
@ -1,12 +1,20 @@
|
|||
import logging
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from flask import Blueprint, make_response
|
||||
|
||||
from app import metric_queue
|
||||
import features
|
||||
|
||||
from app import metric_queue, app
|
||||
from endpoints.decorators import anon_protect, anon_allowed
|
||||
from util.metrics.metricqueue import time_blueprint
|
||||
from util.http import abort
|
||||
|
||||
v1_bp = Blueprint('v1', __name__)
|
||||
time_blueprint(v1_bp, metric_queue)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Note: This is *not* part of the Docker index spec. This is here for our own health check,
|
||||
# since we have nginx handle the _ping below.
|
||||
|
@ -26,6 +34,32 @@ def ping():
|
|||
return response
|
||||
|
||||
|
||||
def check_v1_push_enabled(namespace_name_kwarg='namespace_name'):
|
||||
""" Decorator which checks if V1 push is enabled for the current namespace. The first argument
|
||||
to the wrapped function must be the namespace name or there must be a kwarg with the
|
||||
name `namespace_name`.
|
||||
"""
|
||||
def wrapper(wrapped):
|
||||
@wraps(wrapped)
|
||||
def decorated(*args, **kwargs):
|
||||
if namespace_name_kwarg in kwargs:
|
||||
namespace_name = kwargs[namespace_name_kwarg]
|
||||
else:
|
||||
namespace_name = args[0]
|
||||
|
||||
if features.RESTRICTED_V1_PUSH:
|
||||
whitelist = app.config.get('V1_PUSH_WHITELIST') or []
|
||||
logger.debug('V1 push is restricted to whitelist: %s', whitelist)
|
||||
if namespace_name not in whitelist:
|
||||
abort(405,
|
||||
message=('V1 push support has been deprecated. To enable for this ' +
|
||||
'namespace, please contact support.'))
|
||||
|
||||
return wrapped(*args, **kwargs)
|
||||
return decorated
|
||||
return wrapper
|
||||
|
||||
|
||||
from endpoints.v1 import (
|
||||
index,
|
||||
registry,
|
||||
|
|
|
@ -18,7 +18,7 @@ from data import model
|
|||
from data.registry_model import registry_model
|
||||
from data.registry_model.manifestbuilder import create_manifest_builder, lookup_manifest_builder
|
||||
from endpoints.decorators import anon_protect, anon_allowed, parse_repository_name
|
||||
from endpoints.v1 import v1_bp
|
||||
from endpoints.v1 import v1_bp, check_v1_push_enabled
|
||||
from notifications import spawn_notification
|
||||
from util.audit import track_and_log
|
||||
from util.http import abort
|
||||
|
@ -165,6 +165,7 @@ def update_user(username):
|
|||
@v1_bp.route('/repositories/<repopath:repository>/', methods=['PUT'])
|
||||
@process_auth
|
||||
@parse_repository_name()
|
||||
@check_v1_push_enabled()
|
||||
@ensure_namespace_enabled
|
||||
@generate_headers(scope=GrantType.WRITE_REPOSITORY, add_grant_for_status=201)
|
||||
@anon_allowed
|
||||
|
@ -229,6 +230,7 @@ def create_repository(namespace_name, repo_name):
|
|||
@v1_bp.route('/repositories/<repopath:repository>/images', methods=['PUT'])
|
||||
@process_auth
|
||||
@parse_repository_name()
|
||||
@check_v1_push_enabled()
|
||||
@ensure_namespace_enabled
|
||||
@generate_headers(scope=GrantType.WRITE_REPOSITORY)
|
||||
@anon_allowed
|
||||
|
@ -295,6 +297,7 @@ def get_repository_images(namespace_name, repo_name):
|
|||
@v1_bp.route('/repositories/<repopath:repository>/images', methods=['DELETE'])
|
||||
@process_auth
|
||||
@parse_repository_name()
|
||||
@check_v1_push_enabled()
|
||||
@ensure_namespace_enabled
|
||||
@generate_headers(scope=GrantType.WRITE_REPOSITORY)
|
||||
@anon_allowed
|
||||
|
@ -304,6 +307,7 @@ def delete_repository_images(namespace_name, repo_name):
|
|||
|
||||
@v1_bp.route('/repositories/<repopath:repository>/auth', methods=['PUT'])
|
||||
@parse_repository_name()
|
||||
@check_v1_push_enabled()
|
||||
@ensure_namespace_enabled
|
||||
@anon_allowed
|
||||
def put_repository_auth(namespace_name, repo_name):
|
||||
|
|
|
@ -16,7 +16,7 @@ from data.registry_model import registry_model
|
|||
from data.registry_model.blobuploader import upload_blob, BlobUploadSettings, BlobUploadException
|
||||
from data.registry_model.manifestbuilder import lookup_manifest_builder
|
||||
from digest import checksums
|
||||
from endpoints.v1 import v1_bp
|
||||
from endpoints.v1 import v1_bp, check_v1_push_enabled
|
||||
from endpoints.v1.index import ensure_namespace_enabled
|
||||
from endpoints.decorators import anon_protect, check_region_blacklisted
|
||||
from util.http import abort, exact_abort
|
||||
|
@ -149,6 +149,7 @@ def get_image_layer(namespace, repository, image_id, headers):
|
|||
@v1_bp.route('/images/<image_id>/layer', methods=['PUT'])
|
||||
@process_auth
|
||||
@extract_namespace_repo_from_session
|
||||
@check_v1_push_enabled()
|
||||
@ensure_namespace_enabled
|
||||
@anon_protect
|
||||
def put_image_layer(namespace, repository, image_id):
|
||||
|
@ -240,6 +241,7 @@ def put_image_layer(namespace, repository, image_id):
|
|||
@v1_bp.route('/images/<image_id>/checksum', methods=['PUT'])
|
||||
@process_auth
|
||||
@extract_namespace_repo_from_session
|
||||
@check_v1_push_enabled()
|
||||
@ensure_namespace_enabled
|
||||
@anon_protect
|
||||
def put_image_checksum(namespace, repository, image_id):
|
||||
|
@ -345,6 +347,7 @@ def get_image_ancestry(namespace, repository, image_id, headers):
|
|||
@v1_bp.route('/images/<image_id>/json', methods=['PUT'])
|
||||
@process_auth
|
||||
@extract_namespace_repo_from_session
|
||||
@check_v1_push_enabled()
|
||||
@ensure_namespace_enabled
|
||||
@anon_protect
|
||||
def put_image_json(namespace, repository, image_id):
|
||||
|
|
|
@ -9,7 +9,7 @@ from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermissi
|
|||
from data.registry_model import registry_model
|
||||
from data.registry_model.manifestbuilder import lookup_manifest_builder
|
||||
from endpoints.decorators import anon_protect, parse_repository_name
|
||||
from endpoints.v1 import v1_bp
|
||||
from endpoints.v1 import v1_bp, check_v1_push_enabled
|
||||
from util.audit import track_and_log
|
||||
from util.names import TAG_ERROR, TAG_REGEX
|
||||
|
||||
|
@ -59,6 +59,7 @@ def get_tag(namespace_name, repo_name, tag):
|
|||
@process_auth
|
||||
@anon_protect
|
||||
@parse_repository_name()
|
||||
@check_v1_push_enabled()
|
||||
def put_tag(namespace_name, repo_name, tag):
|
||||
permission = ModifyRepositoryPermission(namespace_name, repo_name)
|
||||
repository_ref = registry_model.lookup_repository(namespace_name, repo_name, kind_filter='image')
|
||||
|
@ -98,6 +99,7 @@ def put_tag(namespace_name, repo_name, tag):
|
|||
@process_auth
|
||||
@anon_protect
|
||||
@parse_repository_name()
|
||||
@check_v1_push_enabled()
|
||||
def delete_tag(namespace_name, repo_name, tag):
|
||||
permission = ModifyRepositoryPermission(namespace_name, repo_name)
|
||||
repository_ref = registry_model.lookup_repository(namespace_name, repo_name, kind_filter='image')
|
||||
|
|
Reference in a new issue