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

Commit 789887b

Browse files
committed
Merge pull request #351 from dotcloud/remove-cookie
Remove cookie (flask.session)
2 parents d30962d + 9400667 commit 789887b

File tree

10 files changed

+56
-113
lines changed

10 files changed

+56
-113
lines changed

README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,6 @@ Available configuration options
9595
9696
### General options
9797
98-
1. `secret_key`: 64 character string, this key should be unique and secret. It
99-
is used by the Registry to sign secret things. If you leave this blank, the
100-
Registry will generate a random string.
10198
1. `loglevel`: string, level of debugging. Any of python's
10299
[logging](http://docs.python.org/2/library/logging.html) module levels:
103100
`debug`, `info`, `warn`, `error` or `critical`
@@ -397,11 +394,6 @@ using this command:
397394
gunicorn -k gevent --max-requests 100 --graceful-timeout 3600 -t 3600 -b localhost:5000 -w 8 docker_registry.wsgi:application
398395
```
399396
400-
Note that when using multiple workers, the secret_key for the Flask session
401-
must be set explicitly in config.yml. Otherwise each worker will use its own
402-
random secret key, leading to unpredictable behavior.
403-
404-
405397
#### nginx
406398
407399
[Here is an nginx configuration file example.](https://github.com/dotcloud/docker-registry/blob/master/contrib/nginx.conf), which applies to versions < 1.3.9 which are compiled with the [HttpChunkinModule](http://wiki.nginx.org/HttpChunkinModule).

config/config_s3.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ prod:
1212
s3_bucket: _env:AWS_BUCKET
1313
s3_encrypt: true
1414
s3_secure: true
15-
secret_key: REPLACEME
1615
s3_encrypt: true
1716
s3_secure: true
1817
storage_path: /images
19-
storage_redirect: False # Redirect signed URL for image files
18+
storage_redirect: False # Redirect signed URL for image files

config/config_sample.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ common:
2828
# See: https://developers.google.com/accounts/docs/OAuth2.
2929
oauth2: REPLACEME
3030

31-
# Let gunicorn set this environment variable or set a random string here
32-
secret_key: _env:SECRET_KEY
33-
3431
search_backend: "_env:SEARCH_BACKEND:"
3532
sqlalchemy_index_database:
3633
"_env:SQLALCHEMY_INDEX_DATABASE:sqlite:////tmp/docker-registry.db"

config/config_test.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ test:
44
loglevel: info
55
storage: local
66
storage_path: /tmp/test
7-
secret_key: foobar42
87
s3_access_key: _env:S3_ACCESS_KEY
98
s3_secret_key: _env:S3_SECRET_KEY
109
s3_bucket: _env:S3_BUCKET

config/gunicorn_config.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
## Gunicorn config file
22

33
import os
4-
import random
5-
import string
64

75
flavor = os.environ.get('SETTINGS_FLAVOR', 'dev')
86

9-
if flavor == 'dev':
10-
os.environ['SECRET_KEY'] = ''.join(
11-
[random.choice(string.ascii_lowercase + string.ascii_uppercase +
12-
string.digits) for x in range(128)])
13-
147
reload = True
158
bind = '0.0.0.0:{0}'.format(os.environ.get('PORT_WWW', 8000))
169
graceful_timeout = 3600

docker_registry/app.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
from . import toolkit
1414
from .lib import config
1515

16-
from .lib.core.exceptions import ConfigError
17-
1816

1917
VERSION = '0.6.9'
2018
app = flask.Flask('docker-registry')
@@ -46,10 +44,6 @@ def after_request(response):
4644

4745

4846
def init():
49-
# Configure the secret key
50-
if not cfg.secret_key:
51-
raise ConfigError('`secret_key\' is not set')
52-
app.secret_key = cfg.secret_key
5347
# Configure the email exceptions
5448
info = cfg.email_exceptions
5549
if info:

docker_registry/images.py

Lines changed: 33 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ def wrapper(*args, **kwargs):
5050
return flask.Response(status=304, headers=headers)
5151
kwargs['headers'] = headers
5252
# Prevent the Cookie to be sent when the object is cacheable
53-
flask.session.modified = False
5453
return f(*args, **kwargs)
5554
return wrapper
5655

@@ -117,9 +116,11 @@ def _get_image_json(image_id, headers=None):
117116
headers['X-Docker-Size'] = str(size)
118117
except OSError:
119118
pass
120-
checksum_path = store.image_checksum_path(image_id)
121-
if store.exists(checksum_path):
122-
headers['X-Docker-Checksum'] = store.get_content(checksum_path)
119+
try:
120+
csums = load_checksums(image_id)
121+
headers['X-Docker-Payload-Checksum'] = csums
122+
except IOError:
123+
pass
123124
return toolkit.response(data, headers=headers, raw=True)
124125

125126

@@ -254,20 +255,8 @@ def put_image_layer(image_id):
254255
csums.append(tarsum.compute())
255256
tmp.close()
256257

257-
try:
258-
checksum = store.get_content(store.image_checksum_path(image_id))
259-
except IOError:
260-
# We don't have a checksum stored yet, that's fine skipping the check.
261-
# Not removing the mark though, image is not downloadable yet.
262-
flask.session['checksum'] = csums
263-
return toolkit.response()
264-
# We check if the checksums provided matches one the one we computed
265-
if checksum not in csums:
266-
logger.debug('put_image_layer: Wrong checksum')
267-
return toolkit.api_error('Checksum mismatch, ignoring the layer')
268-
269-
# Checksum is ok, we remove the marker
270-
store.remove(mark_path)
258+
# We store the computed checksums for a later check
259+
save_checksums(image_id, csums)
271260
return toolkit.response()
272261

273262

@@ -280,20 +269,16 @@ def put_image_checksum(image_id):
280269
checksum = flask.request.headers.get('X-Docker-Checksum-Payload')
281270
if not checksum:
282271
return toolkit.api_error('Missing Image\'s checksum')
283-
if not flask.session.get('checksum'):
284-
return toolkit.api_error('Checksum not found in Cookie')
285272
if not store.exists(store.image_json_path(image_id)):
286273
return toolkit.api_error('Image not found', 404)
287274
mark_path = store.image_mark_path(image_id)
288275
if not store.exists(mark_path):
289276
return toolkit.api_error('Cannot set this image checksum', 409)
290-
err = store_checksum(image_id, checksum)
291-
if err:
292-
return toolkit.api_error(err)
293-
if checksum not in flask.session.get('checksum', []):
277+
checksums = load_checksums(image_id)
278+
if checksum not in checksums:
294279
logger.debug('put_image_checksum: Wrong checksum. '
295280
'Provided: {0}; Expected: {1}'.format(
296-
checksum, flask.session.get('checksum')))
281+
checksum, checksums))
297282
return toolkit.api_error('Checksum mismatch')
298283
# Checksum is ok, we remove the marker
299284
store.remove(mark_path)
@@ -352,26 +337,37 @@ def get_image_ancestry(image_id, headers):
352337

353338

354339
def check_images_list(image_id):
355-
full_repos_name = flask.session.get('repository')
356-
if not full_repos_name:
357-
# We only enforce this check when there is a repos name in the session
358-
# otherwise it means that the auth is disabled.
340+
if cfg.disable_token_auth is True or cfg.standalone is not False:
341+
# We enforce the check only when auth is enabled so we have a token.
359342
return True
343+
repository = toolkit.get_repository()
360344
try:
361-
path = store.images_list_path(*full_repos_name.split('/'))
345+
path = store.images_list_path(*repository)
362346
images_list = json.loads(store.get_content(path))
363347
except IOError:
364348
return False
365349
return (image_id in images_list)
366350

367351

368-
def store_checksum(image_id, checksum):
369-
checksum_parts = checksum.split(':')
370-
if len(checksum_parts) != 2:
371-
return 'Invalid checksum format'
352+
def save_checksums(image_id, checksums):
353+
for checksum in checksums:
354+
checksum_parts = checksum.split(':')
355+
if len(checksum_parts) != 2:
356+
return 'Invalid checksum format'
372357
# We store the checksum
373358
checksum_path = store.image_checksum_path(image_id)
374-
store.put_content(checksum_path, checksum)
359+
store.put_content(checksum_path, json.dumps(checksums))
360+
361+
362+
def load_checksums(image_id):
363+
checksum_path = store.image_checksum_path(image_id)
364+
data = store.get_content(checksum_path)
365+
try:
366+
return json.loads(data)
367+
except ValueError:
368+
# NOTE(sam): For backward compatibility only, existing data may not be
369+
# a valid json but a simple string.
370+
return [data]
375371

376372

377373
@app.route('/v1/images/<image_id>/json', methods=['PUT'])
@@ -385,16 +381,6 @@ def put_image_json(image_id):
385381
return toolkit.api_error('Invalid JSON')
386382
if 'id' not in data:
387383
return toolkit.api_error('Missing key `id\' in JSON')
388-
# Read the checksum
389-
checksum = flask.request.headers.get('X-Docker-Checksum')
390-
if checksum:
391-
# Storing the checksum is optional at this stage
392-
err = store_checksum(image_id, checksum)
393-
if err:
394-
return toolkit.api_error(err)
395-
else:
396-
# We cleanup any old checksum in case it's a retry after a fail
397-
store.remove(store.image_checksum_path(image_id))
398384
if image_id != data['id']:
399385
return toolkit.api_error('JSON data contains invalid id')
400386
if check_images_list(image_id) is False:
@@ -412,6 +398,8 @@ def put_image_json(image_id):
412398
# If we reach that point, it means that this is a new image or a retry
413399
# on a failed push
414400
store.put_content(mark_path, 'true')
401+
# We cleanup any old checksum in case it's a retry after a fail
402+
store.remove(store.image_checksum_path(image_id))
415403
store.put_content(json_path, flask.request.data)
416404
layers.generate_ancestry(image_id, parent_id)
417405
return toolkit.response()

docker_registry/toolkit.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,7 @@ def check_token(args):
212212
return False
213213
if validate_token(auth) is False:
214214
return False
215-
# Token is valid, we create a session
216-
session = flask.session
217-
session['repository'] = auth.get('repository')
218-
if is_ssl() is False:
219-
# We enforce the IP check only when not using SSL
220-
session['from'] = get_remote_ip()
215+
# Token is valid
221216
return True
222217

223218

@@ -256,11 +251,8 @@ def parse_content_signature(s):
256251
def requires_auth(f):
257252
@functools.wraps(f)
258253
def wrapper(*args, **kwargs):
259-
session = flask.session
260254
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()
255+
return f(*args, **kwargs)
264256
headers = {'WWW-Authenticate': 'Token'}
265257
return api_error('Requires authorization', 401, headers)
266258
return wrapper

test/base.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def _open(*args, **kwargs):
2222
if 'headers' not in kwargs:
2323
kwargs['headers'] = {}
2424
if 'User-Agent' not in kwargs['headers']:
25-
ua = ('docker/0.0 go/go1.2.1 git-commit/3600720 '
25+
ua = ('docker/0.10.1 go/go1.2.1 git-commit/3600720 '
2626
'kernel/3.8.0-19-generic os/linux arch/amd64')
2727
kwargs['headers']['User-Agent'] = ua
2828
return orig_open(*args, **kwargs)
@@ -32,8 +32,20 @@ def gen_random_string(self, length=16):
3232
return ''.join([random.choice(string.ascii_uppercase + string.digits)
3333
for x in range(length)]).lower()
3434

35-
def upload_image(self, image_id, parent_id, layer,
36-
set_checksum_callback=None):
35+
def set_image_checksum(self, image_id, checksum):
36+
headers = {'X-Docker-Checksum-Payload': checksum}
37+
url = '/v1/images/{0}/checksum'.format(image_id)
38+
resp = self.http_client.put(url, headers=headers)
39+
self.assertEqual(resp.status_code, 200, resp.data)
40+
# Once the checksum test passed, the image is "locked"
41+
resp = self.http_client.put(url, headers=headers)
42+
self.assertEqual(resp.status_code, 409, resp.data)
43+
# Cannot set the checksum on an non-existing image
44+
url = '/v1/images/{0}/checksum'.format(self.gen_random_string())
45+
resp = self.http_client.put(url, headers=headers)
46+
self.assertEqual(resp.status_code, 404, resp.data)
47+
48+
def upload_image(self, image_id, parent_id, layer):
3749
json_obj = {
3850
'id': image_id
3951
}
@@ -43,9 +55,7 @@ def upload_image(self, image_id, parent_id, layer,
4355
h = hashlib.sha256(json_data + '\n')
4456
h.update(layer)
4557
layer_checksum = 'sha256:{0}'.format(h.hexdigest())
46-
headers = {'X-Docker-Checksum': layer_checksum}
47-
if set_checksum_callback:
48-
headers = {}
58+
headers = {'X-Docker-Payload-Checksum': layer_checksum}
4959
resp = self.http_client.put('/v1/images/{0}/json'.format(image_id),
5060
headers=headers,
5161
data=json_data)
@@ -58,10 +68,10 @@ def upload_image(self, image_id, parent_id, layer,
5868
input_stream=layer_file)
5969
layer_file.close()
6070
self.assertEqual(resp.status_code, 200, resp.data)
61-
if set_checksum_callback:
62-
set_checksum_callback(image_id, layer_checksum)
71+
self.set_image_checksum(image_id, layer_checksum)
6372
# Push done, test reading the image
6473
resp = self.http_client.get('/v1/images/{0}/json'.format(image_id))
65-
self.assertEqual(resp.headers.get('x-docker-size'), str(len(layer)))
6674
self.assertEqual(resp.status_code, 200, resp.data)
67-
self.assertEqual(resp.headers['x-docker-checksum'], layer_checksum)
75+
self.assertEqual(resp.headers.get('x-docker-size'), str(len(layer)))
76+
self.assertEqual(resp.headers['x-docker-payload-checksum'],
77+
layer_checksum)

test/test_images.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,27 +64,6 @@ def test_notfound(self):
6464
self.gen_random_string()))
6565
self.assertEqual(resp.status_code, 404, resp.data)
6666

67-
def test_set_checksum_after(self):
68-
def set_checksum_callback(image_id, checksum):
69-
headers = {'X-Docker-Checksum': checksum}
70-
url = '/v1/images/{0}/checksum'.format(image_id)
71-
resp = self.http_client.put(url, headers=headers)
72-
self.assertEqual(resp.status_code, 200, resp.data)
73-
# Once the checksum test passed, the image is "locked"
74-
resp = self.http_client.put(url, headers=headers)
75-
self.assertEqual(resp.status_code, 409, resp.data)
76-
# Cannot set the checksum on an non-existing image
77-
url = '/v1/images/{0}/checksum'.format(self.gen_random_string())
78-
resp = self.http_client.put(url, headers=headers)
79-
self.assertEqual(resp.status_code, 404, resp.data)
80-
81-
image_id = self.gen_random_string()
82-
layer_data = self.gen_random_string(1024)
83-
self.upload_image(image_id,
84-
parent_id=None,
85-
layer=layer_data,
86-
set_checksum_callback=set_checksum_callback)
87-
8867
def test_bytes_range(self):
8968
image_id = self.gen_random_string()
9069
layer_data = self.gen_random_string(1024)

0 commit comments

Comments
 (0)