Changeset - e3eaa053564d
[Not reviewed]
0 5 0
Branko Majic (branko) - 4 years ago 2020-07-26 16:59:47
branko@majic.rs
MAR-162: Make the ldap_server_tls_certificate ldap_server_tls_key parameters mandatory:

- Updated release notes.
- Updated role reference documentation.
- Updated usage instructions to cover set-up of CA hierarchy earlier on
in the process.
5 files changed with 112 insertions and 84 deletions:
0 comments (0 inline, 0 general)
docs/releasenotes.rst
Show inline comments
 
@@ -19,6 +19,7 @@ Breaking changes:
 
* All roles
 

	
 
  * Support for Debian 8 Jessie has been dropped.
 
  * TLS private key and certificate parameters are now mandatory.
 

	
 
* ``mail_forwarder`` role
 

	
docs/rolereference.rst
Show inline comments
 
@@ -745,11 +745,11 @@ Parameters
 
  <http://www.openldap.org/doc/admin24/slapdconf2.html#cn=config>` for value
 
  description and syntax.
 

	
 
**ldap_server_tls_certificate** (string, optional, ``{{ lookup('file', tls_certificate_dir + '/' ansible_fqdn + '_ldap.pem') }}``)
 
**ldap_server_tls_certificate** (string, mandatory)
 
  X.509 certificate used for TLS for LDAP service. The file will be stored in
 
  directory ``/etc/ssl/certs/`` under name ``{{ ansible_fqdn }}_ldap.pem``.
 

	
 
**ldap_server_tls_key** (string, optional, ``{{ lookup('file', tls_private_key_dir + '/' ansible_fqdn + '_ldap.key') }}``)
 
**ldap_server_tls_key** (string, mandatory)
 
  Private key used for TLS for LDAP service. The file will be stored in
 
  directory ``/etc/ssl/private/`` under name ``{{ ansible_fqdn }}_ldap.key``.
 

	
docs/usage.rst
Show inline comments
 
@@ -225,21 +225,96 @@ First of all, let's set-up some basic directory structure and configuration:
 
     [backup]
 
     bak.example.com
 

	
 
4. Create a number of directories for storing playbooks, group variables, SSH
 
   keys, and GnuPG keyring (we'll get to this later)::
 
4. Create a number of directories for storing playbooks, group
 
   variables, SSH keys, X.509 artefacts (for TLS), and GnuPG keyring
 
   (we'll get to this later)::
 

	
 
     mkdir ~/mysite/playbooks/
 
     mkdir ~/mysite/group_vars/
 
     mkdir ~/mysite/ssh/
 
     mkdir ~/mysite/gnupg/
 

	
 
5. Before moving ahead, we should also create SSH private/public key pair that
 
   will be used by Ansible for connecting to destination servers, as well as
 
   for some roles::
 
5. Create SSH private/public key pair that will be used by Ansible for
 
   connecting to destination servers, as well as for some roles::
 

	
 
     ssh-keygen -f ~/.ssh/id_rsa -N ''
 

	
 

	
 
Protecting communications using TLS
 
-----------------------------------
 

	
 
In order to protect the communications between users and servers, as
 
well as between servers themselves, it is important to set-up and
 
properly configure TLS for each role.
 

	
 
*Majic Ansible Roles* mandates use of TLS wherever possible. In other
 
words, *you must* have TLS private keys and certificates issued by
 
some CA for all servers in order to be able to use most of the
 
roles. The private keys and certificates are primarily meant to be
 
generated *per service*, and that is the approach we will pursue here
 
as well.
 

	
 
TLS private keys should be ideally generated locally and kept in a
 
safe environment (possibly encrypted until needed), while the X.509
 
certificates should be issued by a relevant certification
 
authority. You can choose to roll-out your own CA, use one of the
 
public CAs, or perhaps go for a mix of both.
 

	
 
For the purpose of this guide, we'll set-up a small simple local CA to
 
issue all the necessary certificates, and we'll generate the private
 
keys and issue server certificates on the go as needed.
 

	
 
So, let us make a slight detour to create a CA of our own:
 

	
 
1. First off, install a couple more tools on the Ansible server. We
 
   will be using ``certtool`` for our improvised CA needs (run this as
 
   ``root``)::
 

	
 
     apt-get install -y gnutls-bin
 

	
 
2. Create directory where the private keys and certificates will be
 
   stored at (you can switch back to the ``ansible`` user now)::
 

	
 
     mkdir ~/mysite/tls/
 

	
 
3. Create a template for the ``certtool`` so it would know what
 
   extensions and content to have in the CA certificate:
 

	
 
   :file:`~/mysite/tls/ca.cfg`
 
   ::
 

	
 
      organization = "Example Inc."
 
      country = "SE"
 
      cn = "Example Inc. Test Site CA"
 
      expiration_days = 1825
 
      ca
 
      cert_signing_key
 
      crl_signing_key
 

	
 
4. Almost there... Now let us generate the CA private key and
 
   self-signed certificate::
 

	
 
     certtool --sec-param high --generate-privkey --outfile ~/mysite/tls/ca.key
 
     certtool --template ~/mysite/tls/ca.cfg --generate-self-signed --load-privkey ~/mysite/tls/ca.key --outfile ~/mysite/tls/ca.pem
 

	
 
5. And just one more small tweak - we need to provide a truststore PEM
 
   file containing all CA certificates in the chain for services to be
 
   able to connect to each-other (where necessary). In this particular
 
   case we have a super-simple hierarchy (root CA is also issuing the
 
   end entity certificates), so simply make a copy of the ``ca.pem``::
 

	
 
     cp ~/mysite/tls/ca.pem ~/mysite/tls/truststore.pem
 

	
 
.. note::
 
   A useful feature that all roles implement is a check to see if
 
   certificates will expire within the next 30 days. This check is
 
   performed via cronjob at midnight, and failing results will end-up
 
   being delivered to the ``root`` user on local server. Later on,
 
   once you have configured the mail server, you should be able to
 
   set-up the necessary aliases to have the mails delivered to
 
   non-local accounts too.
 

	
 

	
 
Preseed files
 
-------------
 

	
 
@@ -478,11 +553,22 @@ Let's take care of this common configuration right away:
 
      common_packages:
 
        - emacs25-nox
 

	
 
      ca_certificates:
 
        truststore: "{{ lookup('file', '~/mysite/tls/truststore.pem') }}"
 

	
 
   .. note::
 
      The ``common`` role comes with ability to set-up time
 
      synchronisation using NTP. This is not done by default. For
 
      details see the role parameter ``ntp_servers``.
 

	
 
   .. note::
 
      The ``ca_certificates`` parameter lets us deploy custom CA
 
      certificates on servers. The name we pick (in this case
 
      ``truststore``) can be set to anything. In this particular case,
 
      we want to deploy our own CA certificate for use as truststore,
 
      since this is what the services will use to validate server
 
      certificates when connecting to each-other.
 

	
 
6. That's all for configuration, time to apply the changes::
 

	
 
     workon mysite && ansible-playbook playbooks/site.yml
 
@@ -546,12 +632,16 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 

	
 
      ldap_admin_password: admin
 
      ldap_server_organization: "Example Inc."
 
      ldap_server_tls_certificate: "{{ lookup('file', 'tls/comms.example.com_ldap.pem') }}"
 
      ldap_server_tls_key: "{{ lookup('file', 'tls/comms.example.com_ldap.key') }}"
 

	
 
4. Phew. That was... Well, actually, easy :) Technically, only the LDAP admin
 
   password *must* be set, but it's nice to have better organisation specified
 
   than the default one. Let's add the LDAP client configuration next. We will
 
   start off with global LDAP client configuration. In case of LDAP client,
 
   we've got to be a bit more explicit.
 
4. Phew. That was... Well, actually, easy :) Technically, only the
 
   LDAP admin password and TLS certificate/key *must* be set, but it
 
   is nice to have organisation explicitly specified as well (instead
 
   of using whatever Debian picks as default). Let us add the LDAP
 
   client configuration next. We will start off with global LDAP
 
   client configuration. In case of the LDAP client role, we have got
 
   to be a bit more explicit.
 

	
 
   :file:`~/mysite/group_vars/all.yml`
 
   ::
 
@@ -598,48 +688,11 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 
          value: demand
 

	
 
6. Ok, time to re-run the playbooks again... Wait a minute, something
 
   is missing here... Oh, right, I forgot to mention one thing - Majic
 
   Ansible Roles use TLS throughout wherever possible. In other words,
 
   you *must* have TLS private keys and certificates issued by some CA
 
   for all servers in order to be able to use most of the roles
 
   (actually, you need them issued per *service*). This includes
 
   ``ldap_server`` too. So, let's make a slight detour to create a CA
 
   of our own, plus the necessary server certificate for the LDAP
 
   service...
 

	
 
   .. note::
 
      Another useful feature the roles implement is a check to see if
 
      certificates will expire within the next 30 days. This check is
 
      performed via cronjob at midnight, and failing results will
 
      end-up being delivered to the ``root`` user on local
 
      server. Later on, once you have configured the mail server, you
 
      should be able to set-up the necessary aliases to have the mails
 
      delivered to non-local accounts too.
 
   is missing here... Ah, right, we have to generate the TLS private
 
   key and issue the X.509 certificate.
 

	
 
   1. Let's first install a couple of more tools on the Ansible server, since we
 
      will be using ``certtool`` for our improvised CA needs (run this as
 
      ``root``)::
 

	
 
        apt-get install -y gnutls-bin
 

	
 
   2. Create directory where the private keys and certificates will be stored
 
      at (you can switch back to the ``ansible`` user now)::
 

	
 
        mkdir ~/mysite/tls/
 

	
 
   3. It is time to create a couple of templates for the ``certtool`` so it
 
      would know what extensions and content to have in the certificates:
 

	
 
      :file:`~/mysite/tls/ca.cfg`
 
      ::
 

	
 
         organization = "Example Inc."
 
         country = "SE"
 
         cn = "Example Inc. Test Site CA"
 
         expiration_days = 1825
 
         ca
 
         cert_signing_key
 
         crl_signing_key
 
   1. Create template for the ``certtool`` so it would know what
 
      extensions and content to have in the certificate:
 

	
 
      :file:`~/mysite/tls/comms.example.com_ldap.cfg`
 
      ::
 
@@ -653,35 +706,12 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 
         signing_key
 
         encryption_key
 

	
 
   4. Almost there... Now let's generate the keys and certificates::
 
   2. Almost there... Now let us generate the key and issue the certificate::
 

	
 
        certtool --sec-param high --generate-privkey --outfile ~/mysite/tls/ca.key
 
        certtool --template ~/mysite/tls/ca.cfg --generate-self-signed --load-privkey ~/mysite/tls/ca.key --outfile ~/mysite/tls/ca.pem
 
        certtool --sec-param normal --generate-privkey --outfile ~/mysite/tls/comms.example.com_ldap.key
 
        certtool --generate-certificate --load-ca-privkey ~/mysite/tls/ca.key --load-ca-certificate ~/mysite/tls/ca.pem --template ~/mysite/tls/comms.example.com_ldap.cfg --load-privkey ~/mysite/tls/comms.example.com_ldap.key --outfile ~/mysite/tls/comms.example.com_ldap.pem
 

	
 
   5. And just one more small tweak - we need to provide a truststore PEM file
 
      containing all CA certificates in the chain. In this particular case we
 
      have a super-simple hierarchy (root CA is also issuing the end entity
 
      certificates), so simply make a copy of the ``ca.pem``. The
 
      ``truststore.pem`` file is picked-up automatically by many other roles::
 

	
 
        cp ~/mysite/tls/ca.pem ~/mysite/tls/truststore.pem
 

	
 
7. With private keys and certificates in place, we are almost ready to re-run
 
   the playbooks! Now, just a *small* tweak to the general configuration to set
 
   where the TLS private keys and certificates can be found at, and all should
 
   be fine.
 

	
 
   :file:`~/mysite/group_vars/all.yml`
 
   ::
 

	
 
      tls_private_key_dir: "~/mysite/tls/"
 
      tls_certificate_dir: "~/mysite/tls/"
 
      ca_certificates:
 
        truststore: "{{ lookup('file', '~/mysite/tls/truststore.pem') }}"
 

	
 
8. And now, for the finishing touch, just run the playbooks again::
 
7. And now, for the finishing touch, just run the playbooks again::
 

	
 
     workon mysite && ansible-playbook playbooks/site.yml
 

	
roles/ldap_server/defaults/main.yml
Show inline comments
 
@@ -7,8 +7,6 @@ ldap_server_domain: "{{ ansible_domain }}"
 
ldap_server_int_basedn: "{{ ldap_server_domain | regex_replace('\\.', ',dc=') | regex_replace('^', 'dc=') }}"
 
ldap_server_organization: "Private"
 
ldap_server_log_level: 256
 
ldap_server_tls_certificate: "{{ lookup('file', tls_certificate_dir + '/' + ansible_fqdn + '_ldap.pem') }}"
 
ldap_server_tls_key: "{{ lookup('file', tls_private_key_dir + '/' + ansible_fqdn + '_ldap.key') }}"
 
ldap_server_ssf: 128
 
ldap_server_consumers: []
 
ldap_server_groups: []
roles/ldap_server/molecule/default/group_vars/parameters-mandatory.yml
Show inline comments
 
@@ -2,6 +2,9 @@
 

	
 
ldap_admin_password: adminpassword
 

	
 
ldap_server_tls_certificate: "{{ lookup('file', 'tests/data/x509/parameters-mandatory-stretch64.local_ldap.pem') }}"
 
ldap_server_tls_key: "{{ lookup('file', 'tests/data/x509/parameters-mandatory-stretch64.local_ldap.key') }}"
 

	
 
# ldap_client
 
ldap_client_config:
 
  - comment: CA truststore
 
@@ -10,7 +13,3 @@ ldap_client_config:
 
  - comment: Ensure TLS is enforced
 
    option: TLS_REQCERT
 
    value: demand
 

	
 
# common vars (not the role, global common)
 
tls_private_key_dir: tests/data/x509/
 
tls_certificate_dir: tests/data/x509/
0 comments (0 inline, 0 general)