diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index 9dfdb55c448892b728389655eff11c1e7144a6c5..614acdbab6bc974a8df3f87d5c3341abdbdd2fe3 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -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 diff --git a/docs/rolereference.rst b/docs/rolereference.rst index 61dcb33d7e33b8d442cda2692f792be02b311acc..087e4aa5f5ad31389d14e252de07c5f66dabc036 100644 --- a/docs/rolereference.rst +++ b/docs/rolereference.rst @@ -745,11 +745,11 @@ Parameters ` 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``. diff --git a/docs/usage.rst b/docs/usage.rst index ac2bfb467e591f0f78f741141e3550b1021d9c49..32da358eb2dd5a3450606d8b5e38c34b10e3f6ee 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -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 diff --git a/roles/ldap_server/defaults/main.yml b/roles/ldap_server/defaults/main.yml index 6833a391b26868b4e8218e8fbeb96ac46264a2dc..465ea9fb8fc35da661c87eaa22af6685ca0c58fd 100644 --- a/roles/ldap_server/defaults/main.yml +++ b/roles/ldap_server/defaults/main.yml @@ -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: [] diff --git a/roles/ldap_server/molecule/default/group_vars/parameters-mandatory.yml b/roles/ldap_server/molecule/default/group_vars/parameters-mandatory.yml index 018091ee40454e18dbf706cae65dee376c493cbd..2bfc875bdb26f3d7a7afe9cbdf943793f01bed22 100644 --- a/roles/ldap_server/molecule/default/group_vars/parameters-mandatory.yml +++ b/roles/ldap_server/molecule/default/group_vars/parameters-mandatory.yml @@ -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/