snapshot: add new role with 'snapper' option
btrbk will be next \^*^/ Also: - detect the root filesystem in play with `ansible_mounts` instead of specifying it manually. - dnscrypt: hardcode some privacy settings
This commit is contained in:
parent
cb124aa08f
commit
40ac02c67e
|
@ -5,3 +5,6 @@ skip_list:
|
|||
- package-latest
|
||||
- fqcn[action-core]
|
||||
- name[casing]
|
||||
|
||||
warn_list:
|
||||
- var-naming[no-role-prefix]
|
||||
|
|
13
TODO.md
13
TODO.md
|
@ -1,13 +1,16 @@
|
|||
# TODO
|
||||
# Todo list
|
||||
|
||||
Stuff that are planned to be changed.
|
||||
Stuff that are planned to be added/changed.
|
||||
|
||||
## Configuration
|
||||
|
||||
- [ ] /etc/security/access.conf
|
||||
- [ ] Filesystem snapshot:
|
||||
- [ ] snapper / btrbk (rootfs=btrfs)
|
||||
- [ ] sanoid (rootfs=zfs)
|
||||
- [ ] btrbk (rootfs=btrfs)
|
||||
- [ ] sanoid / zrepl (rootfs=zfs)
|
||||
- [ ] Filesystem backup (I don't have spare hard drives -_- so not supported for now):
|
||||
- [ ] Local incremental backups (to spare disk)
|
||||
- [ ] Remote backups
|
||||
- [ ] incron
|
||||
- [ ] bees
|
||||
- [ ] kea as another option for dhcp client
|
||||
|
@ -17,7 +20,7 @@ Stuff that are planned to be changed.
|
|||
|
||||
## Cosmetic
|
||||
|
||||
- [ ] Packer + Terraform / Pulumi (zfs + btrfs VMs) for testing the playbook
|
||||
- [ ] Packer + Terraform / Pulumi (zfs + btrfs VMs) for testing the playbook (need <https://github.com/digitalocean/go-libvirt/issues/171> implemented first)
|
||||
|
||||
## Just in case I forget
|
||||
|
||||
|
|
|
@ -29,8 +29,6 @@ dns_resolver: dnscrypt-proxy
|
|||
|
||||
repository: https://ftp.udx.icscoe.jp/Linux/alpine
|
||||
|
||||
rootfs: btrfs
|
||||
|
||||
username: follie
|
||||
|
||||
# Don't specify "seat" or "polkitd" group here
|
||||
|
@ -61,9 +59,6 @@ dnscrypt:
|
|||
- quad9-dnscrypt-ip4-filter-pri
|
||||
- cloudflare-security
|
||||
- cloudflare-security-ipv6
|
||||
ephemeral_keys: true
|
||||
tls_disable_session_tickets: true
|
||||
tls_cipher_suite: [52392, 49199]
|
||||
bootstrap_resolvers: [9.9.9.9:53, 1.1.1.1:53]
|
||||
netprobe_address: 1.1.1.1:53
|
||||
local_doh:
|
||||
|
@ -139,6 +134,43 @@ earlyoom:
|
|||
# auditd by default rotates its logfile when reaching file size limit
|
||||
auditd_logrotate_daily: false
|
||||
|
||||
# Configuration for filesystem snapshot tools ─────────────────────────────────
|
||||
# NOTE: these are examples to take note of available options
|
||||
|
||||
snapper:
|
||||
- name: home
|
||||
subvolume: /home
|
||||
pre_post_cleanup:
|
||||
enabled: true
|
||||
number_cleanup:
|
||||
enabled: false
|
||||
timeline:
|
||||
cleanup_enabled: true
|
||||
min_age: 1800
|
||||
hourly: 8
|
||||
daily: 4
|
||||
weekly: 2
|
||||
monthly: 0
|
||||
yearly: 0
|
||||
- name: root
|
||||
subvolume: /
|
||||
pre_post_cleanup:
|
||||
enabled: true
|
||||
min_age: 900
|
||||
number_cleanup:
|
||||
enabled: true
|
||||
min_age: 1800
|
||||
limit: 10-30
|
||||
limit_important: 10
|
||||
timeline:
|
||||
cleanup_enabled: false
|
||||
|
||||
btrbk:
|
||||
|
||||
sanoid:
|
||||
|
||||
zrepl:
|
||||
|
||||
# Secrets encrypted with ansible-vault ────────────────────────────────────────
|
||||
|
||||
password: '{{ vault_password }}'
|
||||
|
|
|
@ -6,7 +6,7 @@ initramfs_generator:
|
|||
|
||||
snapshot_tool: '{{ ["sanoid", "zrepl"] if rootfs == "zfs"
|
||||
else ["snapper", "btrbk"] if rootfs == "btrfs"
|
||||
else ["lvm"] }}'
|
||||
else ["none"] }}'
|
||||
|
||||
# NOTE: Keep this in sync with `shell_mappings` in roles/user/defaults/main.yml
|
||||
usershell:
|
||||
|
|
|
@ -171,12 +171,12 @@ cert_refresh_delay = 240
|
|||
## This may improve privacy but can also have a significant impact on CPU usage
|
||||
## Only enable if you don't have a lot of network load
|
||||
|
||||
dnscrypt_ephemeral_keys = {{ dnscrypt.ephemeral_keys | lower }}
|
||||
dnscrypt_ephemeral_keys = true
|
||||
|
||||
|
||||
## DoH: Disable TLS session tickets - increases privacy but also latency
|
||||
|
||||
tls_disable_session_tickets = {{ dnscrypt.tls_disable_session_tickets | lower }}
|
||||
tls_disable_session_tickets = true
|
||||
|
||||
|
||||
## DoH: Use a specific cipher suite instead of the server preference
|
||||
|
@ -194,7 +194,7 @@ tls_disable_session_tickets = {{ dnscrypt.tls_disable_session_tickets | lower }}
|
|||
## Keep tls_cipher_suite empty if you have issues fetching sources or
|
||||
## connecting to some DoH servers. Google and Cloudflare are fine with it.
|
||||
|
||||
tls_cipher_suite = {{ dnscrypt.tls_cipher_suite }}
|
||||
tls_cipher_suite = [52392, 49199]
|
||||
|
||||
|
||||
## Bootstrap resolvers
|
||||
|
@ -497,7 +497,7 @@ cert_key_file = '{{ ansible_hostname }}.pem'
|
|||
|
||||
[blocked_names]
|
||||
|
||||
{% if dnscrypt.adblock %}
|
||||
{% if (dnscrypt.adblock | bool) %}
|
||||
## Path to the file of blocking rules (absolute, or relative to the same directory as the config file)
|
||||
|
||||
blocked_names_file = '/etc/dnscrypt-proxy/blocked-names.txt'
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
fstype: tmpfs
|
||||
opts: rw,nosuid,nodev,size=4G,mode=1777
|
||||
state: present
|
||||
when: rootfs != 'zfs'
|
||||
|
||||
# /run is mounted with exec by default
|
||||
- name: fstab | Harden mount options for /run
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
- name: ntpd | Adjust ntpd service configuration
|
||||
copy:
|
||||
copy: # noqa: jinja[spacing]
|
||||
content: >
|
||||
NTPD_OPTS="-N
|
||||
{%- for pool in ntp_opts.pools %} -p {{ pool }}{% endfor %}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
- name: Create .snapshots subvolumes manually
|
||||
debug:
|
||||
msg: >
|
||||
Please create .snapshots/ directories and corresponding mounted subvolumes
|
||||
under {{ snapper | map(attribute='subvolume') | join(', ') }} targets
|
||||
manually.
|
|
@ -0,0 +1 @@
|
|||
---
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
- name: snapshot | Setup {{ snapshot_tool }}
|
||||
include_tasks: '{{ snapshot_tool }}.yml'
|
|
@ -0,0 +1 @@
|
|||
---
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
- name: snapper | Install snapper package
|
||||
community.general.apk:
|
||||
name: snapper
|
||||
state: present
|
||||
|
||||
- name: snapper | Install config for each target
|
||||
template:
|
||||
src: snapper.j2
|
||||
dest: /etc/snapper/configs/{{ item.name }}
|
||||
mode: '600'
|
||||
owner: root
|
||||
group: root
|
||||
loop: '{{ snapper }}'
|
||||
|
||||
- name: snapper | Install main snapper config
|
||||
copy:
|
||||
content: |
|
||||
# List of snapper configurations.
|
||||
SNAPPER_CONFIGS="{{ snapper | map(attribute='name') | join(' ') }}"
|
||||
dest: /etc/snapper/snapper
|
||||
mode: '644'
|
||||
owner: root
|
||||
group: root
|
||||
notify:
|
||||
- Create .snapshots subvolumes manually
|
|
@ -0,0 +1 @@
|
|||
---
|
|
@ -0,0 +1,41 @@
|
|||
SUBVOLUME="{{ item.subvolume }}"
|
||||
|
||||
FSTYPE="btrfs"
|
||||
|
||||
# No QGROUP value (yet?) since it's currently troublesome
|
||||
QGROUP=""
|
||||
|
||||
# these are the default settings
|
||||
SPACE_LIMIT="0.5"
|
||||
FREE_LIMIT="0.2"
|
||||
|
||||
# Allow a list of users/groups to operate on this config's snapshots
|
||||
ALLOW_USERS=""
|
||||
ALLOW_GROUPS=""
|
||||
|
||||
SYNC_ACL="no"
|
||||
|
||||
BACKGROUND_COMPARISON="yes"
|
||||
|
||||
# Only "none" or "gzip" (I wish it supported zstd ^-^)
|
||||
COMPRESSION="gzip"
|
||||
|
||||
# Cleaup snapshots based on the number created
|
||||
NUMBER_CLEANUP="{{ item.number_cleanup.enabled | ternary('yes', 'no') }}"
|
||||
NUMBER_MIN_AGE="{{ item.number_cleanup.min_age is defined | ternary(item.number_cleanup.min_age, 1800) }}"
|
||||
NUMBER_LIMIT="{{ item.number_cleanup.limit is defined | ternary(item.number_cleanup.limit, 50) }}"
|
||||
NUMBER_LIMIT_IMPORTANT="{{ item.number_cleanup.limit_important is defined | ternary(item.number_cleanup.limit_important, 10) }}"
|
||||
|
||||
# Configure hourly snapshots
|
||||
TIMELINE_CREATE="yes"
|
||||
TIMELINE_CLEANUP="{{ item.timeline.cleanup_enabled | ternary('yes', 'no') }}"
|
||||
TIMELINE_MIN_AGE="{{ item.timeline.min_age is defined | ternary(item.timeline.min_age, 1800) }}"
|
||||
TIMELINE_LIMIT_HOURLY="{{ item.timeline.hourly is defined | ternary(item.timeline.hourly, 10) }}"
|
||||
TIMELINE_LIMIT_DAILY="{{ item.timeline.daily is defined | ternary(item.timeline.daily, 10) }}"
|
||||
TIMELINE_LIMIT_WEEKLY="{{ item.timeline.weekly is defined | ternary(item.timeline.weekly, 0) }}"
|
||||
TIMELINE_LIMIT_MONTHLY="{{ item.timeline.monthly is defined | ternary(item.timeline.monthly, 10) }}"
|
||||
TIMELINE_LIMIT_YEARLY="{{ item.timeline.yearly is defined | ternary(item.timeline.yearly, 10) }}"
|
||||
|
||||
# Pre-post snapshot pairs
|
||||
EMPTY_PRE_POST_CLEANUP="{{ item.pre_post_cleanup.enabled | ternary('yes', 'no') }}"
|
||||
EMPTY_PRE_POST_MIN_AGE="{{ item.pre_post_cleanup.min_age is defined | ternary(item.pre_post_cleanup.min_age, 1800) }}"
|
48
setup.yml
48
setup.yml
|
@ -1,33 +1,30 @@
|
|||
---
|
||||
- name: Gathering facts
|
||||
hosts: all
|
||||
gather_facts: true
|
||||
tags: always
|
||||
|
||||
- name: Sanity checks
|
||||
hosts: all
|
||||
tags: always
|
||||
tasks:
|
||||
- name: Check user ID
|
||||
fail:
|
||||
msg: This playbook should only be run as 'root'
|
||||
when: ansible_real_user_id != 0
|
||||
- name: Import list of accepted values for defined variables
|
||||
include_vars:
|
||||
name: accepted_values
|
||||
file: ./requirements/accepted_variables.yml
|
||||
- name: Check defined values of top-level variables
|
||||
fail:
|
||||
msg: 'Variable `{{ item }}` needs to be 1 of {{ accepted_values[item] }}'
|
||||
when: not vars[item] in accepted_values[item]
|
||||
loop: '{{ accepted_values | flatten }}'
|
||||
|
||||
- name: Setup the system
|
||||
hosts: all
|
||||
# Hard-coded variables that shouldn't be configured
|
||||
gather_facts: true
|
||||
vars:
|
||||
# Determine the fstype of root filesystem
|
||||
# PERF: a shorter version but requires `py3-jmespath`: '{{ ansible_mounts | json_query("[?mount == `/`].fstype") | first }}'
|
||||
rootfs: '{{ ansible_mounts | selectattr("mount", "equalto", "/") | map(attribute="fstype") | first }}'
|
||||
# elogind needs polkit to function
|
||||
use_polkit: '{{ (seat_manager == "elogind") | ternary("True", polkit) }}'
|
||||
pre_tasks:
|
||||
- name: Sanity checks
|
||||
tags: always
|
||||
block:
|
||||
- name: Check user ID
|
||||
fail:
|
||||
msg: This playbook should be run as 'root'
|
||||
when: ansible_real_user_id != 0
|
||||
- name: Import list of accepted values for custom variables
|
||||
include_vars:
|
||||
name: accepted_values
|
||||
file: ./requirements/accepted_variables.yml
|
||||
- name: Check defined values of top-level variables
|
||||
fail:
|
||||
msg: 'Variable `{{ item }}` needs to be 1 of {{ accepted_values[item] }}'
|
||||
when: not vars[item] in accepted_values[item]
|
||||
loop: '{{ accepted_values | flatten }}'
|
||||
roles:
|
||||
- role: essential
|
||||
tags: essential
|
||||
|
@ -61,6 +58,9 @@
|
|||
tags: usbguard
|
||||
- role: zram
|
||||
tags: zram
|
||||
- role: snapshot
|
||||
tags: snapshot
|
||||
when: snapshot_tool != 'none'
|
||||
- role: earlyoom
|
||||
tags: earlyoom
|
||||
- role: user
|
||||
|
|
Reference in New Issue