Skip to content

Commit 7ce73de

Browse files
committed
Expanding on @denibertovic initial additions, we now have full support for SSL in docker-py. Including the ability to specify the expected SSL Version for issues with OpenSSL sslv3/tls1 recognition issues. Added an exception class for repetitive reminders to look at the CLI doc on docker.io.
1 parent 69cd38a commit 7ce73de

File tree

6 files changed

+80
-10
lines changed

6 files changed

+80
-10
lines changed

docker/client.py

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
import re
1717
import shlex
1818
import struct
19+
import os
1920

2021
import requests
2122
import requests.exceptions
2223
import six
2324

2425
from .auth import auth
2526
from .unixconn import unixconn
27+
from .ssladapter import ssladapter
2628
from .utils import utils
29+
from .exceptions import exceptions
2730

2831
if not six.PY3:
2932
import websocket
@@ -75,12 +78,14 @@ def __init__(self,
7578
timeout=DEFAULT_TIMEOUT_SECONDS,
7679
tls=False,
7780
tls_cert=None,
78-
tls_key=None):
81+
tls_key=None,
82+
tls_verify=False,
83+
tls_ca_cert=None,
84+
ssl_version=None):
7985
super(Client, self).__init__()
80-
if tls and not (tls_cert and tls_key):
81-
raise RuntimeError('tls_key and tls_cert are required.')
82-
if tls and not base_url.startswith('https'):
83-
raise RuntimeError('TLS: base_url has to start with https://')
86+
87+
if (tls or tls_verify) and not base_url.startswith('https://'):
88+
raise exceptions.TLSParameterError('If using TLS, the base_url argument must begin with "https://".')
8489
if base_url is None:
8590
base_url = "http+unix://var/run/docker.sock"
8691
if 'unix:///' in base_url:
@@ -96,10 +101,44 @@ def __init__(self,
96101
self._timeout = timeout
97102
self._auth_configs = auth.load_config()
98103

99-
if tls:
100-
self.cert = (tls_cert, tls_key)
101-
self.verify = False # We assume the server.crt will we self signed
102-
self.mount('https://', requests.adapters.HTTPAdapter())
104+
""" Argument compatibility/mapping with http://docs.docker.io/examples/https/
105+
106+
This diverges from the Docker CLI in that users can specify 'tls' here, but also
107+
disable any public/default CA pool verification by leaving tls_verify=False
108+
"""
109+
""" urllib3 sets a default ssl_version if ssl_version is None
110+
https://github.com/shazow/urllib3/blob/62ecd1523ec383802cb13b09bd7084d2da997420/urllib3/util/ssl_.py#L83
111+
"""
112+
self.ssl_version = ssl_version
113+
114+
""" "tls" and "tls_verify" must have both or neither cert/key files
115+
In either case, Alert the user when both are expected, but any are missing."""
116+
if (tls or tls_verify) and (tls_cert or tls_key):
117+
if not (tls_cert and tls_key) or (not os.path.isfile(tls_cert) or not os.path.isfile(tls_key)):
118+
raise exceptions.TLSParameterError(
119+
'You must provide either both "tls_cert"/"tls_key" files, or neither, in order to use TLS.')
120+
else:
121+
self.cert = (tls_cert, tls_key)
122+
123+
"""
124+
Either set tls_verify to True (public/default CA checks) or to the path of a CA Cert file.
125+
ref: https://github.com/kennethreitz/requests/blob/739d153ef77765392fa109bebead4260c05f3193/requests/adapters.py#L135-L137
126+
ref: https://github.com/kennethreitz/requests/blob/master/requests/sessions.py#L433-L439
127+
"""
128+
if tls_verify:
129+
if not tls_ca_cert:
130+
self.verify = True
131+
elif os.path.isfile(tls_ca_cert):
132+
self.verify = tls_ca_cert
133+
else:
134+
raise exceptions.TLSParameterError(
135+
'If "tls_verify" is set, then "tls_ca_cert" must be blank (to check default/public CA list) OR a path to a CA Cert File.')
136+
else:
137+
self.verify = False
138+
139+
""" Use SSLAdapter for the ability to specify SSL version """
140+
if tls or tls_verify:
141+
self.mount('https://', ssladapter.SSLAdapter(self.ssl_version))
103142
else:
104143
self.mount('http+unix://', unixconn.UnixAdapter(base_url, timeout))
105144

docker/exceptions/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .exceptions import TLSParameterError

docker/exceptions/exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class TLSParameterError(ValueError):
2+
def __init__(self, msg):
3+
self.msg = msg
4+
5+
def __str__(self):
6+
return self.msg + "\n\nTLS configurations should map the Docker CLI client configurations. See http://docs.docker.io/examples/https/ for API details."

docker/ssladapter/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .ssladapter import SSLAdapter

docker/ssladapter/ssladapter.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
""" Resolves OpenSSL issues in some servers:
2+
https://lukasa.co.uk/2013/01/Choosing_SSL_Version_In_Requests/
3+
https://github.com/kennethreitz/requests/pull/799
4+
"""
5+
from requests.adapters import HTTPAdapter
6+
try:
7+
from requests.packages.urllib3.poolmanager import PoolManager
8+
except ImportError:
9+
from urllib3.poolmanager import PoolManager
10+
11+
12+
class SSLAdapter(HTTPAdapter):
13+
'''An HTTPS Transport Adapter that uses an arbitrary SSL version.'''
14+
def __init__(self, ssl_version=None, **kwargs):
15+
self.ssl_version = ssl_version
16+
17+
super(SSLAdapter, self).__init__(**kwargs)
18+
19+
def init_poolmanager(self, connections, maxsize, block=False):
20+
self.poolmanager = PoolManager(num_pools=connections,
21+
maxsize=maxsize,
22+
block=block,
23+
ssl_version=self.ssl_version)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
name="docker-py",
2020
version='0.3.0',
2121
description="Python client for Docker.",
22-
packages=['docker', 'docker.auth', 'docker.unixconn', 'docker.utils'],
22+
packages=['docker', 'docker.auth', 'docker.unixconn', 'docker.utils', 'docker.ssladapter', 'docker.exceptions'],
2323
install_requires=requirements + test_requirements,
2424
zip_safe=False,
2525
test_suite='tests',

0 commit comments

Comments
 (0)