Skip to content
This repository was archived by the owner on Sep 12, 2018. It is now read-only.

Commit e006736

Browse files
committed
Merge pull request #336 from shin-/private-layers
Private layers
2 parents b61de87 + aa2182c commit e006736

File tree

2 files changed

+47
-19
lines changed

2 files changed

+47
-19
lines changed

docker_registry/images.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ def put_image_json(image_id):
403403
parent_id = data.get('parent')
404404
if parent_id and not store.exists(store.image_json_path(data['parent'])):
405405
return toolkit.api_error('Image depends on a non existing parent')
406+
elif not toolkit.validate_parent_access(parent_id):
407+
return toolkit.api_error('Image depends on an unauthorized parent')
406408
json_path = store.image_json_path(image_id)
407409
mark_path = store.image_mark_path(image_id)
408410
if store.exists(json_path) and not store.exists(mark_path):

docker_registry/toolkit.py

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,37 @@ def response(data=None, code=200, headers=None, raw=False):
9292
return flask.current_app.make_response((data, code, h))
9393

9494

95-
def check_session():
96-
session = flask.session
97-
if not session:
98-
logger.debug('check_session: Session is empty')
95+
def validate_parent_access(parent_id):
96+
cfg = config.load()
97+
if cfg.standalone is not False:
98+
return True
99+
auth = _parse_auth_header()
100+
if not auth:
99101
return False
100-
if 'from' in session and get_remote_ip() != session['from']:
101-
logger.debug('check_session: Wrong source ip address')
102-
session.clear()
102+
full_repos_name = auth.get('repository', '').split('/')
103+
if len(full_repos_name) != 2:
104+
logger.debug('validate_token: Invalid repository field')
105+
return False
106+
index_endpoint = cfg.index_endpoint
107+
if index_endpoint is None:
108+
index_endpoint = 'https://index.docker.io'
109+
index_endpoint = index_endpoint.strip('/')
110+
url = '{0}/v1/images/{1}/{2}/layer/{3}/access'.format(
111+
index_endpoint, full_repos_name[0], full_repos_name[1], parent_id
112+
)
113+
headers = {'Authorization': flask.request.headers.get('authorization')}
114+
resp = requests.get(url, verify=True, headers=headers)
115+
if resp.status_code != 200:
116+
logger.debug('validate_parent_access: index returns status {0}'.format(
117+
resp.status_code
118+
))
119+
return False
120+
try:
121+
return json.loads(resp.text).get('authorized', False)
122+
123+
except json.JSONDecodeError:
124+
logger.debug('validate_parent_access: Wrong response format')
103125
return False
104-
# Session is valid
105-
return session.get('auth') is True
106126

107127

108128
def validate_token(auth):
@@ -150,18 +170,23 @@ def is_ssl():
150170
return False
151171

152172

153-
def check_token(args):
154-
cfg = config.load()
155-
if cfg.disable_token_auth is True or cfg.standalone is not False:
156-
return True
173+
def _parse_auth_header():
157174
auth = flask.request.headers.get('authorization', '')
158175
if auth.split(' ')[0].lower() != 'token':
159176
logger.debug('check_token: Invalid token format')
160-
return False
161-
logger.debug('args = {0}'.format(args))
177+
return None
162178
logger.debug('Auth Token = {0}'.format(auth))
163179
auth = dict(_re_authorization.findall(auth))
164180
logger.debug('auth = {0}'.format(auth))
181+
return auth
182+
183+
184+
def check_token(args):
185+
cfg = config.load()
186+
if cfg.disable_token_auth is True or cfg.standalone is not False:
187+
return True
188+
logger.debug('args = {0}'.format(args))
189+
auth = _parse_auth_header()
165190
if not auth:
166191
return False
167192
if 'namespace' in args and 'repository' in args:
@@ -190,7 +215,6 @@ def check_token(args):
190215
# Token is valid, we create a session
191216
session = flask.session
192217
session['repository'] = auth.get('repository')
193-
session['auth'] = True
194218
if is_ssl() is False:
195219
# We enforce the IP check only when not using SSL
196220
session['from'] = get_remote_ip()
@@ -232,9 +256,11 @@ def parse_content_signature(s):
232256
def requires_auth(f):
233257
@functools.wraps(f)
234258
def wrapper(*args, **kwargs):
235-
if check_signature() is True or check_session() is True \
236-
or check_token(kwargs) is True:
237-
return f(*args, **kwargs)
259+
session = flask.session
260+
if check_signature() is True or check_token(kwargs) is True:
261+
if 'from' not in session or session['from'] == get_remote_ip():
262+
return f(*args, **kwargs)
263+
session.clear()
238264
headers = {'WWW-Authenticate': 'Token'}
239265
return api_error('Requires authorization', 401, headers)
240266
return wrapper

0 commit comments

Comments
 (0)