Changeset - aa76fae1cf25
[Not reviewed]
0 1 0
Branko Majic (branko) - 8 years ago 2016-01-16 23:30:21
branko@majic.rs
MAR-44: Added usage instructions for the backup roles. Also fixed a couple of errors in existing usage instructions, and included some changes related to addition of a backup server.
1 file changed with 335 insertions and 42 deletions:
0 comments (0 inline, 0 general)
docs/usage.rst
Show inline comments
 
@@ -25,6 +25,7 @@ By the end of the instructions you will have the following:
 
* Ansible server, used for configuring 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.
 

	
 

	
 
Pre-requisites
 
@@ -127,7 +128,8 @@ packages, and to prepare the environment a bit on the Ansible server:
 

	
 
     mkdir ~/mysite/
 
     mkvirtualenv -a ~/mysite/ mysite
 
     pip install ansible
 
     pip install -U pip
 
     pip install 'ansible~=1.9'
 

	
 

	
 
Cloning the *Majic Ansible Roles*
 
@@ -156,7 +158,9 @@ First of all, let's set-up some basic directory structure and configuration:
 

	
 
1. Create Ansible configuration file.
 

	
 
   :file:`~/mysite/ansible.cfg`::
 
   :file:`~/mysite/ansible.cfg`
 

	
 
   ::
 

	
 
     [defaults]
 

	
 
@@ -172,7 +176,9 @@ First of all, let's set-up some basic directory structure and configuration:
 

	
 
3. Create the hosts file.
 

	
 
   :file:`~/mysite/hosts`::
 
   :file:`~/mysite/hosts`
 

	
 
   ::
 

	
 
     [preseed]
 
     localhost ansible_connection=local
 
@@ -186,16 +192,15 @@ First of all, let's set-up some basic directory structure and configuration:
 
     [backup]
 
     bak.example.com
 

	
 
4. Create directory where playbooks files will be stored at (the top-level
 
   ones)::
 
4. Create a number of directories for storing playbooks, group variables, SSH
 
   keys, and GnuPG keyring (we'll get to this later)::
 

	
 
     mkdir ~/mysite/playbooks/
 

	
 
5. Create directory where variables will be stored at::
 

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

	
 
6. Before moving ahead, we should also create SSH private/public key pair that
 
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::
 

	
 
@@ -215,7 +220,9 @@ So, let's set this up for start:
 

	
 
1. First of all, create the playbook for generating the preseed files locally.
 

	
 
   :file:`~/mysite/playbooks/preseed.yml`::
 
   :file:`~/mysite/playbooks/preseed.yml`
 

	
 
   ::
 

	
 
      ---
 
      - hosts: preseed
 
@@ -239,7 +246,9 @@ So, let's set this up for start:
 
   the network configuration changes - this is the main thing that will probably
 
   differ in your environment. Create a new configuration file:
 

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

	
 
   ::
 

	
 
      ---
 

	
 
@@ -300,18 +309,20 @@ done for managed servers. This mainly involves set-up of Ansible users on the
 
destination machine, and distributing the SSH public keys for authorisation.
 

	
 
When you use the preseed configuration files to deploy a server, you get the
 
benefit of having the authorized_keys set-up for the root operating system,
 
benefit of having the authorized_keys set-up for the root operating system user,
 
making it easier to bootstrap the machines subsequently via Ansible.
 

	
 
Let's bootstrap our machines now:
 

	
 
1. For start, create a dedicated playbook for the bootstrap process.
 

	
 
   :file:`~/mysite/playbooks/bootstrap.yml`::
 
   :file:`~/mysite/playbooks/bootstrap.yml`
 

	
 
   ::
 

	
 
      ---
 

	
 
      - hosts: [communications, web]
 
      - hosts: [communications, web, backup]
 
        remote_user: root
 
        roles:
 
          - bootstrap
 
@@ -689,8 +700,8 @@ role.
 
      other means than the ``ldap_entries`` option. The reason for this is
 
      because this type of data in LDAP directory can be considered more of an
 
      operational/application data than configuration data that frequently
 
      changes (especially the user passwords/info). Keep in mind that you shold
 
      also be backing-up your LDAP directory on regular basis ;)
 
      changes (especially the user passwords/info). Backups of LDAP directory on
 
      regular basis are important. We will get to that at a later point.
 

	
 
   :file:`~/mysite/group_vars/communications.yml`
 
   ::
 
@@ -802,15 +813,16 @@ role.
 
  your choice.
 

	
 

	
 
Setting-up mail relaying from web server
 
------------------------------------------
 
Setting-up mail relaying from web and backup servers
 
----------------------------------------------------
 

	
 
With the mail server set-up, the next thing to do would be to set-up the SMTP
 
server on web server to relay mails via the communications server. This way we
 
can make sure that mail that gets sent via local SMTP to external addresses on
 
the web server goes through our anti-virus scanner.
 
server on web and backup servers to relay mails via the communications
 
server. This way we can make sure that mail that gets sent via local SMTP to
 
external addresses on those two servers goes through our anti-virus scanner.
 

	
 
1. Update the list of roles for web server to include the mail forwarder role.
 
1. Update the list of roles for web and backup server to include the mail
 
   forwarder role.
 

	
 
   :file:`~/mysite/playbooks/web.yml`
 
   ::
 
@@ -821,16 +833,25 @@ the web server goes through our anti-virus scanner.
 
        sudo: yes
 
        roles:
 
          - common
 
          - ldap_client
 
          - mail_forwarder
 

	
 
2. The next thing is to set-up the configuration for the new role. Web server
 
   configuration has not been touched before, so this will be a new
 
   configuration file.
 

	
 
   :file:`~/mysite/group_vars/web.yml`
 
   :file:`~/mysite/playbooks/backup.yml`
 
   ::
 

	
 
      ---
 
      - hosts: backup
 
        remote_user: ansible
 
        sudo: yes
 
        roles:
 
          - common
 
          - mail_forwarder
 

	
 
2. The next thing is to set-up the configuration for the new role. We can define
 
   this globally for all servers
 

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

	
 
      # First, let's make sure any mails directed to localhost root account get
 
      # forwarded to one of our mail users as well.
 
@@ -843,8 +864,8 @@ the web server goes through our anti-virus scanner.
 
      # directly.
 
      smtp_relay_host: comms.example.com
 

	
 
3. Although we have told our web server to use the communications server as
 
   relay for non-local mail, the communications server is not aware of
 
3. Although we have told our web and backup servers to use the communications
 
   server as relay for non-local mail, the communications server is not aware of
 
   this. This would result in the communications server refusing all relay
 
   attempts (if not, it would be an open relay, which is bad).
 

	
 
@@ -854,20 +875,21 @@ the web server goes through our anti-virus scanner.
 
   :file:`~/mysite/group_vars/communications.yml`
 
   ::
 

	
 
      # We want to allow relaying of mails from our web server here. Beware the
 
      # IP spoofing, though! Don't forget to change the bellow IP for your
 
      # server ;)
 
      # We want to allow relaying of mails from our web and backup servers
 
      # here.Beware the IP spoofing, though! Don't forget to change the bellow
 
      # IP for your server ;)
 
      smtp_allow_relay_from:
 
         - 10.32.64.20
 
         - 10.32.64.23
 

	
 
4. Let's apply the changes::
 

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

	
 
5. After this you may want to test out sending mail via web server's local SMTP
 
   to the root user (to see if the aliasing works), and to some external mail
 
   address to check if forwarding works correctly too. Run some something
 
   similar to the following on your web server::
 
5. After this you may want to test out sending mail via web or backup server's
 
   local SMTP to the root user (to see if the aliasing works), and to some
 
   external mail address to check if forwarding works correctly too. Run some
 
   something similar to the following on your web server::
 

	
 
     swaks --to root@localhost --server localhost
 
     swaks --to YOUR_MAIL --server localhost
 
@@ -1018,11 +1040,9 @@ Taking a step back - preparing for web server
 

	
 
Up until now the usage instructions have dealt almost exclusively with the
 
communications server. That is, we haven't done anything beyond the basic set-up
 
of the web server.
 
of the other servers.
 

	
 
Let us first define what we want to deploy on the web server. Since the goal is
 
to demonstrate the full spectrum of *Majic Ansible Roles*, we will cover the
 
remaining unused roles for this purpose. So, here is the plan:
 
Let us first define what we want to deploy on the web server. Here is the plan:
 

	
 
1. First off, we will set-up the web server. This will be necessary no matter
 
   what web application we decide to deploy later on.
 
@@ -1077,11 +1097,14 @@ Nginx.
 

	
 
2. You know the drill, role configuration comes up next. Actually... The web
 
   server role parameters are all optional, and they default to some ok-ish
 
   values. But let us spicen up things a bit nevertheless.
 
   values. But let us spicen up things a bit nevertheless. No configuration has
 
   been deployed before for the web server, so we will be creating a new file.
 

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

	
 
      ---
 

	
 
      web_default_title: "Welcome to default page!"
 
      web_default_message: "Nothing to see here, move along..."
 

	
 
@@ -1640,7 +1663,6 @@ on the safe side:
 
          'wiki.plugins.notifications',
 
          'wiki.plugins.images',
 
          'wiki.plugins.macros',
 
          'south',
 
      )
 

	
 
      MIDDLEWARE_CLASSES = (
 
@@ -1731,7 +1753,9 @@ on the safe side:
 
      #!/usr/bin/env python
 

	
 
      import os
 
      from django import setup
 
      os.environ['DJANGO_SETTINGS_MODULE']='wiki_example_com.settings'
 
      setup()
 
      from django.conf import settings
 
      from django.contrib.auth.models import User
 

	
 
@@ -1778,6 +1802,275 @@ on the safe side:
 
   into *Django Wiki* with username ``admin`` and password ``admin``.
 

	
 

	
 
Backups, backups, backups!
 
--------------------------
 

	
 
As it is well known, everyone has backups of their important data. Right?
 
Riiiiight?
 

	
 
There are two Ansible roles that implement backup functionality. One is the
 
``backup_server`` role, while the other is ``backup_client`` role. Backup is
 
based around the use of `Duplicity <http://duplicity.nongnu.org/>`_ and its
 
convenience wrapper, `Duply <http://duply.net>`_. Due to this selection, it
 
should be noted that the backup clients are the ones making connection to the
 
backup server (not the other way around).
 

	
 
Backups are encrypted and signed using GnuPG before being stored on the backup
 
server. Private key used for encryption and signing is therefore stored on the
 
client side. This key should not be encrypted in order to allow for unattended
 
backups.
 

	
 
Although not necessary, it is highly recommended to have separation between
 
different backup clients and the keys used for encryption and
 
signing. I.e. stick to a separate encryption/signing key for each backup
 
client. It should be noted that it is also possible to specify additional
 
*public* keys to encrypt with. This lets you have a backup decryptable with some
 
other, "master" key.
 

	
 
The backups are transferred to the backup server via SFTP - the
 
``backup_server`` role sets-up a dedicated OpenSSH server instance that limits
 
the connecting clients to a SFTP chroot.
 

	
 
All backups are stored within directory ``/srv/backups`` (on the backup
 
server). Within this directory, every client server has a dedicated
 
sub-directory, and within this sub-directory another sub-directory called
 
``duplicity``, where the actual *Duplicity* backups are stored. So, for example,
 
the directory where backups for ``www.example.com`` are stored at would be
 
``/srv/backups/www.example.com/duplicity``.
 

	
 
When backups are configured, they are set-up to be running every morning at
 
02:00. Before the backup run it is possible to run a preparation task, and a lot
 
of roles do this in order to create database dumps etc.
 

	
 

	
 
Setting-up the backup server
 
----------------------------
 

	
 
With the overview of backups out of the way, it is time to set-up the backup
 
server itself first. This is a farily simple task to perform, so let's get
 
straight to it:
 

	
 
1. Update the playbook for backup server to include the backup server role.
 

	
 
   :file:`~/mysite/playbooks/backup.yml`
 
   ::
 

	
 
      ---
 
      - hosts: backup
 
        remote_user: ansible
 
        sudo: yes
 
        roles:
 
          - common
 
          - mail_forwarder
 
          - backup_server
 

	
 
2. There is just one mandatory parameter for the role - OpenSSH server keys to
 
   be used for backup-dedicated instance:
 

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

	
 
      ---
 

	
 
      backup_host_ssh_private_keys:
 
        dsa: "{{ lookup('file', inventory_dir + '/ssh/bak_dsa_key') }}"
 
        rsa: "{{ lookup('file', inventory_dir + '/ssh/bak_rsa_key') }}"
 
        ed25519: "{{ lookup('file', inventory_dir + '/ssh/bak_ed25519_key') }}"
 
        ecdsa: "{{ lookup('file', inventory_dir + '/ssh/bak_ecdsa_key') }}"
 

	
 
3. Since we have decided to specify the keys above through file lookup, the
 
   above-listed keys now need to be generated::
 

	
 
     ssh-keygen -f ~/mysite/ssh/bak_dsa_key -N '' -t dsa
 
     ssh-keygen -f ~/mysite/ssh/bak_rsa_key -N '' -t rsa
 
     ssh-keygen -f ~/mysite/ssh/bak_ed25519_key -N '' -t ed25519
 
     ssh-keygen -f ~/mysite/ssh/bak_ecdsa_key -N '' -t ecdsa
 

	
 

	
 
Adding backup clients
 
---------------------
 

	
 
Well, that was all nice and dandy, but it does look like something is
 
missing... Ah, we haven't really configured any backup clients, right?
 
Surprisingly enough, specifying backup clients is optional, but that won't get
 
you far.
 

	
 
Luckily for you, all relevant *Majic Ansible Roles* are *backup-aware*. In other
 
words, all the roles have been implemented with support for doing back-ups - it
 
is just that by default this functionality is disabled (since you might be
 
relying on some other schema to back things up - LVM snapshots or what-not).
 

	
 
All that is needed is to enable the backups for each role, and provide some
 
extra variables required by the ``backup_client`` role.
 

	
 
For this a set of GnuPG private keys are necessary. These need to be provided as
 
ASCII-armoured GnuPG-exported files. For simplicity sake, this example documents
 
use of GnuPG keyring in conjunction with Ansible's ``pipe`` lookup.
 

	
 
So, back to the business:
 

	
 
1. Update the backup server configuration - each client needs to be explicitly
 
   registered:
 

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

	
 
      backup_clients:
 
        - server: comms.example.com
 
          public_key: "{{ lookup('file', inventory_dir + '/ssh/comms.example.com.pub') }}"
 
          ip: 10.32.64.19
 
        - server: www.example.com
 
          public_key: "{{ lookup('file', inventory_dir + '/ssh/www.example.com.pub') }}"
 
          ip: 10.32.64.20
 
        # Ah, this is a bit interesting - we can back-up the backup server
 
        # itself! Don't worry, though, this is mainly so the logs and home
 
        # directories are preserved, so it won't take too much space ;)
 
        - server: bak.example.com
 
          public_key: "{{ lookup('file', inventory_dir + '/ssh/bak.example.com.pub') }}"
 
          ip: 127.0.0.1
 

	
 
2. And now to configure backup clients for all servers:
 

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

	
 
      enable_backup: yes
 
      backup_encryption_key: "{{ lookup('pipe', 'gpg2 --homedir ~/mysite/gnupg/ --armour --export-secret-keys ' + ansible_fqdn ) }}"
 
      backup_server: bak.example.com
 
      backup_server_host_ssh_public_keys:
 
        - "{{ lookup('file', inventory_dir + '/ssh/bak_dsa_key.pub') }}"
 
        - "{{ lookup('file', inventory_dir + '/ssh/bak_rsa_key.pub') }}"
 
        - "{{ lookup('file', inventory_dir + '/ssh/bak_ed25519_key.pub') }}"
 
        - "{{ lookup('file', inventory_dir + '/ssh/bak_ecdsa_key.pub') }}"
 
      backup_ssh_key: "{{ lookup('file', inventory_dir + '/ssh/' + ansible_fqdn) }}"
 

	
 
3. So, looking at the configuration up there, there is a couple of file lookups
 
   for getting the variable values, as well as one pipe lookup for fetching the
 
   encryption keys. For start, let's create the SSH private keys used for client
 
   log-ins to backup server::
 

	
 
     ssh-keygen -f ~/mysite/ssh/comms.example.com -N ''
 
     ssh-keygen -f ~/mysite/ssh/www.example.com -N ''
 
     ssh-keygen -f ~/mysite/ssh/bak.example.com -N ''
 

	
 
4. Next off, a GnuPG keyring needs to be populated with private keys that will
 
   be used for backup encryption and signing. In total, we need three keys, one
 
   for each server. The keys should not be encrypted, and they should be named
 
   after the respective server's FQDN. For simplicity sake, here is a nice
 
   copy-pastable batch version for doing so:
 

	
 
   .. note:: Key generation requires a lot of entropy. If you are running this
 
             command on a VM, you may want to ``apt-get install haveged`` to
 
             speed this up. Please do read up on haveged if deploying to a real
 
             system, though! Don't trust it blindly!
 

	
 
   ::
 

	
 
     chmod 700 ~/mysite/gnupg
 
     cat << EOF | gpg2 --homedir ~/mysite/gnupg --batch --gen-key
 
     Key-Type:RSA
 
     Key-Length:1024
 
     Name-Real:comms.example.com
 
     Expire-Date:0
 
     %commit
 

	
 
     Key-Type:RSA
 
     Key-Length:1024
 
     Name-Real:www.example.com
 
     Expire-Date:0
 
     %commit
 

	
 
     Key-Type:RSA
 
     Key-Length:1024
 
     Name-Real:bak.example.com
 
     Expire-Date:0
 
     %commit
 
     EOF
 

	
 
5. And... Apply::
 

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

	
 
6. At this point the backup roles have been set-up everywhere, and the backups
 
   will be running every day at 02:00 in the morning. Of course, you may want to
 
   test out a backup yourself immediatelly by running the following command on
 
   servers::
 

	
 
     duply main backup
 

	
 
   .. note:: For more information on available commands and how to work with
 
             backup tool, please have look at `Official Duply Pages
 
             <http://duply.net/>`_.
 

	
 

	
 
Adding backup support to custom roles
 
-------------------------------------
 

	
 
As mentioned before, all of the supplied roles coming with *Majic Ansible Roles*
 
include backup support. What gets backed-up depends on the role implementation
 
(see role reference for details). What about backup support for custom roles?
 
This is something that has to be done by hand. However, it is quite simple to do
 
so.
 

	
 
Backup integration will be demonstrated with the previously implemented
 
``tbg`` role.
 

	
 
*The Bug Genie* stores most of its data in database, but thanks to the
 
``database`` role its backup is already handled for us. As a side-note, just
 
before every backup run the database is dumped and stored in location
 
``/srv/backup/tbg.sql``. That file is subsequently backed-up via *Duply* run.
 

	
 
What is not backed-up for us, though, are the files uploaded to *The Bug
 
Genie*. So let's fix that one.
 

	
 
1. Add the backup client role to list of dependencies.
 

	
 
   :file:`~/mysite/roles/tbg/meta/main.yml`
 
   ::
 

	
 
        - role: backup_client
 

	
 
2. Add task to the ``tbg`` role that deploys file containing additional patterns
 
   that should be backed-up from the disk during backup runs:
 

	
 
   :file:`~/mysite/roles/tbg/tasks/main.yml`
 
   ::
 

	
 
      - name: Configure back-up of The Bug Genie uploaded files
 
        copy: content="/var/www/tbg.example.com/files" dest="/etc/duply/main/patterns/tbg"
 
              owner="root" group="root" mode=600
 
        notify:
 
          - Assemble Duply include patterns
 

	
 
   .. warning:: Don't forget to notify the ``Assemble Duply include patterns``
 
                handler! This handler will collect all the different patterns
 
                deployed by various roles in a single place for backup software
 
                to pick-up.
 

	
 
3. Apply the changes::
 

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

	
 
4. Now rerun the backup on server ``www.example.com`` (as root). If you haven't
 
   uploaded any files, you may want to do so before testing to make sure
 
   something is backed-up.
 

	
 
   ::
 

	
 
     duply main backup
 

	
 
5. Verify that the files have been backed-up:
 

	
 
   ::
 

	
 
      duply main list
 

	
 
.. note:: If you wanted to run a script prior to backup run, you would simply
 
          deploy a shell script with desired content to
 
          ``/etc/duply/main/pre.d/``. Just make sure the permissions for it are
 
          ok (it has to be executable by the root user).
 

	
 

	
 
Where to go next?
 
-----------------
 

	
0 comments (0 inline, 0 general)