Changeset - 500658358454
[Not reviewed]
0 5 10
Branko Majic (branko) - 8 years ago 2015-11-15 14:18:44
branko@majic.rs
MAR-44: Added backup server implementation. Updated testsite to include deployment of dedicated backup server. Documented the backup server implementation (except for usage instructions).
15 files changed with 373 insertions and 9 deletions:
0 comments (0 inline, 0 general)
.gitignore
Show inline comments
 
@@ -9,4 +9,8 @@ testsite/preseed_files/
 
testsite/tls/*.pem
 
testsite/tls/*.key
 
testsite/tls/*.*_*.cfg
 
testsite/retry/*
 
\ No newline at end of file
 
testsite/retry/*
 
testsite/ssh/*_key
 
testsite/ssh/*_key.pub
 
testsite/ssh/*.example.com
 
testsite/ssh/*.example.com.pub
 
\ No newline at end of file
docs/rolereference.rst
Show inline comments
 
@@ -1305,3 +1305,82 @@ website):
 
  - role: database
 
    db_name: phpinfo_example_com
 
    db_password: phpinfo_example_com
 

	
 

	
 
Backup Server
 
-------------
 

	
 
The ``backup_server`` role can be used for setting-up a server to act as backup
 
storage for the backup clients. Storage is made available to the clients
 
exclusively via SFTP on a dedicated port and dedicated OpenSSH server
 
instance. This instance is specifically configured and tailored for this
 
purpose.
 

	
 
The role is primarily aimed for use with `Duplicity
 
<http://duplicity.nongnu.org/>`_, but should be also usable for generic SFTP
 
uploads.
 

	
 
The role implements the following:
 

	
 
* Installs backup software (Duplicity, Duply).
 
* Creates a dedicated directory structure for backups with the following structure:
 

	
 
  * ``/srv/backups/`` - main directory under which all the backups reside.
 
  * ``/srv/backups/SERVER_NAME/`` - home directory for the backup user, name
 
    after the server. Backup users are confined to their respective home
 
    directory via chroot. Backup users can't write to their own home directory,
 
    though.
 
  * ``/srv/backups/SERVER_NAME/duplicity/`` - directory where the Duplicity
 
    backups are stored at. This directory is writable by the respective backup
 
    user.
 
  * ``SERVER_NAME/.ssh/`` - directory where authorized keys are stored. Backup
 
    user is not allowed to make modifications to this directory and files
 
    contained within (i.e. backup users can't add more keys to the
 
    ``authorized_keys`` file).
 
* Creates dedicated operating system users for backup clients. These users will
 
  be made members of the ``backup`` group as well (as an additional group).
 
* Sets-up ``authorized_keys`` for the backup clients.
 
* Makes sure the backup users can't log-in via regular OpenSSH server instance.
 
* Sets-up dedicated OpenSSH server instances to be used exclusively by backup
 
  clients. The instance listens on TCP port ``2222``.
 
* Updates firewall to allow incoming TCP connections to port
 
  ``2222``. Connections are allowed only from the configured IP addresses
 
  associated with backup clients.
 

	
 

	
 
Parameters
 
~~~~~~~~~~
 

	
 
**backup_clients** (list, optional)
 
  List of backup clients that are connecting to the backup server. This is
 
  usually done on a per-server basis. Each item in the list is a dictionary
 
  describing the backup client. The following keys are available:
 

	
 
  **server** (string, mandatory)
 
    Name of the server that is backed up. It is highly recommended to use
 
    server's FQDN for this purpose. The dedicated operating system user created
 
    will have the name of format ``bak-ESCAPED_SERVER_NAME``, where
 
    ``ESCAPED_SERVER_NAME`` is calculated by taking the passed-in server name
 
    and replacing all dots (``.``) with undescores (``_``). For example,
 
    ``web.example.com`` will be turned into ``bak-web_example_com``.
 

	
 
  **uid** (integer, optional, ``whatever OS picks``)
 
    Uid for the operating system user. User's default group will have a GID
 
    identical to the user's UID if specified. Otherwise user's default group
 
    will have OS-determined GID.
 

	
 
  **ip** (IPv4 address, mandatory)
 
    IPv4 address from which the backup client server is connecting to the backup
 
    server. Used for introducing stricter firewall rules.
 

	
 
**backup_host_ssh_private_keys** (dictionary, mandatory)
 
  Defines host keys used for the dedicated OpenSSH server instance for
 
  backup. Key values that must be provided are: **dsa**, **rsa**, **ed25519**,
 
  and **ecdsa**, with values for each one of them corresponding to a private key
 
  generated using the appropriate algorithm. Keys for this purpose can be easily
 
  created via commands::
 

	
 
    ssh-keygen -f backup_server_dsa_key -N '' -t dsa
 
    ssh-keygen -f backup_server_rsa_key -N '' -t rsa
 
    ssh-keygen -f backup_server_ed25519_key -N '' -t ed25519
 
    ssh-keygen -f backup_server_ecdsa_key -N '' -t ecdsa
docs/testsite.rst
Show inline comments
 
@@ -110,15 +110,29 @@ In order to deploy the test site, the following steps would normally be taken:
 
   location ``testsite/tls/ca.pem``. It is very important to
 
   include the full CA chain used for LDAP server.
 

	
 
6. Generate the preseed files:
 
6. Generate SSH keys to be used by the backup server and backup clients:
 

	
 
  .. code-block:: shell
 

	
 
    ssh-keygen -f ssh/backup_server_dsa_key -N '' -t dsa
 
    ssh-keygen -f ssh/backup_server_rsa_key -N '' -t rsa
 
    ssh-keygen -f ssh/backup_server_ed25519_key -N '' -t ed25519
 
    ssh-keygen -f ssh/backup_server_ecdsa_key -N '' -t ecdsa
 
    ssh-keygen -f ssh/mail.example.com -N ''
 
    ssh-keygen -f ssh/ldap.example.com -N ''
 
    ssh-keygen -f ssh/xmpp.example.com -N ''
 
    ssh-keygen -f ssh/web.example.com -N ''
 
    ssh-keygen -f ssh/backup.example.com -N ''
 

	
 
7. Generate the preseed files:
 

	
 
  .. code-block:: shell
 

	
 
    ansible-playbook playbooks/preseed.yml
 

	
 
7. Install all servers using the generated preseed files.
 
8. Install all servers using the generated preseed files.
 

	
 
8. Add the SSH host fingerprints to your ``known_hosts`` file (don't forget to
 
9. Add the SSH host fingerprints to your ``known_hosts`` file (don't forget to
 
   remove old entries if you are redoing the process). You can easily obtain all
 
   the necessary fingerprints with command (don't forget to modify domain if you
 
   need to):
 
@@ -127,14 +141,14 @@ In order to deploy the test site, the following steps would normally be taken:
 

	
 
      ssh-keyscan -t ed25519 mail.example.com ldap.example.com xmpp.example.com web.example.com $(resolveip -s mail.example.com) $(resolveip -s ldap.example.com) $(resolveip -s xmpp.example.com) $(resolveip -s web.example.com)
 

	
 
9. Invoke the ``bootstrap.yml`` playbook in order to set-up some basic
 
   environment for Ansible runs on all servers:
 
10. Invoke the ``bootstrap.yml`` playbook in order to set-up some basic
 
    environment for Ansible runs on all servers:
 

	
 
  .. code-block:: shell
 

	
 
    ansible-playbook playbooks/bootstrap.yml
 

	
 
10. Finally, apply configuration on all servers:
 
11. Finally, apply configuration on all servers:
 

	
 
  .. code-block:: shell
 

	
roles/backup_server/defaults/main.yml
Show inline comments
 
new file 100644
 
---
 

	
 
backup_clients: []
 
backup_host_ssh_keys:
 
  dsa: "{{ ssh_key_dir }}/{{ ansible_fqdn }}_dsa_key"
 
  rsa: "{{ ssh_key_dir }}/{{ ansible_fqdn }}_rsa_key"
 
  ed25519: "{{ ssh_key_dir }}/{{ ansible_fqdn }}_ed25519_key"
 
  ecdsa: "{{ ssh_key_dir }}/{{ ansible_fqdn }}_ecdsa_key"
roles/backup_server/files/backup-sshd_config
Show inline comments
 
new file 100644
 
# Listen on separate port for backup purposes.
 
Port 2222
 

	
 
# Use the SSH protocol version 2 (which is safer).
 
Protocol 2
 

	
 
# Define dedicated host keys for backup SSH server.
 
HostKey /etc/ssh-backup/ssh_host_rsa_key
 
HostKey /etc/ssh-backup/ssh_host_dsa_key
 
HostKey /etc/ssh-backup/ssh_host_ecdsa_key
 
HostKey /etc/ssh-backup/ssh_host_ed25519_key
 

	
 
# Use privilege separation for increased security.
 
UsePrivilegeSeparation yes
 

	
 
# Configure logging.
 
SyslogFacility AUTH
 
LogLevel INFO
 

	
 
# Users logging-in have 10 seconds to login upon established connection.
 
LoginGraceTime 10
 

	
 
# Don't allow root accounts logins.
 
PermitRootLogin no
 

	
 
# Enforce strict checking of home directory mode. However, this is not used for
 
# the chroots (chroots must check mode).
 
StrictModes yes
 

	
 
# Allow public key authentication.
 
PubkeyAuthentication yes
 

	
 
# Don't read the user's ~/.rhosts and ~/.shosts files for eventual
 
# RhostsRSAAuthentication or HostbasedAuthentication.
 
IgnoreRhosts yes
 

	
 
# Disable host-based authentication.
 
HostbasedAuthentication no
 

	
 
# Do not allow logins with empty passwords.
 
PermitEmptyPasswords no
 

	
 
# Don't allow challenge-response authentication.
 
ChallengeResponseAuthentication no
 

	
 
# Disable password-based authentication.
 
PasswordAuthentication no
 

	
 
# Disable X11 forwarding.
 
X11Forwarding no
 

	
 
# Do not print motd to avoid eventual issues for clients.
 
PrintMotd no
 

	
 
# Do not print the date and time of the last user login.
 
PrintLastLog no
 

	
 
# Use TPC keepalives for detecting dead connections.
 
TCPKeepAlive yes
 

	
 
# Use the internal SFTP so we can also easily utilise chroot.
 
Subsystem sftp internal-sftp
 

	
 
# Use PAM. But thanks to PasswordAuthentication being set to "no", PAM will be
 
# used just for session stuff.
 
UsePAM yes
 

	
 
# Specify a dedicated PID file for the backup SSH.
 
PidFile /var/run/sshd-backup.pid
 

	
 
# Users logging-in are forced to use the SFTP server.
 
ForceCommand internal-sftp
 

	
 
# Chroot logged-in users to their home directories.
 
ChrootDirectory %h
 

	
 
# Do not allow any TCP forwarding.
 
AllowTCPForwarding no
 

	
 
# Only allow the members of this group to log-in into this instance of OpenSSH
 
# server.
 
AllowGroups backup
 
\ No newline at end of file
roles/backup_server/files/ssh-backup.default
Show inline comments
 
new file 100644
 
# Default settings for OpenSSH backup server. Used by systemd service.
 

	
 
# Separate configuration file is used for backup instance.
 
SSHD_OPTS="-f /etc/ssh-backup/sshd_config"
roles/backup_server/files/ssh-backup.service
Show inline comments
 
new file 100644
 
[Unit]
 
Description=OpenBSD Secure Shell server for backup purposes
 
After=network.target auditd.service
 
ConditionPathExists=!/etc/ssh-backup/sshd_not_to_be_run
 

	
 
[Service]
 
EnvironmentFile=-/etc/default/ssh-backup
 
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
 
ExecReload=/bin/kill -HUP $MAINPID
 
KillMode=process
 
Restart=on-failure
 

	
 
[Install]
 
WantedBy=multi-user.target
roles/backup_server/files/ssh-backup.socket
Show inline comments
 
new file 100644
 
[Unit]
 
Description=OpenBSD Secure Shell server socket for backup purposes
 
Before=ssh-backup.service
 
Conflicts=ssh-backup.service
 
ConditionPathExists=!/etc/ssh-backup/sshd_not_to_be_run
 

	
 
[Socket]
 
ListenStream=2222
 
Accept=yes
 

	
 
[Install]
 
WantedBy=sockets.target
roles/backup_server/handlers/main.yml
Show inline comments
 
new file 100644
 
---
 

	
 
- name: Restart backup SSH server
 
  service: name=ssh-backup state=restarted
roles/backup_server/tasks/main.yml
Show inline comments
 
new file 100644
 
---
 

	
 
- name: Install backup software
 
  apt: name="{{ item }}" state=installed
 
  with_items:
 
    - duplicity
 
    - duply
 

	
 
- name: Create directory for storing backups
 
  file: path="/srv/backups" state=directory
 
        owner="root" group="root" mode=751
 

	
 
- name: Create backup client groups
 
  group: name="{{ item.server | replace('.', '_') | regex_replace('^', 'bak-') }}"
 
         gid="{{ item.uid | default(omit) }}" system="yes"
 
  with_items: backup_clients
 

	
 
- name: Create backup client users
 
  user: name="{{ item.server | replace('.', '_') | regex_replace('^', 'bak-') }}"
 
        group="{{ item.server | replace('.', '_') | regex_replace('^', 'bak-') }}"
 
        groups="backup"
 
        uid="{{ item.uid | default(omit) }}"
 
        system=yes createhome=no state=present home="/srv/backups/{{ item.server }}"
 
  with_items: backup_clients
 

	
 
- name: Create home directories for backup client users
 
  file: path="/srv/backups/{{ item.server }}" state=directory
 
        owner="root" group="{{ item.server | replace('.', '_') | regex_replace('^', 'bak-') }}" mode=750
 
  with_items: backup_clients
 

	
 
- name: Create duplicity directories for backup client users
 
  file: path="/srv/backups/{{ item.server }}/duplicity" state=directory
 
        owner="{{ item.server | replace('.', '_') | regex_replace('^', 'bak-') }}"
 
        group="{{ item.server | replace('.', '_') | regex_replace('^', 'bak-') }}"
 
        mode=770
 
  with_items: backup_clients
 

	
 
- name: Create SSH directory for backup client users
 
  file: path="/srv/backups/{{ item.server }}/.ssh" state=directory
 
        owner="root" group="root" mode=751
 
  with_items: backup_clients
 

	
 
- name: Populate authorized keys for backup client users
 
  authorized_key: user="{{ item.server | replace('.', '_') | regex_replace('^', 'bak-') }}"
 
                  key="{{ item.public_key }}" manage_dir="no" state="present"
 
  with_items: backup_clients
 

	
 
- name: Set-up authorized_keys file permissions for backup client users
 
  file: path="/srv/backups/{{ item.server }}/.ssh/authorized_keys" state=file
 
        owner="root" group="{{ item.server | replace('.', '_') | regex_replace('^', 'bak-') }}"
 
        mode=640
 
  with_items: backup_clients
 

	
 
- name: Deny the backup group login via regular SSH
 
  lineinfile: dest="/etc/ssh/sshd_config" state=present line="DenyGroups backup"
 
  notify:
 
    - Restart SSH
 

	
 
- name: Set-up directory for the backup OpenSSH server instance
 
  file: path="/etc/ssh-backup/" state=directory
 
        owner="root" group="root" mode="700"
 

	
 
- name: Deploy configuration file for the backup OpenSSH server instance service
 
  copy: src="ssh-backup.default" dest="/etc/default/ssh-backup"
 
        owner="root" group="root" mode="644"
 
  notify:
 
    - Restart backup SSH server
 

	
 
- name: Deploy configuration file for the backup OpenSSH server instance
 
  copy: src="backup-sshd_config" dest="/etc/ssh-backup/sshd_config"
 
        owner="root" group="root" mode="600"
 
  notify:
 
    - Restart backup SSH server
 

	
 
- name: Deploy the private keys for backup OpenSSH server instance
 
  copy: content="{{ item.value }}" dest="/etc/ssh-backup/ssh_host_{{ item.key }}_key"
 
        owner="root" group="root" mode="600"
 
  with_dict: backup_host_ssh_private_keys
 
  no_log: True
 
  notify:
 
    - Restart backup SSH server
 

	
 
- name: Deploy backup OpenSSH server systemd service file
 
  copy: src="ssh-backup.service" dest="/etc/systemd/system/ssh-backup.service"
 
        owner=root group=root mode=644
 
  notify:
 
    - Reload systemd
 
    - Restart backup SSH server
 

	
 
- name: Start and enable OpenSSH backup service
 
  service: name="ssh-backup" state="started" enabled="yes"
 

	
 
- name: Deploy firewall configuration for backup server
 
  template: src="ferm_backup.conf.j2" dest="/etc/ferm/conf.d/40-backup.conf" owner=root group=root mode=640
 
  notify:
 
    - Restart ferm
roles/backup_server/templates/ferm_backup.conf.j2
Show inline comments
 
new file 100644
 
{% if backup_clients -%}
 
table filter {
 
    chain INPUT {
 
        saddr ({% for client in backup_clients %} {{ client.ip }}{% endfor %}) @subchain "backup_in" {
 
            # SSH
 
            proto tcp dport 2222 ACCEPT;
 
        }
 
    }
 
}
 
{%- endif %}
testsite/group_vars/backup.yml
Show inline comments
 
new file 100644
 
---
 

	
 
local_mail_aliases:
 
  root: "root john.doe@{{ testsite_domain }}"
 

	
 
smtp_relay_host: mail.{{ testsite_domain }}
 

	
 
smtp_relay_truststore: /etc/ssl/certs/ca.pem
 

	
 
backup_clients:
 
  - server: web.example.com
 
    uid: 3000
 
    public_key: "{{ lookup('file', inventory_dir + '/ssh/web.example.com.pub') }}"
 
    ip: 10.32.64.1
 
  - server: mail.example.com
 
    public_key: "{{ lookup('file', inventory_dir + '/ssh/mail.example.com.pub') }}"
 
    ip: 10.32.64.1
 

	
 
backup_host_ssh_private_keys:
 
  dsa: "{{ lookup('file', inventory_dir + '/ssh/backup_server_dsa_key') }}"
 
  rsa: "{{ lookup('file', inventory_dir + '/ssh/backup_server_rsa_key') }}"
 
  ed25519: "{{ lookup('file', inventory_dir + '/ssh/backup_server_ed25519_key') }}"
 
  ecdsa: "{{ lookup('file', inventory_dir + '/ssh/backup_server_ecdsa_key') }}"
testsite/hosts
Show inline comments
 
@@ -13,8 +13,12 @@ mail.example.com
 
[web]
 
web.example.com
 

	
 
[backup]
 
backup.example.com
 

	
 
[testsite:children]
 
ldap
 
xmpp
 
mail
 
web
 
\ No newline at end of file
 
web
 
backup
 
\ No newline at end of file
testsite/playbooks/backup.yml
Show inline comments
 
new file 100644
 
---
 

	
 
- hosts: backup
 
  remote_user: ansible
 
  sudo: yes
 
  roles:
 
    - common
 
    - mail_forwarder
 
    - backup_server
 
\ No newline at end of file
testsite/playbooks/site.yml
Show inline comments
 
@@ -4,4 +4,5 @@
 
- include: ldap.yml
 
- include: xmpp.yml
 
- include: mail.yml
 
- include: web.yml
 
\ No newline at end of file
 
- include: web.yml
 
- include: backup.yml
 
\ No newline at end of file
0 comments (0 inline, 0 general)