Skip to content

Commit 387db11

Browse files
committed
Re-write kwargs_from_env to handle TLS options better
This more closely matches the way the docker client interprets the relevant environment variables. Among other things, it's now possible to set DOCKER_TLS_VERIFY=false. Signed-off-by: Mike Dougherty <[email protected]>
1 parent bba8e28 commit 387db11

File tree

3 files changed

+91
-33
lines changed

3 files changed

+91
-33
lines changed

docker/tls.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
class TLSConfig(object):
88
cert = None
9+
ca_cert = None
910
verify = None
1011
ssl_version = None
1112

@@ -48,27 +49,18 @@ def __init__(self, client_cert=None, ca_cert=None, verify=None,
4849
)
4950
self.cert = (tls_cert, tls_key)
5051

51-
# Either set verify to True (public/default CA checks) or to the
52-
# path of a CA Cert file.
53-
if verify is not None:
54-
if not ca_cert:
55-
self.verify = verify
56-
elif os.path.isfile(ca_cert):
57-
if not verify:
58-
raise errors.TLSParameterError(
59-
'verify can not be False when a CA cert is'
60-
' provided.'
61-
)
62-
self.verify = ca_cert
63-
else:
64-
raise errors.TLSParameterError(
65-
'Invalid CA certificate provided for `tls_ca_cert`.'
66-
)
52+
# If verify is set, make sure the cert exists
53+
self.verify = verify
54+
self.ca_cert = ca_cert
55+
if self.verify and self.ca_cert and not os.path.isfile(self.ca_cert):
56+
raise errors.TLSParameterError(
57+
'Invalid CA certificate provided for `tls_ca_cert`.'
58+
)
6759

6860
def configure_client(self, client):
6961
client.ssl_version = self.ssl_version
70-
if self.verify is not None:
71-
client.verify = self.verify
62+
client.verify = self.verify
63+
client.ca_cert = self.ca_cert
7264
if self.cert:
7365
client.cert = self.cert
7466
client.mount('https://', ssladapter.SSLAdapter(

docker/utils/utils.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -445,26 +445,45 @@ def parse_devices(devices):
445445

446446
def kwargs_from_env(ssl_version=None, assert_hostname=None):
447447
host = os.environ.get('DOCKER_HOST')
448-
cert_path = os.environ.get('DOCKER_CERT_PATH')
448+
449+
# empty string for cert path is the same as unset.
450+
cert_path = os.environ.get('DOCKER_CERT_PATH') or None
451+
452+
# empty string for tls verify counts as "false".
453+
# Any value or 'unset' counts as true.
449454
tls_verify = os.environ.get('DOCKER_TLS_VERIFY')
455+
if tls_verify == '':
456+
tls_verify = False
457+
enable_tls = True
458+
else:
459+
tls_verify = tls_verify is not None
460+
enable_tls = cert_path or tls_verify
450461

451462
params = {}
452463

453464
if host:
454465
params['base_url'] = (host.replace('tcp://', 'https://')
455-
if tls_verify else host)
466+
if enable_tls else host)
467+
468+
if not enable_tls:
469+
return params
456470

457-
if tls_verify and not cert_path:
471+
if not cert_path:
458472
cert_path = os.path.join(os.path.expanduser('~'), '.docker')
459473

460-
if tls_verify and cert_path:
461-
params['tls'] = tls.TLSConfig(
462-
client_cert=(os.path.join(cert_path, 'cert.pem'),
463-
os.path.join(cert_path, 'key.pem')),
464-
ca_cert=os.path.join(cert_path, 'ca.pem'),
465-
verify=True,
466-
ssl_version=ssl_version,
467-
assert_hostname=assert_hostname)
474+
if not tls_verify and assert_hostname is None:
475+
# assert_hostname is a subset of TLS verification,
476+
# so if it's not set already then set it to false.
477+
assert_hostname = False
478+
479+
params['tls'] = tls.TLSConfig(
480+
client_cert=(os.path.join(cert_path, 'cert.pem'),
481+
os.path.join(cert_path, 'key.pem')),
482+
ca_cert=os.path.join(cert_path, 'ca.pem'),
483+
verify=tls_verify,
484+
ssl_version=ssl_version,
485+
assert_hostname=assert_hostname,
486+
assert_fingerprint=tls_verify)
468487

469488
return params
470489

tests/unit/utils_test.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ def tearDown(self):
165165

166166
def test_kwargs_from_env_empty(self):
167167
os.environ.update(DOCKER_HOST='',
168-
DOCKER_CERT_PATH='',
169-
DOCKER_TLS_VERIFY='')
168+
DOCKER_CERT_PATH='')
169+
os.environ.pop('DOCKER_TLS_VERIFY', None)
170170

171171
kwargs = kwargs_from_env()
172172
self.assertEqual(None, kwargs.get('base_url'))
@@ -178,10 +178,11 @@ def test_kwargs_from_env_tls(self):
178178
DOCKER_TLS_VERIFY='1')
179179
kwargs = kwargs_from_env(assert_hostname=False)
180180
self.assertEqual('https://192.168.59.103:2376', kwargs['base_url'])
181-
self.assertTrue('ca.pem' in kwargs['tls'].verify)
181+
self.assertTrue('ca.pem' in kwargs['tls'].ca_cert)
182182
self.assertTrue('cert.pem' in kwargs['tls'].cert[0])
183183
self.assertTrue('key.pem' in kwargs['tls'].cert[1])
184184
self.assertEqual(False, kwargs['tls'].assert_hostname)
185+
self.assertTrue(kwargs['tls'].verify)
185186
try:
186187
client = Client(**kwargs)
187188
self.assertEqual(kwargs['base_url'], client.base_url)
@@ -190,6 +191,51 @@ def test_kwargs_from_env_tls(self):
190191
except TypeError as e:
191192
self.fail(e)
192193

194+
def test_kwargs_from_env_tls_verify_false(self):
195+
os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376',
196+
DOCKER_CERT_PATH=TEST_CERT_DIR,
197+
DOCKER_TLS_VERIFY='')
198+
kwargs = kwargs_from_env(assert_hostname=True)
199+
self.assertEqual('https://192.168.59.103:2376', kwargs['base_url'])
200+
self.assertTrue('ca.pem' in kwargs['tls'].ca_cert)
201+
self.assertTrue('cert.pem' in kwargs['tls'].cert[0])
202+
self.assertTrue('key.pem' in kwargs['tls'].cert[1])
203+
self.assertEqual(True, kwargs['tls'].assert_hostname)
204+
self.assertEqual(False, kwargs['tls'].verify)
205+
try:
206+
client = Client(**kwargs)
207+
self.assertEqual(kwargs['base_url'], client.base_url)
208+
self.assertEqual(kwargs['tls'].ca_cert, client.ca_cert)
209+
self.assertEqual(kwargs['tls'].cert, client.cert)
210+
self.assertFalse(kwargs['tls'].verify)
211+
except TypeError as e:
212+
self.fail(e)
213+
214+
def test_kwargs_from_env_tls_verify_false_no_cert(self):
215+
temp_dir = tempfile.mkdtemp()
216+
cert_dir = os.path.join(temp_dir, '.docker')
217+
shutil.copytree(TEST_CERT_DIR, cert_dir)
218+
219+
os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376',
220+
HOME=temp_dir,
221+
DOCKER_TLS_VERIFY='')
222+
os.environ.pop('DOCKER_CERT_PATH', None)
223+
kwargs = kwargs_from_env(assert_hostname=True)
224+
self.assertEqual('https://192.168.59.103:2376', kwargs['base_url'])
225+
self.assertTrue('ca.pem' in kwargs['tls'].ca_cert)
226+
self.assertTrue('cert.pem' in kwargs['tls'].cert[0])
227+
self.assertTrue('key.pem' in kwargs['tls'].cert[1])
228+
self.assertEqual(True, kwargs['tls'].assert_hostname)
229+
self.assertEqual(False, kwargs['tls'].verify)
230+
try:
231+
client = Client(**kwargs)
232+
self.assertEqual(kwargs['base_url'], client.base_url)
233+
self.assertEqual(kwargs['tls'].ca_cert, client.ca_cert)
234+
self.assertEqual(kwargs['tls'].cert, client.cert)
235+
self.assertFalse(kwargs['tls'].verify)
236+
except TypeError as e:
237+
self.fail(e)
238+
193239
def test_kwargs_from_env_no_cert_path(self):
194240
try:
195241
temp_dir = tempfile.mkdtemp()
@@ -201,7 +247,8 @@ def test_kwargs_from_env_no_cert_path(self):
201247
DOCKER_TLS_VERIFY='1')
202248

203249
kwargs = kwargs_from_env()
204-
self.assertIn(cert_dir, kwargs['tls'].verify)
250+
self.assertTrue(kwargs['tls'].verify)
251+
self.assertIn(cert_dir, kwargs['tls'].ca_cert)
205252
self.assertIn(cert_dir, kwargs['tls'].cert[0])
206253
self.assertIn(cert_dir, kwargs['tls'].cert[1])
207254
finally:

0 commit comments

Comments
 (0)