mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-27 14:57:41 +00:00
Merge pull request #161 from untitaker/verify_docs
Document certificate verification properly
This commit is contained in:
commit
4384823cd1
7 changed files with 89 additions and 23 deletions
|
|
@ -18,8 +18,9 @@ Table of Contents
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
tutorial
|
tutorial
|
||||||
config
|
ssl-tutorial
|
||||||
keyring
|
keyring
|
||||||
|
config
|
||||||
supported
|
supported
|
||||||
problems
|
problems
|
||||||
vdir
|
vdir
|
||||||
|
|
|
||||||
56
docs/ssl-tutorial.rst
Normal file
56
docs/ssl-tutorial.rst
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
.. _ssl-tutorial:
|
||||||
|
|
||||||
|
==============================
|
||||||
|
SSL and certificate validation
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Vdirsyncer uses the requests_ library for all its HTTP and SSL interaction.
|
||||||
|
|
||||||
|
All SSL configuration is done per-storage. Storages that have anything to do
|
||||||
|
with SSL have two parameters: ``verify`` and ``verify_fingerprint``.
|
||||||
|
|
||||||
|
- The ``verify`` parameter determines whether to verify SSL certificates.
|
||||||
|
|
||||||
|
1. The default, ``true``, means that certificates will be validated against a
|
||||||
|
set of trusted CAs. See :ref:`ssl-cas`.
|
||||||
|
|
||||||
|
2. The value ``false`` will disable both trusted-CA-validation and the
|
||||||
|
validation of the certificate's expiration date. Unless combined with
|
||||||
|
``verify_fingerprint``, **you should not use this value at all, because
|
||||||
|
it's a security risk**.
|
||||||
|
|
||||||
|
3. You can also set ``verify`` to a path of the server's certificate in PEM
|
||||||
|
format, instead of relying on the default root CAs::
|
||||||
|
|
||||||
|
[storage foo]
|
||||||
|
type = caldav
|
||||||
|
...
|
||||||
|
verify = "/path/to/cert.pem"
|
||||||
|
|
||||||
|
- The ``verify_fingerprint`` parameter can be used to compare the SSL
|
||||||
|
fingerprint to a fixed value. The value can be either a SHA1-fingerprint or
|
||||||
|
an MD5 one::
|
||||||
|
|
||||||
|
[storage foo]
|
||||||
|
type = caldav
|
||||||
|
...
|
||||||
|
verify_fingerprint = "94:FD:7A:CB:50:75:A4:69:82:0A:F8:23:DF:07:FC:69:3E:CD:90:CA"
|
||||||
|
|
||||||
|
Using it will effectively set ``verify=False``.
|
||||||
|
|
||||||
|
.. _ssl-cas:
|
||||||
|
|
||||||
|
Trusted CAs
|
||||||
|
-----------
|
||||||
|
|
||||||
|
As said, vdirsyncer uses the requests_ library for such parts, which, by
|
||||||
|
default, `uses its own set of trusted CAs
|
||||||
|
<http://www.python-requests.org/en/latest/user/advanced/#ca-certificates>`_.
|
||||||
|
|
||||||
|
However, the actual behavior depends on how you have installed it. Some Linux
|
||||||
|
distributions, such as Debian, patch their ``python-requests`` package to use
|
||||||
|
the system certificate CAs. Normally these two stores are similar enough for
|
||||||
|
you not to care. If the behavior on your system is somehow confusing, your best
|
||||||
|
bet is explicitly setting the SSL options above.
|
||||||
|
|
||||||
|
.. _requests: www.python-requests.org/
|
||||||
5
setup.py
5
setup.py
|
|
@ -38,10 +38,13 @@ setup(
|
||||||
'console_scripts': ['vdirsyncer = vdirsyncer.cli:main']
|
'console_scripts': ['vdirsyncer = vdirsyncer.cli:main']
|
||||||
},
|
},
|
||||||
install_requires=[
|
install_requires=[
|
||||||
|
# https://github.com/mitsuhiko/click/issues/200
|
||||||
'click>=3.1',
|
'click>=3.1',
|
||||||
'requests>=2.1',
|
# https://github.com/shazow/urllib3/pull/444
|
||||||
|
'requests>=2.4.1',
|
||||||
'lxml>=3.0',
|
'lxml>=3.0',
|
||||||
'icalendar>=3.6',
|
'icalendar>=3.6',
|
||||||
|
# https://github.com/sigmavirus24/requests-toolbelt/pull/28
|
||||||
'requests_toolbelt>=0.3.0'
|
'requests_toolbelt>=0.3.0'
|
||||||
],
|
],
|
||||||
extras_require={'keyring': ['keyring']}
|
extras_require={'keyring': ['keyring']}
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,8 @@ def test_get_class_init_args_on_storage():
|
||||||
assert not required
|
assert not required
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not utils.compat.PY2,
|
||||||
|
reason='https://github.com/shazow/urllib3/issues/529')
|
||||||
def test_request_ssl(httpsserver):
|
def test_request_ssl(httpsserver):
|
||||||
sha1 = '94:FD:7A:CB:50:75:A4:69:82:0A:F8:23:DF:07:FC:69:3E:CD:90:CA'
|
sha1 = '94:FD:7A:CB:50:75:A4:69:82:0A:F8:23:DF:07:FC:69:3E:CD:90:CA'
|
||||||
md5 = '19:90:F7:23:94:F2:EF:AB:2B:64:2D:57:3D:25:95:2D'
|
md5 = '19:90:F7:23:94:F2:EF:AB:2B:64:2D:57:3D:25:95:2D'
|
||||||
|
|
@ -218,6 +220,10 @@ def test_request_ssl(httpsserver):
|
||||||
utils.request('GET', httpsserver.url)
|
utils.request('GET', httpsserver.url)
|
||||||
assert 'certificate verify failed' in str(excinfo.value)
|
assert 'certificate verify failed' in str(excinfo.value)
|
||||||
utils.request('GET', httpsserver.url, verify=False)
|
utils.request('GET', httpsserver.url, verify=False)
|
||||||
utils.request('GET', httpsserver.url, verify=False,
|
utils.request('GET', httpsserver.url,
|
||||||
verify_fingerprint=sha1)
|
verify_fingerprint=sha1)
|
||||||
utils.request('GET', httpsserver.url, verify=False, verify_fingerprint=md5)
|
utils.request('GET', httpsserver.url, verify_fingerprint=md5)
|
||||||
|
with pytest.raises(requests.exceptions.SSLError) as excinfo:
|
||||||
|
utils.request('GET', httpsserver.url,
|
||||||
|
verify_fingerprint=''.join(reversed(sha1)))
|
||||||
|
assert 'Fingerprints did not match' in str(excinfo.value)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ from requests import session as requests_session
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
|
|
||||||
from .base import Item, Storage
|
from .base import Item, Storage
|
||||||
from .http import USERAGENT, prepare_auth, prepare_verify
|
from .http import HTTP_STORAGE_PARAMETERS, USERAGENT, prepare_auth, \
|
||||||
|
prepare_verify
|
||||||
from .. import exceptions, log, utils
|
from .. import exceptions, log, utils
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -250,18 +251,9 @@ class DavSession(object):
|
||||||
|
|
||||||
class DavStorage(Storage):
|
class DavStorage(Storage):
|
||||||
|
|
||||||
'''
|
__doc__ = '''
|
||||||
:param url: Base URL or an URL to a collection.
|
:param url: Base URL or an URL to a collection.
|
||||||
:param username: Username for authentication.
|
''' + HTTP_STORAGE_PARAMETERS + '''
|
||||||
:param password: Password for authentication.
|
|
||||||
:param verify: Verify SSL certificate, default True. This can also be a
|
|
||||||
local path to a self-signed SSL certificate.
|
|
||||||
:param verify_fingerprint: Optional. SHA1 or MD5 fingerprint of the
|
|
||||||
expected server certificate.
|
|
||||||
:param auth: Optional. Either ``basic``, ``digest`` or ``guess``. Default
|
|
||||||
``guess``. If you know yours, consider setting it explicitly for
|
|
||||||
performance.
|
|
||||||
:param useragent: Default ``vdirsyncer``.
|
|
||||||
:param unsafe_href_chars: Replace the given characters when generating
|
:param unsafe_href_chars: Replace the given characters when generating
|
||||||
hrefs. Defaults to ``'@'``.
|
hrefs. Defaults to ``'@'``.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,21 +44,28 @@ def prepare_verify(verify):
|
||||||
return verify
|
return verify
|
||||||
|
|
||||||
|
|
||||||
class HttpStorage(Storage):
|
HTTP_STORAGE_PARAMETERS = '''
|
||||||
'''
|
|
||||||
Use a simple ``.ics`` file (or similar) from the web.
|
|
||||||
|
|
||||||
:param url: URL to the ``.ics`` file.
|
|
||||||
:param username: Username for authentication.
|
:param username: Username for authentication.
|
||||||
:param password: Password for authentication.
|
:param password: Password for authentication.
|
||||||
:param verify: Verify SSL certificate, default True. This can also be a
|
:param verify: Verify SSL certificate, default True. This can also be a
|
||||||
local path to a self-signed SSL certificate.
|
local path to a self-signed SSL certificate. See :ref:`ssl-tutorial`
|
||||||
|
for more information.
|
||||||
:param verify_fingerprint: Optional. SHA1 or MD5 fingerprint of the
|
:param verify_fingerprint: Optional. SHA1 or MD5 fingerprint of the
|
||||||
expected server certificate.
|
expected server certificate. See :ref:`ssl-tutorial` for more
|
||||||
|
information.
|
||||||
:param auth: Optional. Either ``basic``, ``digest`` or ``guess``. Default
|
:param auth: Optional. Either ``basic``, ``digest`` or ``guess``. Default
|
||||||
``guess``. If you know yours, consider setting it explicitly for
|
``guess``. If you know yours, consider setting it explicitly for
|
||||||
performance.
|
performance.
|
||||||
:param useragent: Default ``vdirsyncer``.
|
:param useragent: Default ``vdirsyncer``.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class HttpStorage(Storage):
|
||||||
|
__doc__ = '''
|
||||||
|
Use a simple ``.ics`` file (or similar) from the web.
|
||||||
|
|
||||||
|
:param url: URL to the ``.ics`` file.
|
||||||
|
''' + HTTP_STORAGE_PARAMETERS + '''
|
||||||
|
|
||||||
A simple example::
|
A simple example::
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,7 @@ def request(method, url, session=None, latin1_fallback=True,
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
|
|
||||||
if verify_fingerprint is not None:
|
if verify_fingerprint is not None:
|
||||||
|
kwargs['verify'] = False
|
||||||
https_prefix = 'https://'
|
https_prefix = 'https://'
|
||||||
|
|
||||||
if not isinstance(session.adapters[https_prefix], _FingerprintAdapter):
|
if not isinstance(session.adapters[https_prefix], _FingerprintAdapter):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue