From 0261e875679f1bf63c8d689da7fc7e014597885d Mon Sep 17 00:00:00 2001 From: Stonewall Jackson Date: Sat, 4 Feb 2023 01:23:43 -0500 Subject: initial commit --- playbooks/archiver.yml | 9 + playbooks/asterisk.yml | 18 ++ playbooks/bitwarden.yml | 15 + playbooks/common.yml | 112 +++++++ playbooks/cups.yml | 14 + playbooks/dav.yml | 25 ++ playbooks/dev_servers.yml | 9 + playbooks/dns_records.yml | 28 ++ playbooks/freeipa.yml | 24 ++ playbooks/freeipa_bootstrap.yml | 10 + playbooks/freeipa_replica.yml | 9 + playbooks/git.yml | 54 ++++ playbooks/jellyfin.yml | 20 ++ playbooks/linux_desktops.yml | 24 ++ playbooks/linux_laptops.yml | 27 ++ playbooks/mail.yml | 36 +++ playbooks/nagios.yml | 15 + playbooks/nameservers.yml | 9 + playbooks/nfs.yml | 9 + playbooks/opnsense.yml | 17 + playbooks/photostructure.yml | 20 ++ playbooks/populate_domain.yml | 99 ++++++ playbooks/postgres.yml | 15 + playbooks/privbrowse.yml | 33 ++ playbooks/proxmox.yml | 21 ++ playbooks/proxmox_instance.yml | 5 + playbooks/radius.yml | 9 + playbooks/site.yml | 37 +++ playbooks/syncthing.yml | 19 ++ playbooks/syslog.yml | 9 + playbooks/test.yml | 4 + playbooks/ttrss.yml | 21 ++ playbooks/turn.yml | 10 + playbooks/unifi.yml | 15 + playbooks/util/backup.yml | 606 ++++++++++++++++++++++++++++++++++++ playbooks/util/client_cert.yml | 71 +++++ playbooks/util/decomission_host.yml | 56 ++++ playbooks/util/restore.yml | 477 ++++++++++++++++++++++++++++ playbooks/util/wireguard_config.yml | 49 +++ playbooks/webserver_internal.yml | 46 +++ playbooks/webserver_public.yml | 38 +++ playbooks/wiki.yml | 25 ++ playbooks/xmpp.yml | 9 + playbooks/yum.yml | 33 ++ playbooks/znc.yml | 14 + 45 files changed, 2225 insertions(+) create mode 100644 playbooks/archiver.yml create mode 100644 playbooks/asterisk.yml create mode 100644 playbooks/bitwarden.yml create mode 100644 playbooks/common.yml create mode 100644 playbooks/cups.yml create mode 100644 playbooks/dav.yml create mode 100644 playbooks/dev_servers.yml create mode 100644 playbooks/dns_records.yml create mode 100644 playbooks/freeipa.yml create mode 100644 playbooks/freeipa_bootstrap.yml create mode 100644 playbooks/freeipa_replica.yml create mode 100644 playbooks/git.yml create mode 100644 playbooks/jellyfin.yml create mode 100644 playbooks/linux_desktops.yml create mode 100644 playbooks/linux_laptops.yml create mode 100644 playbooks/mail.yml create mode 100644 playbooks/nagios.yml create mode 100644 playbooks/nameservers.yml create mode 100644 playbooks/nfs.yml create mode 100644 playbooks/opnsense.yml create mode 100644 playbooks/photostructure.yml create mode 100644 playbooks/populate_domain.yml create mode 100644 playbooks/postgres.yml create mode 100644 playbooks/privbrowse.yml create mode 100644 playbooks/proxmox.yml create mode 100644 playbooks/proxmox_instance.yml create mode 100644 playbooks/radius.yml create mode 100644 playbooks/site.yml create mode 100644 playbooks/syncthing.yml create mode 100644 playbooks/syslog.yml create mode 100644 playbooks/test.yml create mode 100644 playbooks/ttrss.yml create mode 100644 playbooks/turn.yml create mode 100644 playbooks/unifi.yml create mode 100644 playbooks/util/backup.yml create mode 100644 playbooks/util/client_cert.yml create mode 100644 playbooks/util/decomission_host.yml create mode 100644 playbooks/util/restore.yml create mode 100644 playbooks/util/wireguard_config.yml create mode 100644 playbooks/webserver_internal.yml create mode 100644 playbooks/webserver_public.yml create mode 100644 playbooks/wiki.yml create mode 100644 playbooks/xmpp.yml create mode 100644 playbooks/yum.yml create mode 100644 playbooks/znc.yml (limited to 'playbooks') diff --git a/playbooks/archiver.yml b/playbooks/archiver.yml new file mode 100644 index 0000000..9056db3 --- /dev/null +++ b/playbooks/archiver.yml @@ -0,0 +1,9 @@ +- import_playbook: common.yml + vars: + hostlist: archive_servers + +- name: configure archiver + hosts: archive_servers + tags: archive,archiver + roles: + - archive_server diff --git a/playbooks/asterisk.yml b/playbooks/asterisk.yml new file mode 100644 index 0000000..153176a --- /dev/null +++ b/playbooks/asterisk.yml @@ -0,0 +1,18 @@ +- import_playbook: common.yml + vars: + hostlist: asterisk_servers + +- name: configure asterisk pbx + hosts: asterisk_servers + tags: asterisk + roles: + - role: asterisk + + - role: archive_job + archive_name: asterisk + archive_user: asterisk + archive_shell: >- + TIMESTAMP=$(date +%Y%m%d%H%M%S); + tar czf "asterisk-${TIMESTAMP}.tar.gz" + --transform "s|^\.|asterisk-${TIMESTAMP}|" -C {{ asterisk_data_dir }} . + tags: archive diff --git a/playbooks/bitwarden.yml b/playbooks/bitwarden.yml new file mode 100644 index 0000000..cb9a911 --- /dev/null +++ b/playbooks/bitwarden.yml @@ -0,0 +1,15 @@ +- import_playbook: common.yml + vars: + hostlist: bitwarden_servers + +- name: configure vaultwarden + hosts: bitwarden_servers + tags: vaultwarden,bitwarden + roles: + - role: vaultwarden + + - role: apache_vhost + apache_default_vhost: yes + apache_canonical_hostname: '{{ vaultwarden_server_name }}' + apache_config: '{{ vaultwarden_apache_config }}' + tags: apache diff --git a/playbooks/common.yml b/playbooks/common.yml new file mode 100644 index 0000000..e96be0b --- /dev/null +++ b/playbooks/common.yml @@ -0,0 +1,112 @@ +- hosts: '{{ hostlist | default("el") }}' + gather_facts: no + tags: common + roles: + - role: proxmox_instance + when: '"proxmox_instances" in group_names' + tags: proxmox + + - role: dns_records + when: not (bootstrap | default(false)) + tags: dns + + - role: gather_facts + when: not ansible_facts + tags: always + + - role: udev + when: not ansible_virtualization_tech_guest + tags: udev + + - role: root_authorized_keys + tags: authorized_keys + + - role: root_password + tags: root_password + + - role: polkit + tags: polkit + + - role: grub + tags: grub + + - role: sudo + tags: sudo,sudoers + + - role: hostname + tags: hostname + + - role: timezone + tags: timezone + + - role: journald + tags: journald + + - role: yum_disable_default_repos + when: '"yum_mirrors" not in group_names' + + - role: yum + yum_repositories: + - rocky-baseos + - rocky-appstream + - rocky-extras + when: + - '"yum_mirrors" not in group_names' + - not (bootstrap | default(false)) + tags: yum + + - role: dnsmasq + when: '"freeipa_servers" not in group_names' + tags: dnsmasq + + - role: locale + tags: locale + + - role: selinux + tags: selinux + + - role: qemu_guest_agent + when: '"kvm" in ansible_virtualization_tech_guest' + tags: qemu + + - role: firewalld + tags: firewalld + + - role: chrony + tags: chrony,ntp + + - role: dnf_automatic + tags: yum + + - role: ssh + tags: ssh + + - role: tuned + tags: tuned + + - role: motd + tags: motd + + - role: packages + tags: packages + + - role: postfix_client + when: '"mail_servers" not in group_names' + tags: postfix,mail + + - role: freeipa_client + when: '"freeipa_servers" not in group_names' + tags: freeipa + + - role: rsyslog_client + when: + - '"syslog_servers" not in group_names' + - not (bootstrap | default(false)) + tags: rsyslog + + - role: nagios_client + when: + - (group_names | intersect(nagios_excluded_groups) | length) == 0 + - '"yum_mirrors" not in group_names' + - not (bootstrap | default(false)) + tags: nagios diff --git a/playbooks/cups.yml b/playbooks/cups.yml new file mode 100644 index 0000000..20bed0b --- /dev/null +++ b/playbooks/cups.yml @@ -0,0 +1,14 @@ +- import_playbook: common.yml + vars: + hostlist: cups_servers + +- name: configure cups + hosts: cups_servers + tags: cups + roles: + - role: cups_server + + - role: archive_job + archive_name: cups + archive_shell: '{{ cups_archive_shell }}' + tags: archive diff --git a/playbooks/dav.yml b/playbooks/dav.yml new file mode 100644 index 0000000..21c4a97 --- /dev/null +++ b/playbooks/dav.yml @@ -0,0 +1,25 @@ +- import_playbook: common.yml + vars: + hostlist: dav_servers + +- name: configure sabredav + hosts: dav_servers + tags: sabredav,dav + roles: + - role: sabredav + + - role: apache_vhost + apache_default_vhost: yes + apache_document_root: '{{ sabredav_home }}' + apache_config: '{{ sabredav_apache_config }}' + tags: apache + + - role: php + php_fpm_environment: '{{ sabredav_php_environment }}' + php_fpm_admin_flags: '{{ sabredav_php_flags }}' + tags: php + + - role: archive_job + archive_name: webdav + archive_shell: '{{ sabredav_archive_shell }}' + tags: archive diff --git a/playbooks/dev_servers.yml b/playbooks/dev_servers.yml new file mode 100644 index 0000000..2602d6d --- /dev/null +++ b/playbooks/dev_servers.yml @@ -0,0 +1,9 @@ +- import_playbook: common.yml + vars: + hostlist: dev_servers + +- name: configure development environment + hosts: dev_servers + tags: dev + roles: + - dev_environment diff --git a/playbooks/dns_records.yml b/playbooks/dns_records.yml new file mode 100644 index 0000000..93d635e --- /dev/null +++ b/playbooks/dns_records.yml @@ -0,0 +1,28 @@ +- name: add dns records for infrastructure hosts + hosts: proxmox_hypervisors:opnsense_firewalls:unmanaged + tags: dns + roles: + - dns_records + +- name: add reverse dns records for firewall vlan interfaces + hosts: freeipa_master + tags: dns + tasks: + - name: create reverse dns zones + ipadnszone: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + zone_name: '{{ item }}' + state: present + loop: "{{ vlans.values() | map(attribute='gateway') | ansible.utils.ipaddr('revdns') | map('regex_replace', '^[^.]+\\.', '') | unique }}" + + - name: create ptr records + ipadnsrecord: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + zone_name: "{{ item | ansible.utils.ipaddr('revdns') | regex_replace('^[^.]+\\.', '') }}" + record_name: '{{ item | split(".") | last }}' + record_type: PTR + record_value: '{{ groups["opnsense_firewalls"] | sort | first }}.{{ domain }}.' + state: present + loop: "{{ vlans.values() | map(attribute='gateway') }}" diff --git a/playbooks/freeipa.yml b/playbooks/freeipa.yml new file mode 100644 index 0000000..a83aaf6 --- /dev/null +++ b/playbooks/freeipa.yml @@ -0,0 +1,24 @@ +- import_playbook: common.yml + vars: + hostlist: freeipa_servers + +- name: configure freeipa master + hosts: freeipa_master + tags: freeipa + roles: + - role: freeipa_server + + - role: archive_job + archive_name: ipa + archive_on_calendar: 'Sat *-*-* 02:00:00' + archive_shell: >- + ipa-backup && + mv -v /var/lib/ipa/backup/* . && + find . -mindepth 1 -type d -exec chmod -v 770 {} + + tags: archive + +- name: configure freeipa replicas + hosts: freeipa_servers:!freeipa_master + tags: freeipa + roles: + - freeipa_server diff --git a/playbooks/freeipa_bootstrap.yml b/playbooks/freeipa_bootstrap.yml new file mode 100644 index 0000000..38865a1 --- /dev/null +++ b/playbooks/freeipa_bootstrap.yml @@ -0,0 +1,10 @@ +- import_playbook: common.yml + vars: + hostlist: freeipa_master + bootstrap: yes + +- name: configure freeipa master + hosts: freeipa_master + tags: freeipa + roles: + - freeipa_server diff --git a/playbooks/freeipa_replica.yml b/playbooks/freeipa_replica.yml new file mode 100644 index 0000000..adac739 --- /dev/null +++ b/playbooks/freeipa_replica.yml @@ -0,0 +1,9 @@ +- import_playbook: common.yml + vars: + hostlist: freeipa_master + +- name: configure freeipa replicas + hosts: freeipa_servers:!freeipa_master + tags: freeipa + roles: + - freeipa_replica diff --git a/playbooks/git.yml b/playbooks/git.yml new file mode 100644 index 0000000..9e4c112 --- /dev/null +++ b/playbooks/git.yml @@ -0,0 +1,54 @@ +- import_playbook: common.yml + vars: + hostlist: git_servers + +- name: configure git repository + hosts: git_servers + tags: git + roles: + - role: gitolite + tags: gitolite + + - role: archive_job + archive_name: gitolite + archive_user: '{{ gitolite_user }}' + archive_shell: '{{ gitolite_archive_shell }}' + tags: archive + + - role: cgit + tags: cgit + + - role: apache_vhost + apache_default_vhost: yes + apache_document_root: '{{ cgit_static_dir }}' + apache_config: | + SetEnv "GIT_PROJECT_ROOT" "{{ gitolite_home }}/repositories" + SetEnv "GIT_HTTP_EXPORT_ALL" "1" + + + AuthType GSSAPI + AuthName "FreeIPA Single Sign-On" + AuthLDAPUrl "{{ apache_ldap_url }}?krbprincipalname" + {{ apache_ldap_creds }} + + + Require ip {{ kerberized_cidrs | join(" ") }} + + Require ldap-attribute memberof=cn={{ gitolite_access_group }},{{ freeipa_group_basedn }} + Require ldap-attribute memberof=cn={{ gitolite_admin_group }},{{ freeipa_group_basedn }} + + + + Require not ip {{ kerberized_cidrs | join(" ") }} + Require all granted + + + + + Alias /static "{{ cgit_static_dir }}" + + ScriptAliasMatch "{{ git_backend_regex }}" "{{ gitolite_cgi_script }}/$1" + ScriptAlias "/" "{{ cgit_cgi_script }}/" + vars: + git_backend_regex: '(?x)^/(.*/(HEAD | info/refs | objects/(info/[^/]+ | [0-9a-f]{2}/[0-9a-f]{38} | pack/pack-[0-9a-f]{40}\.(pack|idx)) | git-(upload|receive)-pack))$' + tags: apache diff --git a/playbooks/jellyfin.yml b/playbooks/jellyfin.yml new file mode 100644 index 0000000..7fa6721 --- /dev/null +++ b/playbooks/jellyfin.yml @@ -0,0 +1,20 @@ +- import_playbook: common.yml + vars: + hostlist: jellyfin_servers + +- name: configure jellyfin + hosts: jellyfin_servers + tags: jellyfin + roles: + - role: jellyfin + + - role: apache_vhost + apache_default_vhost: yes + apache_config: '{{ jellyfin_apache_config }}' + tags: apache + + - role: archive_job + archive_name: jellyfin + archive_user: '{{ jellyfin_user }}' + archive_shell: '{{ jellyfin_archive_shell }}' + tags: archive diff --git a/playbooks/linux_desktops.yml b/playbooks/linux_desktops.yml new file mode 100644 index 0000000..d7c2fee --- /dev/null +++ b/playbooks/linux_desktops.yml @@ -0,0 +1,24 @@ +- import_playbook: common.yml + vars: + hostlist: linux_desktops + +- name: configure linux desktop environment + hosts: linux_desktops + roles: + - role: dev_environment + tags: dev + + - role: linux_desktop + tags: desktop,linux_desktop + + - role: local_homedirs + tags: local_homedirs,homedirs,homedir + + - role: firefox + tags: firefox + + - role: evolution + tags: evolution + + - role: cups_client + tags: cups diff --git a/playbooks/linux_laptops.yml b/playbooks/linux_laptops.yml new file mode 100644 index 0000000..c841e95 --- /dev/null +++ b/playbooks/linux_laptops.yml @@ -0,0 +1,27 @@ +- import_playbook: common.yml + vars: + hostlist: linux_laptops + +- name: configure linux desktop environment + hosts: linux_laptops + roles: + - role: dev_environment + tags: dev + + - role: linux_desktop + tags: desktop,linux_desktop + + - role: local_homedirs + tags: local_homedirs,homedirs,homedir + + - role: firefox + tags: firefox + + - role: evolution + tags: evolution + + - role: cups_client + tags: cups + + - role: linux_laptop + tags: laptop,linux_laptop diff --git a/playbooks/mail.yml b/playbooks/mail.yml new file mode 100644 index 0000000..6df70f3 --- /dev/null +++ b/playbooks/mail.yml @@ -0,0 +1,36 @@ +- import_playbook: common.yml + vars: + hostlist: rspamd_servers,mail_servers,imap_servers + +- name: configure rspamd + hosts: rspamd_servers + tags: rspamd + roles: + - role: rspamd + + - role: apache_vhost + apache_default_vhost: yes + apache_config: '{{ rspamd_apache_config }}' + tags: apache + + - role: archive_job + archive_name: rspamd + archive_shell: '{{ rspamd_archive_shell }}' + tags: archive + +- name: configure Postfix + hosts: mail_servers + tags: postfix,smtp + roles: + - postfix_server + +- name: configure Dovecot + hosts: imap_servers + tags: dovecot,imap + roles: + - role: dovecot + + - role: archive_job + archive_name: dovecot + archive_command: '{{ dovecot_archive_script }}' + tags: archive diff --git a/playbooks/nagios.yml b/playbooks/nagios.yml new file mode 100644 index 0000000..cb13d57 --- /dev/null +++ b/playbooks/nagios.yml @@ -0,0 +1,15 @@ +- import_playbook: common.yml + vars: + hostlist: nagios_servers + +- name: configure nagios + hosts: nagios_servers + tags: nagios + roles: + - role: nagios_server + + - role: apache_vhost + apache_default_vhost: yes + apache_document_root: '{{ nagios_html_dir }}' + apache_config: '{{ nagios_apache_config }}' + tags: apache diff --git a/playbooks/nameservers.yml b/playbooks/nameservers.yml new file mode 100644 index 0000000..a977744 --- /dev/null +++ b/playbooks/nameservers.yml @@ -0,0 +1,9 @@ +- import_playbook: common.yml + vars: + hostlist: authoritative_nameservers + +- name: configure nsd + hosts: authoritative_nameservers + tags: nsd + roles: + - nsd diff --git a/playbooks/nfs.yml b/playbooks/nfs.yml new file mode 100644 index 0000000..a066afb --- /dev/null +++ b/playbooks/nfs.yml @@ -0,0 +1,9 @@ +- import_playbook: common.yml + vars: + hostlist: nfs_servers + +- name: configure nfs exports + hosts: nfs_servers + tags: nfs + roles: + - nfs_server diff --git a/playbooks/opnsense.yml b/playbooks/opnsense.yml new file mode 100644 index 0000000..dd23a91 --- /dev/null +++ b/playbooks/opnsense.yml @@ -0,0 +1,17 @@ +- name: configure opnsense firewall + hosts: opnsense_firewalls + gather_facts: yes + vars: + unbound_max_negative_cache: 5 + roles: + - freebsd_loader + - devd + - pxe_server + tasks: + - name: set unbound negative ttl + copy: + content: | + server: + cache-max-negative-ttl: {{ unbound_max_negative_cache }} + dest: /usr/local/etc/unbound.opnsense.d/custom.conf + tags: unbound diff --git a/playbooks/photostructure.yml b/playbooks/photostructure.yml new file mode 100644 index 0000000..12ebe1f --- /dev/null +++ b/playbooks/photostructure.yml @@ -0,0 +1,20 @@ +- import_playbook: common.yml + vars: + hostlist: photostructure_servers + +- name: configure photostructure + hosts: photostructure_servers + tags: photostructure + roles: + - role: photostructure + + - role: apache_vhost + apache_default_vhost: yes + apache_config: '{{ photostructure_apache_config }}' + tags: apache + + - role: archive_job + archive_name: photostructure + archive_shell: '{{ photostructure_archive_shell }}' + archive_on_calendar: monthly + tags: archive diff --git a/playbooks/populate_domain.yml b/playbooks/populate_domain.yml new file mode 100644 index 0000000..acb1ec7 --- /dev/null +++ b/playbooks/populate_domain.yml @@ -0,0 +1,99 @@ +- name: populate freeipa domain + hosts: freeipa_master + vars: + default_user_password: ChangeMe123! + tasks: + - name: create users + ipauser: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ item.name }}' + givenname: '{{ item.givenname }}' + sn: '{{ item.sn }}' + email: '{{ [item.mail] if item.mail is defined else omit }}' + loginshell: '{{ item.loginshell | default(omit) }}' + password: '{{ item.password | default(default_user_password) }}' + update_password: on_create + state: present + loop: '{{ freeipa_users | default([]) }}' + tags: users + + - name: add custom attributes + ldap_attrs: + dn: 'uid={{ item.name }},{{ freeipa_user_basedn }}' + attributes: + mailAlternateAddress: '{{ item.mail_aliases | default([]) }}' + jid: '{{ item.jid | default([]) }}' + bind_dn: uid={{ ipa_user }},{{ freeipa_user_basedn }} + bind_pw: '{{ ipa_pass }}' + server_uri: ldaps://{{ ipa_host }} + state: exact + loop: "{{ freeipa_users | default([]) }}" + tags: users + + - name: create groups + ipagroup: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ item.name }}' + description: '{{ item.description | default(omit) }}' + user: '{{ item.user | default(omit) }}' + group: '{{ item.group | default(omit) }}' + nonposix: '{{ item.nonposix | default(omit) }}' + action: '{{ "member" if (item.append | default(false)) else "group" }}' + state: present + loop: '{{ freeipa_groups | default([]) }}' + tags: groups + + - name: add group email addresses + ldap_attrs: + dn: 'cn={{ item.name }},{{ freeipa_group_basedn }}' + attributes: + mail: '{{ item.mail | default([]) }}' + mailAlternateAddress: '{{ item.mail_aliases | default([]) }}' + bind_dn: uid={{ ipa_user }},{{ freeipa_user_basedn }} + bind_pw: '{{ ipa_pass }}' + server_uri: ldaps://{{ ipa_host }} + state: exact + loop: "{{ freeipa_groups | default([]) }}" + tags: groups + + - name: create sudo rules + ipasudorule: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ item.name }}' + description: '{{ item.description | default(omit) }}' + allow_sudocmd: '{{ item.cmd | default(omit) }}' + cmdcategory: '{{ item.cmdcategory | default(omit) }}' + allow_sudocmdgroup: '{{ item.cmdgroup | default(omit) }}' + host: '{{ item.host | default(omit) }}' + hostcategory: '{{ item.hostcategory | default(omit) }}' + hostgroup: '{{ item.hostgroup | default(omit) }}' + runasusercategory: '{{ item.runasusercategory | default(omit) }}' + runasgroupcategory: '{{ item.runasgroupcategory | default(omit) }}' + user: '{{ item.user | default(omit) }}' + usercategory: '{{ item.usercategory | default(omit) }}' + group: '{{ item.usergroup | default(omit) }}' + state: present + loop: '{{ freeipa_sudo_rules | default([]) }}' + tags: sudo + + - name: create hbac rules + ipahbacrule: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ item.name }}' + description: '{{ item.description | default(omit) }}' + host: '{{ item.host | default(omit) }}' + hostcategory: '{{ item.hostcategory | default(omit) }}' + hostgroup: '{{ item.hostgroup | default(omit) }}' + hbacsvc: '{{ item.service | default(omit) }}' + servicecategory: '{{ item.servicecategory | default(omit) }}' + hbacsvcgroup: '{{ item.servicegroup | default(omit) }}' + user: '{{ item.user | default(omit) }}' + usercategory: '{{ item.usercategory | default(omit) }}' + group: '{{ item.usergroup | default(omit) }}' + state: present + loop: '{{ freeipa_hbac_rules | default([]) }}' + tags: hbac diff --git a/playbooks/postgres.yml b/playbooks/postgres.yml new file mode 100644 index 0000000..72192ec --- /dev/null +++ b/playbooks/postgres.yml @@ -0,0 +1,15 @@ +- import_playbook: common.yml + vars: + hostlist: postgresql_servers + +- name: configure postgresql + hosts: postgresql_servers + tags: postgres,postgresql + roles: + - role: postgresql_server + + - role: archive_job + archive_name: postgres + archive_user: '{{ postgresql_user }}' + archive_shell: '{{ postgresql_archive_shell }}' + tags: archive diff --git a/playbooks/privbrowse.yml b/playbooks/privbrowse.yml new file mode 100644 index 0000000..8b61d5c --- /dev/null +++ b/playbooks/privbrowse.yml @@ -0,0 +1,33 @@ +- import_playbook: common.yml + vars: + hostlist: privbrowse_servers + +- name: configure web service frontends + hosts: privbrowse_servers + roles: + - role: invidious + tags: invidious + + - role: apache_vhost + apache_server_name: '{{ invidious_server_name }}' + apache_server_aliases: [] + apache_config: '{{ invidious_apache_config }}' + tags: apache + + - role: teddit + tags: teddit + + - role: apache_vhost + apache_server_name: '{{ teddit_server_name }}' + apache_server_aliases: [] + apache_config: '{{ teddit_apache_config }}' + tags: apache + + - role: nitter + tags: nitter + + - role: apache_vhost + apache_server_name: '{{ nitter_server_name }}' + apache_server_aliases: [] + apache_config: '{{ nitter_apache_config }}' + tags: apache diff --git a/playbooks/proxmox.yml b/playbooks/proxmox.yml new file mode 100644 index 0000000..9c449a4 --- /dev/null +++ b/playbooks/proxmox.yml @@ -0,0 +1,21 @@ +- name: configure proxmox hypervisor + hosts: proxmox_hypervisors + gather_facts: yes + roles: + - role: root_password + tags: root_password + + - role: root_authorized_keys + tags: ssh,authorized_keys + + - role: journald + tags: journald + + - role: hostname + tags: hostname + + - role: timezone + tags: timezone + + - role: proxmox_hypervisor + tags: proxmox,pve diff --git a/playbooks/proxmox_instance.yml b/playbooks/proxmox_instance.yml new file mode 100644 index 0000000..f326b4a --- /dev/null +++ b/playbooks/proxmox_instance.yml @@ -0,0 +1,5 @@ +- name: build proxmox virtual machine + hosts: proxmox_instances + tags: proxmox + roles: + - proxmox_instance diff --git a/playbooks/radius.yml b/playbooks/radius.yml new file mode 100644 index 0000000..6529365 --- /dev/null +++ b/playbooks/radius.yml @@ -0,0 +1,9 @@ +- import_playbook: common.yml + vars: + hostlist: radius_servers + +- name: configure freeradius + hosts: radius_servers + tags: freeradius,radius + roles: + - freeradius diff --git a/playbooks/site.yml b/playbooks/site.yml new file mode 100644 index 0000000..d4bcd11 --- /dev/null +++ b/playbooks/site.yml @@ -0,0 +1,37 @@ +# internal hosts +- import_playbook: opnsense.yml +- import_playbook: proxmox.yml +- import_playbook: freeipa_bootstrap.yml +- import_playbook: dns_records.yml +- import_playbook: yum.yml +- import_playbook: freeipa.yml +- import_playbook: archiver.yml +- import_playbook: syslog.yml +- import_playbook: mail.yml +- import_playbook: cups.yml +- import_playbook: radius.yml +- import_playbook: unifi.yml +- import_playbook: postgres.yml +- import_playbook: dav.yml +- import_playbook: bitwarden.yml +- import_playbook: ttrss.yml +- import_playbook: znc.yml +- import_playbook: git.yml +- import_playbook: wiki.yml +- import_playbook: jellyfin.yml +- import_playbook: privbrowse.yml +- import_playbook: populate_domain.yml +- import_playbook: syncthing.yml +- import_playbook: photostructure.yml +- import_playbook: nfs.yml +- import_playbook: webserver_internal.yml +- import_playbook: dev_servers.yml +- import_playbook: linux_desktops.yml +- import_playbook: nagios.yml + +# public-facing hosts +- import_playbook: nameservers.yml +- import_playbook: webserver_public.yml +- import_playbook: turn.yml +- import_playbook: xmpp.yml +- import_playbook: asterisk.yml diff --git a/playbooks/syncthing.yml b/playbooks/syncthing.yml new file mode 100644 index 0000000..3fad588 --- /dev/null +++ b/playbooks/syncthing.yml @@ -0,0 +1,19 @@ +- import_playbook: common.yml + vars: + hostlist: syncthing_servers + +- name: configure syncthing + hosts: syncthing_servers + tags: syncthing + roles: + - role: syncthing + + - role: archive_job + archive_name: syncthing + archive_shell: '{{ syncthing_archive_shell }}' + tags: archive + + - role: apache_vhost + apache_default_vhost: yes + apache_config: '{{ syncthing_apache_config }}' + tags: apache diff --git a/playbooks/syslog.yml b/playbooks/syslog.yml new file mode 100644 index 0000000..2891dc6 --- /dev/null +++ b/playbooks/syslog.yml @@ -0,0 +1,9 @@ +- import_playbook: common.yml + vars: + hostlist: syslog_servers + +- name: configure rsyslog server + hosts: syslog_servers + tags: rsyslog,syslog + roles: + - rsyslog_server diff --git a/playbooks/test.yml b/playbooks/test.yml new file mode 100644 index 0000000..f3eaa62 --- /dev/null +++ b/playbooks/test.yml @@ -0,0 +1,4 @@ +- hosts: all + tasks: + - debug: + var: vlan diff --git a/playbooks/ttrss.yml b/playbooks/ttrss.yml new file mode 100644 index 0000000..befd157 --- /dev/null +++ b/playbooks/ttrss.yml @@ -0,0 +1,21 @@ +- import_playbook: common.yml + tags: common + vars: + hostlist: ttrss_servers + +- name: configure tinytinyrss + hosts: ttrss_servers + tags: ttrss + roles: + - role: ttrss + + - role: apache_vhost + apache_default_vhost: yes + apache_canonical_hostname: '{{ ttrss_server_name }}' + apache_document_root: '{{ ttrss_home }}' + apache_config: '{{ ttrss_apache_config }}' + tags: apache + + - role: php + php_fpm_environment: '{{ ttrss_php_environment }}' + tags: php diff --git a/playbooks/turn.yml b/playbooks/turn.yml new file mode 100644 index 0000000..20b6196 --- /dev/null +++ b/playbooks/turn.yml @@ -0,0 +1,10 @@ +- import_playbook: common.yml + tags: common + vars: + hostlist: turn_servers + +- name: configure coturn + hosts: turn_servers + tags: coturn,turn + roles: + - role: coturn diff --git a/playbooks/unifi.yml b/playbooks/unifi.yml new file mode 100644 index 0000000..1b0864d --- /dev/null +++ b/playbooks/unifi.yml @@ -0,0 +1,15 @@ +- import_playbook: common.yml + tags: common + vars: + hostlist: unifi_controllers + +- name: configure unifi controller + hosts: unifi_controllers + tags: unifi + roles: + - role: unifi + + - role: archive_job + archive_name: unifi + archive_shell: '{{ unifi_archive_shell }}' + tags: archive diff --git a/playbooks/util/backup.yml b/playbooks/util/backup.yml new file mode 100644 index 0000000..0c99eea --- /dev/null +++ b/playbooks/util/backup.yml @@ -0,0 +1,606 @@ +################# +# Set backup name +################# +- hosts: localhost + tags: always + tasks: + - name: get current timestamp + setup: + filter: ansible_date_time + + - name: create backup directory + file: + path: '{{ backup_path }}' + state: directory + +- hosts: all:localhost:!unmanaged + tags: always + tasks: + - name: set backup name + set_fact: + backup_name: '{{ backup_name | default(hostvars.localhost.ansible_date_time.iso8601_basic_short) }}' + + +################ +# IMAP Mailboxes +################ +- name: backup dovecot mailboxes + hosts: imap_servers + vars_files: ../../roles/dovecot/vars/main.yml + vars: + dovecot_backup_dir: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-mailboxes + dovecot_backup_tarball: '{{ dovecot_backup_dir }}.tar.gz' + dovecot_backup_sieve_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-sieve.tar.gz + tags: dovecot,imap + tasks: + - name: create backup directory + file: + path: '{{ dovecot_backup_dir }}' + owner: '{{ dovecot_vmail_user }}' + group: '{{ dovecot_vmail_user }}' + mode: 0770 + state: directory + + - name: collect dovecot users + command: doveadm user * + register: dovecot_users + changed_when: no + + - name: export mailboxes + command: >- + doveadm -o plugin/quota= backup -n inbox -f -u {{ item | quote }} + mdbox:{{ dovecot_backup_dir | quote }}/{{ item | quote }}/mdbox:LAYOUT=fs + loop: '{{ dovecot_users.stdout_lines }}' + + - name: compress backup directory + archive: + path: '{{ dovecot_backup_dir }}' + dest: '{{ dovecot_backup_tarball }}' + mode: 0400 + remove: yes + + - name: fetch mailbox tarball + fetch: + src: '{{ dovecot_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete mailbox tarball from remote host + file: + path: '{{ dovecot_backup_tarball }}' + state: absent + + - name: compress sieve scripts + archive: + path: + - '{{ dovecot_vmail_dir }}/*/sieve' + - '{{ dovecot_vmail_dir }}/*/.dovecot.sieve' + dest: '{{ dovecot_backup_sieve_tarball }}' + mode: 0400 + + - name: fetch sieve tarball + fetch: + src: '{{ dovecot_backup_sieve_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete sieve tarball from remote host + file: + path: '{{ dovecot_backup_sieve_tarball }}' + state: absent + + +################## +# Rspamd Databases +################## +- name: backup rspamd databases + hosts: rspamd_servers + vars_files: + - ../../roles/redis/vars/main.yml + - ../../roles/rspamd/vars/main.yml + vars: + rspamd_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-rspamd.tar.gz + tags: rspamd + tasks: + - name: dump redis databases to disk + command: + cmd: redis-cli -p {{ item }} + stdin: save + loop: + - '{{ rspamd_redis_port }}' + - '{{ rspamd_redis_bayes_port }}' + + - name: compress redis directory + archive: + path: '{{ redis_home }}' + dest: '{{ rspamd_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ rspamd_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ rspamd_backup_tarball }}' + state: absent + + +################### +# ZNC Configuration +################### +- name: backup znc configuration + hosts: znc_servers + vars_files: ../../roles/znc/vars/main.yml + vars: + znc_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-znc.tar.gz + tags: znc + tasks: + - name: compress znc directory + archive: + path: '{{ znc_home }}' + dest: '{{ znc_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ znc_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ znc_backup_tarball }}' + state: absent + + +######################### +# Syncthing Configuration +######################### +- name: backup syncthing configuration + hosts: syncthing_servers + vars_files: ../../roles/syncthing/vars/main.yml + vars: + syncthing_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-syncthing.tar.gz + tags: syncthing + tasks: + - name: compress syncthing directory + archive: + path: '{{ syncthing_home }}' + dest: '{{ syncthing_backup_tarball }}' + exclusion_patterns: + - '*/index-*.db*' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ syncthing_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ syncthing_backup_tarball }}' + state: absent + + +################## +# Git Repositories +################## +- name: backup git respositories + hosts: git_servers + vars_files: ../../roles/gitolite/vars/main.yml + vars: + git_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-git.tar.gz + tags: git + tasks: + - name: compress git directory + archive: + path: '{{ gitolite_home }}' + dest: '{{ git_backup_tarball }}' + exclusion_patterns: + - git/.ansible* + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ git_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ git_backup_tarball }}' + state: absent + + +###################### +# PostgreSQL Databases +###################### +- name: backup postgresql databases + hosts: postgresql_servers + vars_files: ../../roles/postgresql_server/vars/main.yml + vars: + postgresql_backup_file: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-pg_dumpall.sql + postgresql_backup_gzip: '{{ postgresql_backup_file }}.gz' + tags: postgres,postgresql + tasks: + - name: dump databases + command: pg_dumpall -f {{ postgresql_backup_file | quote }} + become: yes + become_user: '{{ postgresql_user }}' + + - name: compress sql file + archive: + path: '{{ postgresql_backup_file }}' + dest: '{{ postgresql_backup_gzip }}' + mode: 0400 + remove: yes + + - name: fetch backup gzip + fetch: + src: '{{ postgresql_backup_gzip }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup gzip from remote + file: + path: '{{ postgresql_backup_gzip }}' + state: absent + + + +######################## +# Jellyfin Configuration +######################## +- name: backup jellyfin configuration + hosts: jellyfin_servers + vars_files: ../../roles/jellyfin/vars/main.yml + vars: + jellyfin_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-jellyfin.tar.gz + tags: jellyfin + tasks: + - name: compress jellyfin directories + archive: + path: + - '{{ jellyfin_home }}/data' + - '{{ jellyfin_home }}/metadata' + - '{{ jellyfin_home }}/plugins' + - '{{ jellyfin_home }}/root' + - '{{ jellyfin_conf_dir }}' + dest: '{{ jellyfin_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ jellyfin_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ jellyfin_backup_tarball }}' + state: absent + + +################## +# Mediawiki Images +################## +- name: backup mediawiki images + hosts: wiki_servers + vars_files: ../../roles/mediawiki/vars/main.yml + vars: + mediawiki_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-mediawiki.tar.gz + tags: mediawiki,wiki + tasks: + - name: compress images directory + archive: + path: '{{ mediawiki_home }}/images' + dest: '{{ mediawiki_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ mediawiki_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ mediawiki_backup_tarball }}' + state: absent + + +######################### +# Photostructure Database +######################### +- name: backup photostructure database + hosts: photostructure_servers + vars_files: ../../roles/photostructure/vars/main.yml + vars: + photostructure_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-photostructure.tar + tags: photostructure + tasks: + - name: stop photostructure + systemd: + name: photostructure + state: stopped + + - name: archive photostructure library + archive: + path: '{{ photostructure_library }}' + dest: '{{ photostructure_backup_tarball }}' + format: tar + mode: 0400 + + - name: start photostructure + systemd: + name: photostructure + state: started + + - name: fetch backup tarball + fetch: + src: '{{ photostructure_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + validate_checksum: no # The tarball is way too big. + + - name: delete backup tarball from remote host + file: + path: '{{ photostructure_backup_tarball }}' + state: absent + + +############### +# Asterisk Data +############### +- name: backup asterisk data + hosts: asterisk_servers + vars_files: ../../roles/asterisk/vars/main.yml + vars: + asterisk_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-asterisk.tar.gz + tags: asterisk + tasks: + - name: stop asterisk + systemd: + name: asterisk + state: stopped + + - name: compress asterisk directory + archive: + path: '{{ asterisk_data_dir }}' + dest: '{{ asterisk_backup_tarball }}' + mode: 0400 + + - name: start asterisk + systemd: + name: asterisk + state: started + + - name: fetch backup tarball + fetch: + src: '{{ asterisk_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ asterisk_backup_tarball }}' + state: absent + + +#################### +# Cups Configuration +#################### +- name: backup cups configuration + hosts: cups_servers + vars: + cups_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-cups.tar.gz + tags: cups + tasks: + - name: compress cups configuration + archive: + path: + - /etc/cups/ppd + - /etc/cups/printers.conf + dest: '{{ cups_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ cups_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ cups_backup_tarball }}' + state: absent + + +#################### +# WebDAV Directories +#################### +- name: backup webdav directories + hosts: dav_servers + vars_files: ../../roles/sabredav/vars/main.yml + vars: + sabredav_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-webdav.tar.gz + tags: dav,sabredav,webdav + tasks: + - name: compress webdav directory + archive: + path: '{{ sabredav_home }}/webdav' + dest: '{{ sabredav_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ sabredav_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ sabredav_backup_tarball }}' + state: absent + + +############### +# Hastebin Data +############### +- name: backup hastebin data + hosts: pastebin_servers + vars_files: ../../roles/hastebin/vars/main.yml + vars: + hastebin_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-hastebin.tar.gz + tags: pastebin,hastebin + tasks: + - name: compress paste directory + archive: + path: '{{ hastebin_data_dir }}' + dest: '{{ hastebin_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ hastebin_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ hastebin_backup_tarball }}' + state: absent + + +################## +# Psitransfer Data +################## +- name: backup psitransfer data + hosts: filedrop_servers + vars_files: ../../roles/psitransfer/vars/main.yml + vars: + psitransfer_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-psitransfer.tar.gz + tags: psitransfer + tasks: + - name: compress files directory + archive: + path: '{{ psitransfer_data_dir }}' + dest: '{{ psitransfer_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ psitransfer_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ psitransfer_backup_tarball }}' + state: absent + + +################## +# Apache WWW files +################## +- name: backup public apache files + hosts: web_servers + vars_files: + - ../../roles/apache/vars/main.yml + vars: + apache_backup_tarball: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-www.tar.gz + tags: apache,www + tasks: + - when: apache_backup_dirs | default([]) | length > 0 + block: + - name: compress www directory + archive: + path: "{{ apache_backup_dirs | map('regex_replace', '^', apache_public_dir~'/') }}" + dest: '{{ apache_backup_tarball }}' + mode: 0400 + + - name: fetch backup tarball + fetch: + src: '{{ apache_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup tarball from remote host + file: + path: '{{ apache_backup_tarball }}' + state: absent + + +#################### +# Unifi Controllers +#################### +- name: backup unifi controllers + hosts: unifi_controllers + vars_files: ../../roles/unifi/vars/main.yml + tags: unifi + tasks: + - name: collect autobackup files + find: + paths: '{{ unifi_autobackup_dir }}' + patterns: '*.unf' + file_type: file + register: unifi_autobackups + + - name: fetch most recent autobackup file + fetch: + src: "{{ unifi_autobackups.files | sort(attribute='mtime') | map(attribute='path') | last }}" + dest: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-unifi.unf' + flat: yes + + +################ +# FreeIPA Domain +################ +- name: backup freeipa domain + hosts: freeipa_master + vars_files: ../../roles/freeipa_server/vars/main.yml + vars: + freeipa_backup_tarball: /var/tmp/{{ backup_name }}-ipa-{{ freeipa_realm }}.tar.gz + tags: ipa,freeipa + tasks: + - name: create full ipa backup + command: ipa-backup + + - name: collect files in backup directory + find: + paths: '{{ freeipa_backup_dir }}' + patterns: ipa-full-* + file_type: directory + register: freeipa_backups + + - name: compress latest backup + archive: + path: "{{ freeipa_backups.files | sort(attribute='mtime') | map(attribute='path') | last }}" + dest: '{{ freeipa_backup_tarball }}' + mode: 0400 + remove: yes + + - name: fetch backup archive + fetch: + src: '{{ freeipa_backup_tarball }}' + dest: '{{ backup_path }}/' + flat: yes + + - name: delete backup archive from remote host + file: + path: '{{ freeipa_backup_tarball }}' + state: absent + + +############### +# Print summary +############### +- hosts: localhost + tags: always + tasks: + - debug: + msg: Backup {{ backup_name }} written to {{ backup_path }}. diff --git a/playbooks/util/client_cert.yml b/playbooks/util/client_cert.yml new file mode 100644 index 0000000..c81b298 --- /dev/null +++ b/playbooks/util/client_cert.yml @@ -0,0 +1,71 @@ +- name: generate client certificate + hosts: localhost + connection: local + become: no + vars_prompt: + - name: username + prompt: Enter username for the certificate subject + private: no + - name: passphrase + prompt: Enter password for the p12 file + private: yes + vars: + cert_dir: "{{ lookup('env', 'HOME') }}/pki" + key_size: 2048 + key_path: '{{ cert_dir }}/{{ username }}.key' + csr_path: '{{ cert_dir }}/{{ username }}.csr' + crt_path: '{{ cert_dir }}/{{ username }}.crt' + p12_path: '{{ cert_dir }}/{{ username }}.p12' + profile_id: caIPAclientAuth + tasks: + - name: create output directory + file: + path: '{{ cert_dir }}' + state: directory + + - name: generate private key + openssl_privatekey: + path: '{{ key_path }}' + size: '{{ key_size }}' + mode: 0600 + + - name: generate CSR + openssl_csr: + path: '{{ csr_path }}' + privatekey_path: '{{ key_path }}' + common_name: '{{ username }}' + use_common_name_for_san: no + + - name: request certificate from IPA + shell: + cmd: > + ipa cert-request {{ csr_path }} + --principal {{ username }} + --profile-id {{ profile_id }} + --chain + --certificate-out {{ crt_path }} + + # The openssl_pkcs12 ansible module seems to generate files that can't be + # decrypted by Android clients. The openssl CLI works fine though. + - name: generate PKCS#12 file + command: + cmd: > + openssl pkcs12 -export + -out {{ p12_path }} + -inkey {{ key_path }} + -in {{ crt_path }} + -name {{ username }}@{{ domain }} + -password pass:{{ passphrase | quote }} + creates: '{{ p12_path }}' + + - name: cleanup files + file: + path: '{{ item }}' + state: absent + loop: + - '{{ key_path }}' + - '{{ csr_path }}' + - '{{ crt_path }}' + + - debug: + msg: 'PKCS#12 file written to {{ p12_path }}. Passphrase: {{ passphrase }}' diff --git a/playbooks/util/decomission_host.yml b/playbooks/util/decomission_host.yml new file mode 100644 index 0000000..dae4b16 --- /dev/null +++ b/playbooks/util/decomission_host.yml @@ -0,0 +1,56 @@ +- name: decomission host + hosts: '{{ host }}' + tasks: + - name: delete A record + ipadnsrecord: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + zone_name: '{{ domain }}' + record_name: '{{ host }}' + record_type: A + record_value: '{{ ip }}' + state: absent + delegate_to: '{{ freeipa_master }}' + + - name: delete PTR record + ipadnsrecord: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + zone_name: "{{ ip | ansible.utils.ipaddr('revdns') | regex_replace('^[^.]+\\.', '') }}" + record_name: '{{ ip.split(".") | last }}' + record_type: PTR + record_value: '{{ fqdn ~ "." }}' + state: absent + delegate_to: '{{ freeipa_master }}' + + - name: delete CNAME records + ipadnsrecord: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + zone_name: "{{ domain }}" + record_name: '{{ item.split(".") | first }}' + record_type: CNAME + record_value: '{{ fqdn ~ "." }}' + state: absent + delegate_to: '{{ freeipa_master }}' + loop: '{{ cnames }}' + + - name: delete host object + ipahost: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ fqdn }}' + state: absent + delegate_to: '{{ ipa_host }}' + + - name: delete proxmox vm + proxmox_kvm: + node: '{{ proxmox_node }}' + api_host: localhost + api_user: '{{ proxmox_api_user }}' + api_password: '{{ proxmox_api_password }}' + name: '{{ inventory_hostname }}' + force: yes + state: absent + delegate_to: '{{ proxmox_api_host }}' + when: "'proxmox_instances' in group_names" diff --git a/playbooks/util/restore.yml b/playbooks/util/restore.yml new file mode 100644 index 0000000..3a0154c --- /dev/null +++ b/playbooks/util/restore.yml @@ -0,0 +1,477 @@ +################ +# IMAP Mailboxes +################ +- name: restore dovecot mailboxes + hosts: imap_servers + vars_files: ../../roles/dovecot/vars/main.yml + vars: + dovecot_temp_dir: /var/tmp/{{ backup_name }}-{{ inventory_hostname }}-mailboxes + dovecot_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-mailboxes.tar.gz' + dovecot_backup_sieve_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-sieve.tar.gz' + tags: dovecot,imap + tasks: + - name: create temporary directory + file: + path: '{{ dovecot_temp_dir }}' + owner: '{{ dovecot_vmail_user }}' + group: '{{ dovecot_vmail_user }}' + mode: 0770 + state: directory + + - name: extract mailbox tarball + unarchive: + src: '{{ dovecot_backup_tarball }}' + dest: '{{ dovecot_temp_dir }}' + extra_opts: + - --same-owner + - --strip-components=1 + + - name: collect dovecot users + command: doveadm user * + register: dovecot_users + changed_when: no + + - name: import mailboxes + command: >- + doveadm -o plugin/quota= sync -u {{ item | quote }} + mdbox:{{ dovecot_temp_dir }}/{{ item | quote }}/mdbox + loop: '{{ dovecot_users.stdout_lines }}' + + - name: drop FTS indexes + command: doveadm fts rescan -A + + - name: reindex mailboxes + command: doveadm index -A -q * + + - name: delete temporary directory + file: + path: '{{ dovecot_temp_dir }}' + state: absent + + - name: extract sieve scripts + unarchive: + src: '{{ dovecot_backup_sieve_tarball }}' + dest: '{{ dovecot_vmail_dir }}' + extra_opts: + - --same-owner + + +################## +# Rspamd Databases +################## +- name: restore rspamd databases + hosts: rspamd_servers + vars_files: + - ../../roles/redis/vars/main.yml + - ../../roles/rspamd/vars/main.yml + vars: + rspamd_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-rspamd.tar.gz' + tags: rspamd + tasks: + - name: stop redis instances + systemd: + name: redis@{{ item }} + state: stopped + loop: + - '{{ rspamd_redis_port }}' + - '{{ rspamd_redis_bayes_port }}' + + - name: stop rspamd + systemd: + name: rspamd + state: stopped + + - name: extract redis tarballs + unarchive: + src: '{{ rspamd_backup_tarball }}' + dest: '{{ redis_home }}' + extra_opts: + - --strip-components=1 + - --same-owner + + - name: start redis instances + systemd: + name: redis@{{ item }} + state: started + loop: + - '{{ rspamd_redis_port }}' + - '{{ rspamd_redis_bayes_port }}' + + - name: start rspamd + systemd: + name: rspamd + state: started + + +################### +# ZNC Configuration +################### +- name: restore znc configuration + hosts: znc_servers + vars_files: ../../roles/znc/vars/main.yml + vars: + znc_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-znc.tar.gz' + tags: znc + tasks: + - name: stop znc + systemd: + name: znc + state: stopped + + - name: extract config tarball + unarchive: + src: '{{ znc_backup_tarball }}' + dest: '{{ znc_home }}' + extra_opts: + - --strip-components=1 + - --same-owner + + - name: start znc + systemd: + name: znc + state: started + + +######################### +# Syncthing Configuration +######################### +- name: restore syncthing configuration + hosts: syncthing_servers + vars_files: ../../roles/syncthing/vars/main.yml + vars: + syncthing_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-syncthing.tar.gz' + tags: syncthing + tasks: + - name: stop syncthing daemons + command: systemctl stop syncthing-user@* + + - name: extract config tarball + unarchive: + src: '{{ syncthing_backup_tarball }}' + dest: '{{ syncthing_home }}' + extra_opts: + - --strip-components=1 + - --same-owner + + - name: collect syncthing users + find: + paths: '{{ syncthing_home }}' + recurse: no + file_type: directory + register: syncthing_users + + - name: start syncthing daemons + systemd: + name: syncthing-user@{{ item }} + state: started + loop: "{{ syncthing_users.files | map(attribute='path') | map('basename') }}" + + +################## +# Git Repositories +################## +- name: restore git repositories + hosts: git_servers + vars_files: + - ../../roles/gitolite/vars/main.yml + - ../../roles/cgit/vars/main.yml + vars: + git_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-git.tar.gz' + tags: git + tasks: + - name: extract git tarball + unarchive: + src: '{{ git_backup_tarball }}' + dest: '{{ gitolite_home }}' + extra_opts: + - --strip-components=1 + - --same-owner + + - name: clear cgit cache + file: + path: '{{ cgit_cache_dir }}' + owner: apache + mode: 0755 + setype: _default + state: '{{ item }}' + loop: + - absent + - directory + + +###################### +# PostgreSQL Databases +###################### +- name: restore postgresql databases + hosts: postgresql_servers + vars_files: ../../roles/postgresql_server/vars/main.yml + vars: + postgresql_backup_gzip: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-pg_dumpall.sql.gz' + postgresql_remote_gzip: /var/tmp/{{ postgresql_backup_gzip | basename }} + tags: postgres,postgresql + tasks: + - name: copy backup gzip to remote host + copy: + src: '{{ postgresql_backup_gzip }}' + dest: '{{ postgresql_remote_gzip }}' + owner: '{{ postgresql_user }}' + group: '{{ postgresql_user }}' + mode: 0400 + + - name: import database backup + shell: gunzip {{ postgresql_remote_gzip | quote }} --to-stdout | psql + become: yes + become_user: '{{ postgresql_user }}' + + - name: delete gzip file from remote host + file: + path: '{{ postgresql_remote_gzip }}' + state: absent + + +######################## +# Jellyfin Configuration +######################## +- name: restore jellyfin configuration + hosts: jellyfin_servers + vars_files: ../../roles/jellyfin/vars/main.yml + vars: + jellyfin_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-jellyfin.tar.gz' + tags: jellyfin + tasks: + - name: stop jellyfin + systemd: + name: jellyfin + state: stopped + + - name: extract backup tarball + unarchive: + src: '{{ jellyfin_backup_tarball }}' + dest: / + extra_opts: + - --same-owner + + - name: start jellyfin + systemd: + name: jellyfin + state: started + + +################## +# Mediawiki Images +################## +- name: restore mediawiki images + hosts: wiki_servers + vars_files: ../../roles/mediawiki/vars/main.yml + vars: + mediawiki_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-mediawiki.tar.gz' + tags: mediawiki,wiki + tasks: + - name: extract backup tarball + unarchive: + src: '{{ mediawiki_backup_tarball }}' + dest: '{{ mediawiki_home }}/images' + extra_opts: + - --strip-components=1 + - --same-owner + + +######################### +# Photostructure Database +######################### +- name: restore photostructure database + hosts: photostructure_servers + vars_files: + - ../../roles/photostructure/vars/main.yml + vars: + photostructure_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-photostructure.tar' + tags: photostructure + tasks: + - name: stop photostructure + systemd: + name: photostructure + state: stopped + + - name: extract backup tarball + unarchive: + src: '{{ photostructure_backup_tarball }}' + dest: '{{ photostructure_library }}' + extra_opts: + - --strip-components=1 + - --same-owner + + - name: start photostructure + systemd: + name: photostructure + state: started + + +#################### +# Cups Configuration +#################### +- name: restore cups configuration + hosts: cups_servers + vars: + cups_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-cups.tar.gz' + tags: cups + tasks: + - name: stop cups + systemd: + name: cups + state: stopped + + - name: extract backup tarball + unarchive: + src: '{{ cups_backup_tarball }}' + dest: /etc/cups + extra_opts: + - --same-owner + + - name: start cups + systemd: + name: cups + state: started + + +############### +# Asterisk Data +############### +- name: restore asterisk data + hosts: asterisk_servers + vars_files: ../../roles/asterisk/vars/main.yml + vars: + asterisk_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-asterisk.tar.gz' + tags: asterisk + tasks: + - name: stop asterisk + systemd: + name: asterisk + state: stopped + + - name: extract backup tarball + unarchive: + src: '{{ asterisk_backup_tarball }}' + dest: '{{ asterisk_data_dir }}' + extra_opts: + - --strip-components=1 + - --same-owner + + - name: start asterisk + systemd: + name: asterisk + state: started + + +#################### +# WebDAV Directories +#################### +- name: restore webdav directories + hosts: dav_servers + vars_files: ../../roles/sabredav/vars/main.yml + vars: + sabredav_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-webdav.tar.gz' + tags: sabredav,dav,webdav + tasks: + - name: extract backup tarball + unarchive: + src: '{{ sabredav_backup_tarball }}' + dest: '{{ sabredav_home }}/webdav' + extra_opts: + - --strip-components=1 + - --same-owner + + +############### +# Hastebin Data +############### +- name: restore hastebin data + hosts: pastebin_servers + vars_files: ../../roles/hastebin/vars/main.yml + vars: + hastebin_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-hastebin.tar.gz' + tags: hastebin,pastebin + tasks: + - name: extract backup tarball + unarchive: + src: '{{ hastebin_backup_tarball }}' + dest: '{{ hastebin_data_dir }}' + extra_opts: + - --strip-components=1 + - --same-owner + + +################## +# Psitransfer Data +################## +- name: restore psitransfer data + hosts: filedrop_servers + vars_files: ../../roles/psitransfer/vars/main.yml + vars: + psitransfer_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-psitransfer.tar.gz' + tags: psitransfer + tasks: + - name: extract backup tarball + unarchive: + src: '{{ psitransfer_backup_tarball }}' + dest: '{{ psitransfer_data_dir }}' + extra_opts: + - --strip-components=1 + - --same-owner + + +################## +# Apache WWW Files +################## +- name: restore public apache files + hosts: web_servers + vars_files: ../../roles/apache/vars/main.yml + vars: + apache_backup_tarball: '{{ backup_path }}/{{ backup_name }}-{{ inventory_hostname }}-www.tar.gz' + tags: apache,www + tasks: + - name: extract backup tarball + unarchive: + src: '{{ apache_backup_tarball }}' + dest: '{{ apache_public_dir }}' + extra_opts: + - --same-owner + + +################ +# FreeIPA Domain +################ +- name: restore freeipa domain + hosts: freeipa_master + vars_files: ../../roles/freeipa_server/vars/main.yml + vars: + freeipa_backup_tarball: '{{ backup_path }}/{{ backup_name }}-ipa-{{ freeipa_realm }}.tar.gz' + freeipa_remote_backup_path: '{{ freeipa_backup_dir }}/{{ backup_name }}' + tags: ipa,freeipa + tasks: + # Only restore FreeIPA when explicitly requested - it is quite disruptive. + - when: ansible_run_tags | intersect(['ipa','freeipa']) | length > 0 + block: + - name: create backup directory on remote host + file: + path: '{{ freeipa_remote_backup_path }}' + state: directory + mode: 0700 + + - name: extract backup tarball + unarchive: + src: '{{ freeipa_backup_tarball }}' + dest: '{{ freeipa_remote_backup_path }}' + extra_opts: + - --strip-components=1 + - --same-owner + + - name: restore freeipa domain from backup + command: ipa-restore {{ backup_name | quote }} --unattended --password={{ freeipa_ds_password | quote }} + + - name: clear sssd cache + command: sss_cache -E + + - name: delete backup files from remote host + file: + path: '{{ freeipa_remote_backup_path }}' + state: absent diff --git a/playbooks/util/wireguard_config.yml b/playbooks/util/wireguard_config.yml new file mode 100644 index 0000000..fb98ca4 --- /dev/null +++ b/playbooks/util/wireguard_config.yml @@ -0,0 +1,49 @@ +- name: generate client certificate + hosts: localhost + connection: local + become: no + vars_prompt: + - name: client_ip + prompt: Enter client ip address + private: no + vars: + config_path: "{{ lookup('env', 'HOME') }}/{{ organization | replace(' ', '-') | lower }}-wg.conf" + server_pubkey: '{{ wireguard_pubkey }}' + server_port: '{{ wireguard_port | default(51820) }}' + server_host: '{{ wireguard_host }}' + gateway: '{{ vlans.vpn.gateway }}' + dns_server: "{{ vlans.vpn.dns_servers | join(',') }}" + tasks: + - name: generate private key + command: + cmd: wg genkey + register: wg_genkey + changed_when: no + + - name: generate public key + command: + cmd: wg pubkey + stdin: '{{ wg_genkey.stdout }}' + register: wg_pubkey + changed_when: no + + - name: generate wireguard config file + copy: + dest: '{{ config_path }}' + mode: 0600 + content: | + [Interface] + Address = {{ client_ip }}/32 + PrivateKey = {{ wg_genkey.stdout }} + DNS = {{ dns_server }} + + [Peer] + PublicKey = {{ server_pubkey }} + AllowedIPs = 0.0.0.0/0 + Endpoint = {{ server_host }}:{{ server_port }} + + - debug: + msg: 'wireguard client config written to {{ config_path }}' + + - debug: + msg: 'Add the following client to the wireguard server: {{ client_ip }}/32 {{ wg_pubkey.stdout }}' diff --git a/playbooks/webserver_internal.yml b/playbooks/webserver_internal.yml new file mode 100644 index 0000000..eb27c97 --- /dev/null +++ b/playbooks/webserver_internal.yml @@ -0,0 +1,46 @@ +- import_playbook: common.yml + vars: + hostlist: www1 + +- name: configure internal web servers + hosts: www1 + tags: apache + roles: + - role: apache_vhost + apache_default_vhost: yes + apache_config: | + AliasMatch "^/pub/user/([^/]+)(.*)" "/nfs/user/$1/pub$2" + AliasMatch "^/pub/group/([^/]+)(.*)" "/nfs/group/$1/pub$2" + + + Options -FollowSymLinks +Indexes + AllowOverride None + Require all granted + + + + Options -FollowSymLinks +Indexes + AllowOverride None + Require all granted + + tasks: + - name: generate index.html + tags: apache + copy: + dest: /var/www/html/index.html + content: | + + + + + {{ domain }} webserver + + +

This is the {{ organization }} internal webserver. To access files in user or group + public directories, try paths like the following: +

+ diff --git a/playbooks/webserver_public.yml b/playbooks/webserver_public.yml new file mode 100644 index 0000000..17221e6 --- /dev/null +++ b/playbooks/webserver_public.yml @@ -0,0 +1,38 @@ +- import_playbook: common.yml + vars: + hostlist: dmz-www1 + +- name: configure public web server + hosts: dmz-www1 + roles: + - role: apache_vhost + apache_server_name: www.example.com + apache_server_aliases: [example.com] + apache_canonical_hostname: www.example.com + apache_letsencrypt: yes + apache_document_root: /var/www/www.example.com + tags: apache + + - role: archive_job + tags: archive + archive_name: www + archive_shell: >- + TIMESTAMP=$(date +%Y%m%d%H%M%S); + tar czf "www-${TIMESTAMP}.tar.gz" + --transform "s|^\.|www-${TIMESTAMP}|" + -C "{{ apache_public_dir }}" {% for dir in apache_backup_dirs %}{{ dir | quote }} {% endfor %} + + # prosody letsencrypt proxy + - role: prosody_letsencrypt_proxy + prosody_le_role: master + tags: prosody + + tasks: + - name: create webroot + file: + path: /var/www/www.example.com + state: directory + owner: root + group: webmasters + mode: 02770 + tags: apache diff --git a/playbooks/wiki.yml b/playbooks/wiki.yml new file mode 100644 index 0000000..794eb74 --- /dev/null +++ b/playbooks/wiki.yml @@ -0,0 +1,25 @@ +- import_playbook: common.yml + vars: + hostlist: wiki_servers + +- name: configure mediawiki + hosts: wiki_servers + tags: wiki,mediawiki + roles: + - role: mediawiki + + - role: apache_vhost + apache_default_vhost: yes + apache_document_root: '{{ mediawiki_home }}' + apache_config: '{{ mediawiki_apache_config }}' + tags: apache + + - role: php + php_fpm_environment: '{{ mediawiki_php_environment }}' + php_fpm_admin_values: '{{ mediawiki_php_admin_values }}' + tags: php + + - role: archive_job + archive_name: mediawiki + archive_shell: '{{ mediawiki_archive_shell }}' + tags: archive diff --git a/playbooks/xmpp.yml b/playbooks/xmpp.yml new file mode 100644 index 0000000..38d0ce4 --- /dev/null +++ b/playbooks/xmpp.yml @@ -0,0 +1,9 @@ +- import_playbook: common.yml + vars: + hostlist: xmpp_servers + +- name: configure prosody + hosts: xmpp_servers + tags: xmpp,prosody + roles: + - role: prosody diff --git a/playbooks/yum.yml b/playbooks/yum.yml new file mode 100644 index 0000000..e0c829f --- /dev/null +++ b/playbooks/yum.yml @@ -0,0 +1,33 @@ +- import_playbook: common.yml + vars: + hostlist: yum_mirrors + +- name: configure yum mirrors + hosts: yum_mirrors + tags: yum + roles: + - role: yum_mirror + + - role: apache_vhost + apache_default_vhost: yes + apache_document_root: '{{ yum_mirror_webroot }}' + apache_autoindex: yes + apache_redirect_to_https: no + tags: apache + +- name: configure mirror for local packages + hosts: yum_mirrors + tags: yum + roles: + - role: yum_disable_default_repos + + - role: yum + yum_repositories: + - rocky-baseos + - rocky-appstream + - rocky-extras + - epel + + # nagios_client has to run *after* EPEL repository has been configured. + - role: nagios_client + tags: nagios diff --git a/playbooks/znc.yml b/playbooks/znc.yml new file mode 100644 index 0000000..79f3721 --- /dev/null +++ b/playbooks/znc.yml @@ -0,0 +1,14 @@ +- import_playbook: common.yml + vars: + hostlist: znc_servers + +- name: configure znc + hosts: znc_servers + tags: znc + roles: + - role: znc + + - role: archive_job + archive_name: znc + archive_shell: '{{ znc_archive_shell }}' + tags: archive -- cgit