diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index 0db0a20d5990a69214bc40cd52a3390baef5b018..91081a5928276ca58a900808dbc39796f9468e3a 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -5,6 +5,15 @@ Release notes NEXT RELEASE ------------ +**Breaking changes** + +* ``ldap_server`` role + + * Use 2048-bit Diffie-Hellman parameters for relevant TLS + ciphers. This could introduce incompatibility with older + clients/servers trying to connect to the LDAP server. This change + is applicable only under Debian Buster. + **New features/improvements:** * All roles @@ -19,6 +28,12 @@ NEXT RELEASE **Bug fixes:** +* ``ldap_server`` role + + * Allow use of DHE TLS ciphers by generating the necessary + Diffie-Hellman parameters. This bug fix is applicable only under + Debian Buster. + * ``wsgi_website_`` role * When the virtual environment is created, the ``setuptools`` and diff --git a/docs/rolereference.rst b/docs/rolereference.rst index d1f2f41e06fe801b42aabe3f5e6fbe5c2e0582f3..38c16054d2ee9ad84634867b7a59c6d20f26a9b1 100644 --- a/docs/rolereference.rst +++ b/docs/rolereference.rst @@ -764,7 +764,13 @@ Parameters Under Debian Stretch, the DHE ciphers are not usable due to a bug present in OpenLDAP 2.4.44. See https://bugs.launchpad.net/ubuntu/+source/openldap/+bug/1656979 - for details. + for details. DHE ciphers are usable under Debian Buster. + + It should be also noted that under Debian Buster, slapd will not + use the DH parameters generated by the role, but will instead use + them to pick one of the recommended DH parameters from `RFC-7919 + `_. This is based on the + size of role-generated parameters. TLS ciphers to enable on the LDAP server. This should be a GnuTLS-compatible cipher specification that should also include what TLS protocol versions diff --git a/roles/ldap_server/molecule/default/prepare.yml b/roles/ldap_server/molecule/default/prepare.yml index c0314a3f4b4ac04a092581d82645d159c1606788..19a471050a10d84f3961cfafc2bf61f5a491ea5e 100644 --- a/roles/ldap_server/molecule/default/prepare.yml +++ b/roles/ldap_server/molecule/default/prepare.yml @@ -142,12 +142,10 @@ path: "/bin/ss" state: absent - - name: Install netstat utility + - name: Install tools for testing apt: - name: net-tools - state: present - - - name: Install nmap utility for testing TLS - apt: - name: nmap + name: + - net-tools + - nmap + - gnutls-bin state: present diff --git a/roles/ldap_server/molecule/default/tests/test_default.py b/roles/ldap_server/molecule/default/tests/test_default.py index 3840387c3df3b1f4455e94729d9c58dc61bee702..25bda565afaf2da80959cb302951eee9a9c0ff98 100644 --- a/roles/ldap_server/molecule/default/tests/test_default.py +++ b/roles/ldap_server/molecule/default/tests/test_default.py @@ -230,3 +230,24 @@ def test_ldap_tls_certificate_file(host): assert cert.group == 'root' assert cert.mode == 0o644 assert cert.content_string == open('tests/data/x509/server/%s_ldap.cert.pem' % inventory_hostname).read() + + +def test_ldap_server_dh_parameter_file(host): + """ + Tests if the Diffie-Hellman parameter file has been generated + correctly. + """ + + hostname = host.run('hostname').stdout.strip() + dhparam_file_path = '/etc/ssl/private/%s_ldap.dh.pem' % hostname + + with host.sudo(): + dhparam_file = host.file(dhparam_file_path) + assert dhparam_file.is_file + assert dhparam_file.user == 'root' + assert dhparam_file.group == 'openldap' + assert dhparam_file.mode == 0o640 + + dhparam_info = host.run("openssl dhparam -noout -text -in %s", dhparam_file_path) + + assert "DH Parameters: (2048 bit)" in dhparam_info.stdout diff --git a/roles/ldap_server/molecule/default/tests/test_default_buster.py b/roles/ldap_server/molecule/default/tests/test_default_buster.py new file mode 100644 index 0000000000000000000000000000000000000000..cca22e20ab0f9edf6c2ac822087a17542c4b831e --- /dev/null +++ b/roles/ldap_server/molecule/default/tests/test_default_buster.py @@ -0,0 +1,40 @@ +import os + +import testinfra.utils.ansible_runner + + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('parameters-*-buster64') + + +def test_ldap_server_uses_correct_dh_parameters(host): + """ + Tests if the LDAP server uses the generated Diffie-Hellman + parameter. + """ + + # Technically we should be testing here against deployed DH + # parameters file, however... When linked against GnuTLS, slapd + # seems to only take into account the size of pointed-to DH + # parameters, and then picks one of the parameters from the + # RFC-7919 (https://www.ietf.org/rfc/rfc7919.txt) + # instead. Therefore we list here the 2048-bit DH parameter from + # the RFC instead. + expected_dhparam = """-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== +-----END DH PARAMETERS-----""" + + connection = host.run("gnutls-cli --no-ca-verification --starttls-proto=ldap --port 389 " + "--priority 'NONE:+VERS-TLS1.2:+CTYPE-X509:+COMP-NULL:+SIGN-RSA-SHA384:+DHE-RSA:+SHA384:+AEAD:+AES-256-GCM' --verbose localhost") + + output = connection.stdout + begin_marker = "-----BEGIN DH PARAMETERS-----" + end_marker = "-----END DH PARAMETERS-----" + used_dhparam = output[output.find(begin_marker):output.find(end_marker) + len(end_marker)] + + assert used_dhparam == expected_dhparam diff --git a/roles/ldap_server/molecule/default/tests/test_mandatory.py b/roles/ldap_server/molecule/default/tests/test_mandatory.py index ad57079a60c68719fbcce6005d4dd498efe2ff74..47d0bf1ac4782fa18d5d07387570f3ccca4b9130 100644 --- a/roles/ldap_server/molecule/default/tests/test_mandatory.py +++ b/roles/ldap_server/molecule/default/tests/test_mandatory.py @@ -78,16 +78,24 @@ def test_tls_version_and_ciphers(host): # @TODO: Under Debian Stretch, the DHE ciphers are not usable due # to a bug present in OpenLDAP 2.4.44. See # https://bugs.launchpad.net/ubuntu/+source/openldap/+bug/1656979 - # for details. It should be possible to fix this problem once - # switch to buster is mad.e - expected_tls_ciphers = [ - # "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", - # "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", - # "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", - ] + # for details. + expected_tls_ciphers = { + "stretch": [ + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + ], + "buster": [ + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + ] + } + + distribution_release = host.ansible("setup")["ansible_facts"]["ansible_distribution_release"] # Run the nmap scanner against the LDAP server, and fetch the # results. @@ -110,7 +118,7 @@ def test_tls_version_and_ciphers(host): tls_ciphers = sorted(list(tls_ciphers)) assert tls_versions == expected_tls_versions - assert tls_ciphers == expected_tls_ciphers + assert tls_ciphers == expected_tls_ciphers[distribution_release] def test_ssf_configuration(host): diff --git a/roles/ldap_server/molecule/default/tests/test_optional.py b/roles/ldap_server/molecule/default/tests/test_optional.py index 374e601ac1116e3e322abd4d17a69d61e9951869..d7f32dc9464fde40c60f93ef2f0b614e6198852f 100644 --- a/roles/ldap_server/molecule/default/tests/test_optional.py +++ b/roles/ldap_server/molecule/default/tests/test_optional.py @@ -82,17 +82,32 @@ def test_tls_version_and_ciphers(host): # https://bugs.launchpad.net/ubuntu/+source/openldap/+bug/1656979 # for details. It should be possible to fix this problem once # switch to buster is mad.e - expected_tls_ciphers = [ - # "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", - # "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", - # "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - ] + expected_tls_ciphers = { + "stretch": [ + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + ], + "buster": [ + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + ] + } + + distribution_release = host.ansible("setup")["ansible_facts"]["ansible_distribution_release"] # Run the nmap scanner against the LDAP server, and fetch the # results. @@ -115,7 +130,7 @@ def test_tls_version_and_ciphers(host): tls_ciphers = sorted(list(tls_ciphers)) assert tls_versions == expected_tls_versions - assert tls_ciphers == expected_tls_ciphers + assert tls_ciphers == expected_tls_ciphers[distribution_release] def test_ssf_configuration(host): diff --git a/roles/ldap_server/tasks/main.yml b/roles/ldap_server/tasks/main.yml index 598c20ab54c90b0f92588725de7ad64c05ab5dfa..a3fcaf7a6bc511196c8a48c0eb15c6dbc5cb5495 100644 --- a/roles/ldap_server/tasks/main.yml +++ b/roles/ldap_server/tasks/main.yml @@ -91,6 +91,20 @@ command: "ldapadd -H ldapi:/// -Q -Y EXTERNAL -f /etc/ldap/schema/misc.ldif" when: not ldap_misc_schema_present.stdout +# Technically, the only thing this does is pick the size of DH +# parameters to use, with GnuTLS (against which slapd is linked +# against under Debian) picking a matching DH parameter from RFC-7919 +# (https://www.ietf.org/rfc/rfc7919.txt). +- name: Generate the LDAP server Diffie-Hellman parameter + openssl_dhparam: + owner: root + group: openldap + mode: 0640 + path: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.dh.pem" + size: 2048 + notify: + - Restart slapd + - name: Deploy LDAP TLS private key template: src: "ldap_tls_key.j2" @@ -160,6 +174,13 @@ values: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.key" state: exact +- name: Configure DH parameter + ldap_attr: + dn: cn=config + name: olcTLSDHParamFile + values: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.dh.pem" + state: exact + - name: Configure TLS cipher suites ldap_attr: dn: cn=config