Changeset - e9f77d9ac92f
[Not reviewed]
0 1 0
Branko Majic (branko) - 6 years ago 2018-07-17 21:01:56
branko@majic.rs
MAR-129: Update instructions to address a couple of issues:

- Improved formatting in a number of places.
- Fixed a bunch of typos etc.
- Improved wording where relevant.
- Moved around a couple of steps related to adding users to specific
LDAP groups - otherwise we try to add them before groups are
created.
- Fixed some deprecated syntax for including playbooks.
1 file changed with 130 insertions and 89 deletions:
0 comments (0 inline, 0 general)
docs/usage.rst
Show inline comments
 
@@ -20,12 +20,13 @@ Some roles are suited for one-off operations during installation, like the
 
``preseed`` and ``bootstrap``, while some are better suited for periodic runs
 
for maintaining the users and integrity of the system.
 

	
 
By the end of the instructions you will have the following:
 
By the end of following the instructions, you will have the following:
 

	
 
* Ansible server, used for configuring the remaining servers.
 
* Ansible server, used as controller for configuring and managing the
 
  remaining servers.
 
* Communications server, providing the LDAP, mail, and XMPP services.
 
* Web server, providing the web services.
 
* Backup server, where the backups will be stored at.
 
* Backup server, used for storing all of the backups.
 

	
 

	
 
Pre-requisites
 
@@ -192,7 +193,7 @@ First of all, let's set-up some basic directory structure and configuration:
 

	
 
     mkdir ~/mysite/retry
 

	
 
3. Create the hosts file.
 
3. Create the inventory file.
 

	
 
   :file:`~/mysite/hosts`
 

	
 
@@ -231,8 +232,8 @@ Preseed files
 
The ``preseed`` role is useful for generating Debian preseed files. Preseed
 
files can be used for automating the Debian installation process.
 

	
 
Preseed files are commonly created on the Ansible host, and then in some way
 
served to the servers using them during install.
 
Preseed files are created on the Ansible controller, and then supplied
 
to Debian installer.
 

	
 
So, let's set this up for start:
 

	
 
@@ -243,12 +244,12 @@ So, let's set this up for start:
 
   ::
 

	
 
      ---
 

	
 
      - hosts: preseed
 
        roles:
 
          - preseed
 

	
 
2. And that is about it to be able to actually use this particular role! So
 
   let's try running it::
 
2. And that's about it... Now we can generate the pressed files::
 

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

	
 
@@ -318,6 +319,13 @@ with Python's built-in HTTP server::
 
  cd ~/mysite/preseed_files/
 
  python -m SimpleHTTPServer 8000
 

	
 
Then you can point installer to the preseed file selecting the
 
``Advanced options -> Automated install`` (don't press ``ENTER`` yet),
 
then pressing ``TAB``, and appending the following at the end (just
 
fill-in the correct hostname - ``comms``, ``www``, or ``bak``)::
 

	
 
  url=http://ansible.example.com:8000/HOSTNAME.example.com.cfg
 

	
 

	
 
Bootstrapping servers for Ansible set-up
 
----------------------------------------
 
@@ -340,7 +348,10 @@ Let's bootstrap our machines now:
 

	
 
      ---
 

	
 
      - hosts: [communications, web, backup]
 
      - hosts:
 
          - communications
 
          - web
 
          - backup
 
        remote_user: root
 
        roles:
 
          - bootstrap
 
@@ -353,21 +364,21 @@ Let's bootstrap our machines now:
 
3. SSH into all machines at least once from the Ansible server in order to
 
   store the SSH fingerprints into known hosts file::
 

	
 
     ssh root@comms.example.com date
 
     ssh root@www.example.com date
 
     ssh root@comms.example.com date && \
 
     ssh root@www.example.com date && \
 
     ssh root@bak.example.com date
 

	
 
4. Now, simply run the bootstrap role against the servers::
 

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

	
 
6. At this point you won't be able to ssh into the machines with root account
 
   anymore. You will be able to ssh into the machines using the private key of
 
   the ``ansible`` user (from the Ansible server). The ``ansible`` user will
 
   also be granted ability to run the ``sudo`` commands without providing
 
   password.
 
6. At this point you won't be able to ssh into the machines with the
 
   ``root`` account anymore. You will be able to ssh into the machines
 
   using the ``ansible`` user (from the Ansible server). The
 
   ``ansible`` user will also be granted ability to run the ``sudo``
 
   commands without providing password.
 

	
 
7. After this you can finally move on to configuring what you really want -
 
7. Now you can finally move on to configuring what you really want -
 
   common configuration and services for your site.
 

	
 

	
 
@@ -386,6 +397,7 @@ Let's take care of this common configuration right away:
 
   ::
 

	
 
      ---
 

	
 
      - hosts: communications
 
        remote_user: ansible
 
        become: yes
 
@@ -398,6 +410,7 @@ Let's take care of this common configuration right away:
 
   ::
 

	
 
      ---
 

	
 
      - hosts: web
 
        remote_user: ansible
 
        become: yes
 
@@ -410,6 +423,7 @@ Let's take care of this common configuration right away:
 
   ::
 

	
 
      ---
 

	
 
      - hosts: backup
 
        remote_user: ansible
 
        become: yes
 
@@ -422,10 +436,11 @@ Let's take care of this common configuration right away:
 
   ::
 

	
 
      ---
 
      - include: preseed.yml
 
      - include: communications.yml
 
      - include: web.yml
 
      - include: backup.yml
 

	
 
      - import_playbook: preseed.yml
 
      - import_playbook: communications.yml
 
      - import_playbook: web.yml
 
      - import_playbook: backup.yml
 

	
 
5. Time to create configuration for the role. Since this role is supposed to
 
   set-up a common base, we'll set-up the variables file that applies to all
 
@@ -457,10 +472,15 @@ Let's take care of this common configuration right away:
 

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

	
 
7. After this you should be able to *ssh* from Ansible server onto the managed
 
   servers as user ``admin`` using the *SSH* private key. The ``admin`` user's
 
   password has also been set to ``admin``, and the user will be member of
 
   ``sudo`` group.
 
7. After this you should be able to *ssh* from Ansible server onto the
 
   managed servers as user ``admin`` using the *SSH* private key of
 
   the ``ansible`` user on controller machine. The ``admin`` user's
 
   password has also been set to ``admin``, and the user will be
 
   member of ``sudo`` group.
 

	
 
   .. note::
 
      Remote logins over SSH using password authentication are
 
      explicitly disabled as part of common set-up/hardening.
 

	
 

	
 
Introducing LDAP
 
@@ -476,6 +496,7 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 
   ::
 

	
 
      ---
 

	
 
      - hosts: communications
 
        remote_user: ansible
 
        become: yes
 
@@ -491,6 +512,7 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 
   ::
 

	
 
      ---
 

	
 
      - hosts: web
 
        remote_user: ansible
 
        become: yes
 
@@ -536,8 +558,9 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 
          value: demand
 

	
 
5. Ok, so this looks nice and dandy... But, let's have a bit better
 
   configuration on the communications server itself. Namely, on that one we
 
   should be able to connect to socket with LDAP clients instead over TCP port.
 
   configuration on the communications server itself. Namely, on that
 
   one we should be able to connect to the LDAP server via unix socket
 
   instead of TCP.
 

	
 
   :file:`~/mysite/group_vars/communications.yml`
 
   ::
 
@@ -559,13 +582,15 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 
          option: TLS_REQCERT
 
          value: demand
 

	
 
6. Ok, time to re-run the playbooks again... Wait a minute, something is missing
 
   here... Oh, right, 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...
 
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
 
@@ -640,7 +665,7 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 
      ca_certificates:
 
         "truststore": "{{ lookup('file', '~/mysite/tls/truststore.pem') }}"
 

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

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

	
 
@@ -655,11 +680,11 @@ and one for setting-up a local SMTP mail forwarder (for having the rest of your
 
servers relay their mails to the mail server host).
 

	
 
.. note::
 
   Should you ever need to deploy the forwarder role on a laptop or machine
 
   behind NAT, make sure to look at ``smtp_from_relay_allowed`` parameter. In
 
   case you need to connect to the SMTP relay via non-standard port (for example
 
   to work-around ISP blocks), have a look at ``smtp_relay_host_port``
 
   parameter.
 
   Should you ever need to deploy the forwarder role on a laptop or
 
   machine behind NAT, make sure to look at the
 
   ``smtp_from_relay_allowed`` parameter. In case you need to connect
 
   to the SMTP relay via non-standard port (for example to work-around
 
   ISP blocks), have a look at the ``smtp_relay_host_port`` parameter.
 

	
 
The mail server role looks-up available mail domains, users, and aliases in the
 
LDAP directory. This has already been set-up on the server
 
@@ -673,6 +698,7 @@ role.
 
    ::
 

	
 
      ---
 

	
 
      - hosts: communications
 
        remote_user: ansible
 
        become: yes
 
@@ -781,14 +807,7 @@ role.
 
              cn: postmaster@example.com
 
              rfc822MailMember: john.doe@example.com
 

	
 
5. Let's add the two users to the mail group (otherwise, the mail
 
   server will ignore them). We'll use the ``ldap_attr`` module
 
   directly to make our life a bit easier::
 

	
 
     workon mysite && ansible --become -m ldap_attr -a "dn=cn=mail,ou=groups,dc=example,dc=com state=present name=uniqueMember value=uid=johndoe,ou=people,dc=example,dc=com" communications
 
     workon mysite && ansible --become -m ldap_attr -a "dn=cn=mail,ou=groups,dc=example,dc=com state=present name=uniqueMember value=uid=janedoe,ou=people,dc=example,dc=com" communications
 

	
 
6. Once again, before we apply the configuration, we must make sure the
 
5. Once again, before we apply the configuration, we must make sure the
 
   necessary TLS private keys and certificates are available. In this particular
 
   case, we need to set-up separate key/certificate pair for both the SMTP and
 
   IMAP service:
 
@@ -826,10 +845,17 @@ role.
 
        certtool --sec-param normal --generate-privkey --outfile ~/mysite/tls/comms.example.com_imap.key
 
        certtool --generate-certificate --load-ca-privkey ~/mysite/tls/ca.key --load-ca-certificate ~/mysite/tls/ca.pem --template ~/mysite/tls/comms.example.com_imap.cfg --load-privkey ~/mysite/tls/comms.example.com_imap.key --outfile ~/mysite/tls/comms.example.com_imap.pem
 

	
 
7. Configuration and TLS keys have ben set-up, so it is time to apply the changes::
 
6. Configuration and TLS keys have ben set-up, so it is time to apply the changes::
 

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

	
 
7. Let's add the two users to the mail group (otherwise, the mail
 
   server will ignore them). We'll use the ``ldap_attr`` module
 
   directly to make our life a bit easier::
 

	
 
     workon mysite && ansible --become -m ldap_attr -a "dn=cn=mail,ou=groups,dc=example,dc=com state=present name=uniqueMember values=uid=johndoe,ou=people,dc=example,dc=com" communications
 
     workon mysite && ansible --become -m ldap_attr -a "dn=cn=mail,ou=groups,dc=example,dc=com state=present name=uniqueMember values=uid=janedoe,ou=people,dc=example,dc=com" communications
 

	
 
8. If no errors have been reported, at this point you should have two mail
 
   accounts - ``john.doe@example.com``, with password ``johndoe``, and
 
   ``jane.doe@example.com``, with password ``janedoe``. In this particular
 
@@ -851,7 +877,9 @@ role.
 
  (redirected to port 25).
 

	
 
  TLS has also been hardened on port 587 to allow only TLSv1.2 and PFS ciphers
 
  (you can override TLS versions/ciphers via role configuration).
 
  (you can override TLS versions/ciphers via role configuration). TLS
 
  configuration on port 25 has been left unchanged for maximum
 
  interoperability with other servers.
 

	
 

	
 
Setting-up mail relaying from web and backup servers
 
@@ -869,6 +897,7 @@ external addresses on those two servers goes through our anti-virus scanner.
 
   ::
 

	
 
      ---
 

	
 
      - hosts: web
 
        remote_user: ansible
 
        become: yes
 
@@ -881,6 +910,7 @@ external addresses on those two servers goes through our anti-virus scanner.
 
   ::
 

	
 
      ---
 

	
 
      - hosts: backup
 
        remote_user: ansible
 
        become: yes
 
@@ -953,6 +983,7 @@ role.
 
   ::
 

	
 
      ---
 

	
 
      - hosts: communications
 
        remote_user: ansible
 
        become: yes
 
@@ -1013,22 +1044,7 @@ role.
 
         - name: mail
 
         - name: xmpp
 

	
 
4. Ok, configuration of the role is almost complete. You may have noticed that
 
   we still haven't added any users to the new LDAP group called "xmpp". So let
 
   us correct this in similar way as we did for the mail server. Since we have
 
   the user entries already, no need to recreate them here. We will just update
 
   the group membership instead.
 

	
 
   .. warning::
 
      Same warning applies here as for mail server role for managing the
 
      user/group entries! Scroll up and re-read it if you missed it!
 

	
 
   ::
 

	
 
      workon mysite && ansible --become -m ldap_attr -a "dn=cn=xmpp,ou=groups,dc=example,dc=com state=present name=uniqueMember value=uid=johndoe,ou=people,dc=example,dc=com" communications
 
      workon mysite && ansible --become -m ldap_attr -a "dn=cn=xmpp,ou=groups,dc=example,dc=com state=present name=uniqueMember value=uid=janedoe,ou=people,dc=example,dc=com" communications
 

	
 
5. Do you know what comes next? Yes! Create some more TLS private keys
 
4. Do you know what comes next? Yes! Create some more TLS private keys
 
   and certificates, this time for our XMPP server ;)
 

	
 
   1. Create new template for ``certtool``:
 
@@ -1050,10 +1066,27 @@ role.
 
        certtool --sec-param normal --generate-privkey --outfile ~/mysite/tls/comms.example.com_xmpp.key
 
        certtool --generate-certificate --load-ca-privkey ~/mysite/tls/ca.key --load-ca-certificate ~/mysite/tls/ca.pem --template ~/mysite/tls/comms.example.com_xmpp.cfg --load-privkey ~/mysite/tls/comms.example.com_xmpp.key --outfile ~/mysite/tls/comms.example.com_xmpp.pem
 

	
 
6. Apply the changes::
 
5. Apply the changes::
 

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

	
 
6. Ok, configuration of the role is complete. You may have noticed
 
   that we still haven't added any users to the new LDAP group called
 
   "xmpp". So let us correct this in similar way as we did for the
 
   mail server. Since we have the user entries already, no need to
 
   recreate them here. We will just update the group membership
 
   instead.
 

	
 
   .. warning::
 
      Same warning applies here as for mail server role for managing the
 
      user/group entries! Scroll up and re-read it if you missed it!
 

	
 
   ::
 

	
 
      workon mysite && ansible --become -m ldap_attr -a "dn=cn=xmpp,ou=groups,dc=example,dc=com state=present name=uniqueMember values=uid=johndoe,ou=people,dc=example,dc=com" communications
 
      workon mysite && ansible --become -m ldap_attr -a "dn=cn=xmpp,ou=groups,dc=example,dc=com state=present name=uniqueMember values=uid=janedoe,ou=people,dc=example,dc=com" communications
 

	
 

	
 
7. If no errors have been reported, at this point you should have two users
 
   capable of using the XMPP service - one with username
 
   ``john.doe@example.com`` and one with username ``jane.doe@example.com``. Same
 
@@ -1120,6 +1153,7 @@ Nginx.
 
    ::
 

	
 
      ---
 

	
 
      - hosts: web
 
        remote_user: ansible
 
        become: yes
 
@@ -1191,6 +1225,7 @@ server.
 
    ::
 

	
 
      ---
 

	
 
      - hosts: web
 
        remote_user: ansible
 
        become: yes
 
@@ -1228,16 +1263,17 @@ server.
 
Deploying a PHP web application (The Bug Genie)
 
-----------------------------------------------
 

	
 
We have some basic infrastructure up and running now on our web server, so we
 
shall move on to setting-up a PHP web application on it. As mentioned before, we
 
will take *The Bug Genie* as an example.
 
We have some basic infrastructure up and running on our web server, so
 
now we can move on to setting-up a PHP web application on it. As
 
mentioned before, we will take *The Bug Genie* as an example.
 

	
 
For this we will create a local role in our site to take care of it. This role
 
will in turn utilise two roles coming from *Majic Ansible Roles* that will make
 
our life (a little) easier.
 

	
 
To make the example a bit simpler, no parameters will be introducd for this role
 
(not even the password for database, we'll hard-code everything).
 
To make the example a bit simpler, no parameters will be introduced
 
for this role (not even the password for database, we'll hard-code
 
everything).
 

	
 
Before we start, here is a couple of useful pointers regarding the
 
``php_website`` role we'll be using for the PHP part:
 
@@ -1257,18 +1293,19 @@ Before we start, here is a couple of useful pointers regarding the
 
* If you ever need to set some additional PHP FPM settings, this can easily be
 
  done via the ``additional_fpm_config`` role parameter. This particular example
 
  does not set any, though.
 
* Mails deliverd to local admin/application users are forwarded to ``root``
 
* Mails delivered to local admin/application users are forwarded to ``root``
 
  account instead (this can be configured via ``website_mail_recipients`` role
 
  parameter.
 
* If you ever find yourself mixing-up test and production websites, have a look
 
  at ``environment_indicator`` role parameter. It lets you insert small strip at
 
  bottom of each HTML page automatically.
 
* If you ever find yourself mixing-up test and production websites,
 
  have a look at ``environment_indicator`` role parameter. It lets you
 
  insert small strip with environment information at bottom of each
 
  HTML page served by the web server.
 
* Static content (non-PHP) is served directly by *Nginx*.
 
* Each web application gets distinct sub-directory under ``/var/www``, named
 
  after the FQDN. All sub-directories created under there are created with
 
  ``2750`` permissions, with ownership set to admin user, and group set to the
 
  application's group. In other words, all directories will have ``SGID`` bit
 
  set-up, allowing you to create files/directories that will have their group
 
  set, allowing you to create files/directories that will have their group
 
  automatically set to the group of the parent directory.
 
* Files are served (both by *Nginx* and *php5-fpm*) from sub-directory called
 
  ``htdocs`` (located in website directory). For example
 
@@ -1285,7 +1322,7 @@ Before we start, here is a couple of useful pointers regarding the
 
     Just keep in mind that some file-management commands, like ``mv``, do *not*
 
     respect the ``SGID`` bit. In fact, I would recommend using ``cp`` when you
 
     deploy new files to the directory instead (don't simply move them from your
 
     home).
 
     home directory).
 

	
 
1. Start-off with creating the necessary directories for the new role::
 

	
 
@@ -1487,6 +1524,7 @@ Before we start, here is a couple of useful pointers regarding the
 
   ::
 

	
 
      ---
 

	
 
      - hosts: web
 
        remote_user: ansible
 
        become: yes
 
@@ -1544,15 +1582,16 @@ on the safe side:
 
* Mails deliverd to local admin/application users are forwarded to ``root``
 
  account instead (this can be configured via ``website_mail_recipients`` role
 
  parameter.
 
* If you ever find yourself mixing-up test and production websites, have a look
 
  at ``environment_indicator`` role parameter. It lets you insert small strip at
 
  bottom of each HTML page automatically.
 
* If you ever find yourself mixing-up test and production websites,
 
  have a look at ``environment_indicator`` role parameter. It lets you
 
  insert small strip with environment information at bottom of each
 
  HTML page served by the web server.
 
* Static content is served directly by *Nginx*.
 
* Each web application gets distinct sub-directory under ``/var/www``, named
 
  after the FQDN. All sub-directories created under there are created with
 
  ``2750`` permissions, with ownership set to admin user, and group set to the
 
  application's group. In other words, all directories will have ``SGID`` bit
 
  set-up, allowing you to create files/directories that will have their group
 
  set, allowing you to create files/directories that will have their group
 
  automatically set to the group of the parent directory.
 
* Each WSGI website gets a dedicated virtual environment, stored in the
 
  sub-directory ``virtualenv`` of the website directory, for example
 
@@ -1572,7 +1611,7 @@ on the safe side:
 
     Just keep in mind that some file-management commands, like ``mv``, do *not*
 
     respect the ``SGID`` bit. In fact, I would recommend using ``cp`` when you
 
     deploy new files to the directory instead (don't simply move them from your
 
     home).
 
     home directory).
 

	
 
1. Set-up the necessary directories first::
 

	
 
@@ -1873,6 +1912,7 @@ on the safe side:
 
   ::
 

	
 
      ---
 

	
 
      - hosts: web
 
        remote_user: ansible
 
        become: yes
 
@@ -1960,6 +2000,7 @@ straight to it:
 
   ::
 

	
 
      ---
 

	
 
      - hosts: backup
 
        remote_user: ansible
 
        become: yes
 
@@ -2227,7 +2268,7 @@ accounts via aliases (easily achieveable if you use either the
 
``mail_forwarder`` or ``mail_server`` roles).
 

	
 
No packages will be upgraded automatically - ensuring you can make sure upgrades
 
occur correctly and do not cause major outage without anyone being present to
 
work correctly and do not cause major outage without anyone being present to
 
fix them.
 

	
 
Another useful package you may want to look into is ``needrestart`` - which runs
 
@@ -2242,14 +2283,14 @@ though, that this check does *not* verify the Python virtual environments
 
themselves.
 

	
 
This is primarily useful when you use `pip-tools
 
<https://github.com/jazzband/pip-tools>`_ for maintaining the requirements
 
files. In fact, I would both encourage you to utilise ``pip-tools`` for both
 
this purpose and for keeping the virtual environment in sync and up-to-date.
 
<https://github.com/jazzband/pip-tools>`_ for maintaining the
 
requirements files. In fact, I would encourage you to utilise
 
``pip-tools`` for both this purpose and for keeping the virtual
 
environment in sync and up-to-date.
 

	
 
Roles that want to take advantage of this would:
 

	
 
- Create a sub-directory under ``/etc/pip_check_requirements_upgrades/``.
 
  ``/etc/pip_check_requirements_upgrades/FQDN``.
 
- Create a sub-directory ``/etc/pip_check_requirements_upgrades/``.
 
- Deploy ``.in`` and ``.txt`` files within the sub-directory (see ``pip-tools``
 
  docs for explanation of how the ``.in`` files work).
 
- Ensure the created sub-directory and files have ownership set to
0 comments (0 inline, 0 general)