Differences between revisions 1 and 5 (spanning 4 versions)
Revision 1 as of 2016-03-27 03:23:41
Size: 1024
Editor: GregorySzorc
Comment: initial version of page explaining insecurity of Python <2.7.9
Revision 5 as of 2016-07-07 04:45:34
Size: 7245
Editor: GregorySzorc
Comment: line is redundant with the next section
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
Python versions before 2.7.9 (including all versions of Python 2.6) do not support modern and secure SSL. As a result, Mercurial (like any Python program running on older Python versions) cannot ensure connections to remote servers are secure. ## page was renamed from InsecureSSL
== Overview ==
Mercurial performs various checks to verify that connections to servers are secure. These checks vary depending on the Mercurial and Python version being used.
Line 3: Line 5:
Specifically, running versions of Python before 2.7.9 means: == Behavior of Various Configurations ==
Mercurial 3.9 contained a major refactor of the connection security code and user configuration. One of the changes is that Mercurial 3.9+ is more strict about connection security and will abort if a connection cannot be verified (prior versions would issue warnings and continue connecting).
||<tablewidth="&quot" tableheight="&quot" tablestyle="&quot; &amp; quot; &amp; amp; quot; &amp; amp; amp; quot; &amp; amp; amp; amp; quot; 601px&amp; amp; amp; amp; amp; quot; ; 343px&amp; amp; amp; amp; amp; quot&amp; amp; amp; amp; quot; ; &amp; amp; amp; amp; amp; quot&amp; amp; amp; amp; quot; ; &amp; amp; amp; amp; amp; quot&amp; amp; amp; amp; quot&amp; amp; amp; quot; ; &amp; amp; amp; amp; quot&amp; amp; amp; quot; ; &amp; amp; amp; amp; quot&amp; amp; amp; quot&amp; amp; quot; ; &amp; amp; amp; quot&amp; amp; quot; ; &amp; amp; amp; quot&amp; amp; quot&amp; quot; ; &amp; amp; quot&amp; quot; ; &amp; amp; quot&amp; quot&quot; ;&amp;quot&quot;;&amp;quot&quot">Behavior ||Mercurial <3.8 ||Mercurial >=3.9 ||
||Requires trusted CA certificate when connecting to new servers ||No ||Yes ||
||web.cacerts=! disables certificate validation ||Yes ||No (feature removed) ||
||[hostsecurity] config section ||No ||Yes ||
||Preferred certificate fingerprint hash algorithm ||SHA-1 ||SHA-256 ||
||Per-host CA certificates ||No ||Yes ||
||Supporting pinning multiple cert fingerprints per host ||3.8+ ||Yes ||
||smtp.verifycert config option ||Yes ||No (option removed) ||
Line 5: Line 16:
 * No TLS 1.1 or 1.2
 * No good ciphersuites
 * No perfect forward security
 * No Next Protocol Negotiation (NPN)
 * No Server Name Indication (SNI)
 * No system certificate access
Line 12: Line 17:
If you run Python older than 2.7.9, only SSLv2, SSLv3, and TLS 1.0 are available to you. SSLv2 and SSLv3 are insecure and have known vulnerabilities (like POODLE). TLS 1.0 has similar issues, but as of early 2016 it is still largely supported due to its popularity. In other words, SSL/TLS support in Python older than 2.7.9 is so poor that it is practically plain text (read: no security). ||<tablewidth="&quot" tableheight="&quot" tablestyle="&quot; &amp; quot; &amp; amp; quot; &amp; amp; amp; quot; &amp; amp; amp; amp; quot; 501px&amp; amp; amp; amp; amp; quot; ; 188px&amp; amp; amp; amp; amp; quot&amp; amp; amp; amp; quot; ; &amp; amp; amp; amp; amp; quot&amp; amp; amp; amp; quot; ; &amp; amp; amp; amp; amp; quot&amp; amp; amp; amp; quot&amp; amp; amp; quot; ; &amp; amp; amp; amp; quot&amp; amp; amp; quot; ; &amp; amp; amp; amp; quot&amp; amp; amp; quot&amp; amp; quot; ; &amp; amp; amp; quot&amp; amp; quot; ; &amp; amp; amp; quot&amp; amp; quot&amp; quot; ; &amp; amp; quot&amp; quot; ; &amp; amp; quot&amp; quot&quot; ;&amp;quot&quot;;&amp;quot&quot">Behavior ||Python <2.7.9 ||Python >=2.7.9 (or with modern ssl module) ||
||Supports TLS 1.1 and 1.2 ||No ||Yes ||
||Server Name Indication (SNI) support ||No ||Yes ||
||System trusted certificate authority access ||No ||Yes ||
||''Good'' ciphersuites available ||No ||Yes ||
Line 14: Line 23:
These limitations are things that Mercurial cannot work around. Only upgrading to Python 2.7.9+ will make Mercurial more secure.


== Common Errors and Warnings ==
=== abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect ===
This error occurs when Mercurial is unable to load CA certificates to verify the server's certificate.

The way server security works is the server has a ''certificate'' saying they are server ''X''. This certificate is ''signed'' by an entity called a ''Certificate Authority'' or CA. Before that happens, the CA is supposed to verify that they should really sign that certificate. e.g. if someone requests the signing for a certificate for ''google.com'' the CA is supposed to verify that the possessor of that certificate is Google (the company).

Clients contain lists of CAs that are trusted. When connecting to a server, they will enforce that the CA that signed that server's certificate was signed by a trusted CA.

This error message basically means Mercurial is unable to load a list of trusted CAs and therefore can't establish the trust for the server certificate's CA. Since it can't trust the certificate, it is refusing to connect.

'''This error message indicates your Mercurial installation/configuration is incomplete.''' Typically, Mercurial will load your system's CA certificates. However, it can't always do this. Reasons why it can't do this include:

 * Running Python <2.7.9 (older versions of Python don't know how to locate the system CA store)
 * Python is unable to find the system CA store (this may indicate the Python installation is mis-configured)

The mitigation for this error is to tell Mercurial where trusted CA certificates are located. This can be done by setting the ''web.cacerts'' configuration option to the path to a file containing PEM certificates. Common file locations include:

 * /etc/pki/tls/certs/ca-bundle.trust.crt (RedHat, CentOS, Fedora)
 * /etc/ssl/certs/ca-certificates.crt (Debian, Ubuntu, Gentoo)

== Expectations of Mercurial Packages ==
Proper Mercurial packages should guarantee one of the following:

 1. The underlying Python install is able to load CA certificates from configured locations
 1. The ''web.cacerts'' config option is defined to an appropriate location
 1. The ''certifi ''Python package is installed

'''If one of these isn't true, Mercurial will fail to establish secure connections to servers and/or will print a warning saying that Mercurial is improperly configured.'''

To test whether the underlying Python install is able to load CA certificates from default locations, run the following Python code:

{{{
import socket, ssl
context = ssl.create_default_context()
context.verify_mode = ssl.CERT_REQUIRED
context.load_default_certs()
conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname='www.mozilla.org')
conn.connect(('www.mozilla.org', 443))
}}}
This ''should ''work on Python 2.7.9+ or Python versions with a backported ssl module (such as RHEL7's Python 2.7.5 package). If this doesn't work on Python 2.7.9+, that Python package is arguably broken as it doesn't provide a mechanism to verify CA certificates in the standard library.

If the above code works, the Python install is able to load CA certificates (using the location configured by the OpenSSL that Python is using). Mercurial should work out of the box. You don't need to change anything unless you want to explicitly point Mercurial at a different set of trusted CA certificates. (Wanting to point Mercurial at a different set of trusted CA certificates from the Python it runs on is weird: you should consider changing the Python configuration instead.)

If the above code fails, the Mercurial install should set the ''web.cacerts'' config option in the global/system hgrc to a file containing PEM encoded certificates of trusted CAs (preferred) or ensure the [[https://pypi.python.org/pypi/certifi|certifi]] Python package is installed. Configuring ''web.cacerts'' is preferred over installing ''certifi ''because your system likely already has a file containing trusted CA certificates on it and installing ''certifi ''will create ''N+1'' CA files, which results in a more complicated system configuration and potential for things to get out of sync. You probably want a single CA certificate list.

Overview

Mercurial performs various checks to verify that connections to servers are secure. These checks vary depending on the Mercurial and Python version being used.

Behavior of Various Configurations

Mercurial 3.9 contained a major refactor of the connection security code and user configuration. One of the changes is that Mercurial 3.9+ is more strict about connection security and will abort if a connection cannot be verified (prior versions would issue warnings and continue connecting).

Behavior

Mercurial <3.8

Mercurial >=3.9

Requires trusted CA certificate when connecting to new servers

No

Yes

web.cacerts=! disables certificate validation

Yes

No (feature removed)

[hostsecurity] config section

No

Yes

Preferred certificate fingerprint hash algorithm

SHA-1

SHA-256

Per-host CA certificates

No

Yes

Supporting pinning multiple cert fingerprints per host

3.8+

Yes

smtp.verifycert config option

Yes

No (option removed)

Behavior

Python <2.7.9

Python >=2.7.9 (or with modern ssl module)

Supports TLS 1.1 and 1.2

No

Yes

Server Name Indication (SNI) support

No

Yes

System trusted certificate authority access

No

Yes

Good ciphersuites available

No

Yes

Common Errors and Warnings

1. abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect

This error occurs when Mercurial is unable to load CA certificates to verify the server's certificate.

The way server security works is the server has a certificate saying they are server X. This certificate is signed by an entity called a Certificate Authority or CA. Before that happens, the CA is supposed to verify that they should really sign that certificate. e.g. if someone requests the signing for a certificate for google.com the CA is supposed to verify that the possessor of that certificate is Google (the company).

Clients contain lists of CAs that are trusted. When connecting to a server, they will enforce that the CA that signed that server's certificate was signed by a trusted CA.

This error message basically means Mercurial is unable to load a list of trusted CAs and therefore can't establish the trust for the server certificate's CA. Since it can't trust the certificate, it is refusing to connect.

This error message indicates your Mercurial installation/configuration is incomplete. Typically, Mercurial will load your system's CA certificates. However, it can't always do this. Reasons why it can't do this include:

  • Running Python <2.7.9 (older versions of Python don't know how to locate the system CA store)

  • Python is unable to find the system CA store (this may indicate the Python installation is mis-configured)

The mitigation for this error is to tell Mercurial where trusted CA certificates are located. This can be done by setting the web.cacerts configuration option to the path to a file containing PEM certificates. Common file locations include:

  • /etc/pki/tls/certs/ca-bundle.trust.crt (RedHat, CentOS, Fedora)

  • /etc/ssl/certs/ca-certificates.crt (Debian, Ubuntu, Gentoo)

Expectations of Mercurial Packages

Proper Mercurial packages should guarantee one of the following:

  1. The underlying Python install is able to load CA certificates from configured locations
  2. The web.cacerts config option is defined to an appropriate location

  3. The certifi Python package is installed

If one of these isn't true, Mercurial will fail to establish secure connections to servers and/or will print a warning saying that Mercurial is improperly configured.

To test whether the underlying Python install is able to load CA certificates from default locations, run the following Python code:

import socket, ssl
context = ssl.create_default_context()
context.verify_mode = ssl.CERT_REQUIRED
context.load_default_certs()
conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname='www.mozilla.org')
conn.connect(('www.mozilla.org', 443))

This should work on Python 2.7.9+ or Python versions with a backported ssl module (such as RHEL7's Python 2.7.5 package). If this doesn't work on Python 2.7.9+, that Python package is arguably broken as it doesn't provide a mechanism to verify CA certificates in the standard library.

If the above code works, the Python install is able to load CA certificates (using the location configured by the OpenSSL that Python is using). Mercurial should work out of the box. You don't need to change anything unless you want to explicitly point Mercurial at a different set of trusted CA certificates. (Wanting to point Mercurial at a different set of trusted CA certificates from the Python it runs on is weird: you should consider changing the Python configuration instead.)

If the above code fails, the Mercurial install should set the web.cacerts config option in the global/system hgrc to a file containing PEM encoded certificates of trusted CAs (preferred) or ensure the certifi Python package is installed. Configuring web.cacerts is preferred over installing certifi because your system likely already has a file containing trusted CA certificates on it and installing certifi will create N+1 CA files, which results in a more complicated system configuration and potential for things to get out of sync. You probably want a single CA certificate list.

SecureConnections (last edited 2017-01-18 14:51:39 by MadsKiilerich)