diff --git a/.gitignore b/.gitignore index d0c8d0d68e4715defab9b533550a6ce55ce34c4d..dce4445b76ce79b39ab9cdfbd62a21a5aae430f4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ testsite/retry/* testsite/ssh/*_key testsite/ssh/*_key.pub testsite/ssh/*.example.com -testsite/ssh/*.example.com.pub \ No newline at end of file +testsite/ssh/*.example.com.pub +testsite/backup_keyring/ diff --git a/roles/backup_client/defaults/main.yml b/roles/backup_client/defaults/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..da87a621cc12256aac00e92651bd97a5093efb84 --- /dev/null +++ b/roles/backup_client/defaults/main.yml @@ -0,0 +1,3 @@ +--- + +backup_client_username: "bak-{{ ansible_fqdn | replace('.', '_') }}" \ No newline at end of file diff --git a/roles/backup_client/handlers/main.yml b/roles/backup_client/handlers/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..5f48eb6c84776e69a4579e0d226c861136375ada --- /dev/null +++ b/roles/backup_client/handlers/main.yml @@ -0,0 +1,11 @@ +--- + +- name: Assemble Duply include patterns + assemble: dest="/etc/duply/main/include" src="/etc/duply/main/patterns" + owner=root group=root mode=600 backup=yes + +- name: Import encryption public keys + command: gpg2 --homedir /etc/duply/main/gnupg --import /etc/duply/main/public_encryption_keys.asc + +- name: Import signing private keys + command: gpg2 --homedir /etc/duply/main/gnupg --import /etc/duply/main/private_signing_key.asc diff --git a/roles/backup_client/tasks/main.yml b/roles/backup_client/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..19afddc7217b88f987b85cc1c5e00b82810705df --- /dev/null +++ b/roles/backup_client/tasks/main.yml @@ -0,0 +1,66 @@ +--- + +- name: Install backup software + apt: name="{{ item }}" state=installed + with_items: + - duplicity + - duply + +- name: Set-up Duply directories + file: path="{{ item }}" state=directory owner=root group=root mode=700 + with_items: + - "/etc/duply" + - "/etc/duply/main" + - "/etc/duply/main/patterns" + - "/etc/duply/main/gnupg" + - "/etc/duply/main/ssh" + - "/var/cache/duply" + - "/var/cache/duply/main" + +- name: Deploy Duply configuration file + template: src="duply_main_conf.j2" dest="/etc/duply/main/conf" + owner=root group=root mode=600 + +- name: Extract encryption keys + local_action: command gpg2 --homedir "{{ backup_gnupg_keyring }}" --armor --export "{{ backup_encryption_keys | join(',') }}" + become: no + register: encryption_keys + changed_when: False + +- name: Extract signing key + local_action: command gpg2 --homedir "{{ backup_gnupg_keyring }}" --armor --export-secret-key "{{ backup_signing_key }}" + become: no + when: backup_signing_key != "disabled" + register: signing_key + changed_when: False + +- name: Deploy GnuPG public keys for encryption + copy: content="{{ encryption_keys.stdout }}" dest="/etc/duply/main/public_encryption_keys.asc" + owner=root group=root mode=600 + notify: + - Import encryption public keys + +- name: Deploy GnuPG private keys for signing + copy: content="{{ signing_key.stdout }}" dest="/etc/duply/main/private_signing_key.asc" + owner=root group=root mode=600 + no_log: True + when: backup_signing_key != "disabled" + notify: + - Import signing private keys + +- name: Deploy private SSH key for logging-in into backup server + copy: content="{{ backup_ssh_key }}" dest="/etc/duply/main/ssh/identity" + owner="root" group="root" mode="600" + no_log: True + +- name: Deploy custom known_hosts for backup purposes + template: src="known_hosts.j2" dest="/etc/duply/main/ssh/known_hosts" + owner="root" group="root" mode="600" + +- name: Deploy base exclude pattern (exclude all by default) + copy: content="- **" dest="/etc/duply/main/exclude" + owner="root" group="root" mode="600" + +- name: Assemble Duply include patterns + assemble: src="/etc/duply/main/patterns" dest="/etc/duply/main/include" + owner="root" group="root" mode="600" diff --git a/roles/backup_client/templates/duply_main_conf.j2 b/roles/backup_client/templates/duply_main_conf.j2 new file mode 100644 index 0000000000000000000000000000000000000000..cbf501c4ecf5a273410b3b59fd7816c0b74b59a4 --- /dev/null +++ b/roles/backup_client/templates/duply_main_conf.j2 @@ -0,0 +1,55 @@ +# GnuPG keys that should be used for encryption. Normally the encryption key is +# not available locally. +GPG_KEYS_ENC='{{ backup_encryption_keys | join(',') }}' + +# GnuPG keys that should be used for signing. Normally the signing key should be +#available locally. +GPG_KEY_SIGN='{{ backup_signing_key }}' + +# Trust all keys available in the GnuPG keyring. +GPG_OPTS="--homedir /etc/duply/main/gnupg/ --trust-model always" + +# Destination where the backups are stored at. +TARGET='sftp://{{ backup_client_username }}@{{ backup_server }}:2222//duplicity' + +# Base directory to backup (root). File selection is done via include/exclude +# patterns. +SOURCE='/' + +# Maximum age for preserving old backups. Used when running the "purge" +# command. +MAX_AGE=6M + +# Maximum age of the last full backup performed before a new full backup is +# taken. +MAX_FULLBKP_AGE=1M +DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE " + +# Duplicity volume size in megabytes. +VOLSIZE=1024 +DUPL_PARAMS="$DUPL_PARAMS --volsize $VOLSIZE " + +# Output verbosity (error 0, warning 1-2, notice 3-4, info 5-8, debug 9) +VERBOSITY=4 + +# Path to a directory used for restoring files from backups. The file is stored +# there temporarily. +TEMP_DIR="/tmp" + +# Directory for storing (caching) unencrypted metadata. This metadata is used +# for producting incremental backups. +ARCH_DIR="/var/cache/duply/main/" + +# Use the GnuPG agent for passwords prompts. Since we deploy the signing key +# without any encryption, this effectively means no prompts (and encryption key +# should not be available on backup client machines). +DUPL_PARAMS="$DUPL_PARAMS --use-agent" + +# Use the pexepct backend for Duplicity so we can pass in all the +# ssh-options. Use dedicated known hosts and identity file when connecting over +# SFTP. Using -oLogLevel=ERROR makes output a bit less verbose. This is mainly +# to avoid output from sftp telling us it added IP address to known_hosts. +DUPL_PARAMS="$DUPL_PARAMS --ssh-backend pexpect --ssh-options='-oLogLevel=ERROR -oUserKnownHostsFile=/dev/null -oGlobalKnownHostsFile=/etc/duply/main/ssh/known_hosts -oIdentityFile=/etc/duply/main/ssh/identity'" + +# By default we exclude everything, and then include only specific patterns. +DUPL_PARAMS="$DUPL_PARAMS --include-globbing-filelist /etc/duply/main/include" \ No newline at end of file diff --git a/roles/backup_client/templates/known_hosts.j2 b/roles/backup_client/templates/known_hosts.j2 new file mode 100644 index 0000000000000000000000000000000000000000..57f27e419fd9ab6962931b2ba029884fea1de405 --- /dev/null +++ b/roles/backup_client/templates/known_hosts.j2 @@ -0,0 +1,4 @@ +{% for item in backup_server_host_ssh_public_keys %} +[{{ backup_server }}]:2222 {{ item }} +{{ backup_server }} {{ item }} +{% endfor %} diff --git a/testsite/group_vars/backup.yml b/testsite/group_vars/backup.yml index e8a5d20e8a0c5c899e3959274c5048404ca7813d..6aed8d737ead10ff1dba14cf28194c8f9de48e9f 100644 --- a/testsite/group_vars/backup.yml +++ b/testsite/group_vars/backup.yml @@ -11,10 +11,10 @@ backup_clients: - server: web.example.com uid: 3000 public_key: "{{ lookup('file', inventory_dir + '/ssh/web.example.com.pub') }}" - ip: 10.32.64.1 + ip: 10.32.64.18 - server: mail.example.com public_key: "{{ lookup('file', inventory_dir + '/ssh/mail.example.com.pub') }}" - ip: 10.32.64.1 + ip: 10.32.64.15 backup_host_ssh_private_keys: dsa: "{{ lookup('file', inventory_dir + '/ssh/backup_server_dsa_key') }}"