From 466fb54aab7eb91cb554a2611518e63ff0e3a20b Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Tue, 20 Dec 2022 19:47:11 +0100 Subject: [PATCH] Initial commit --- README.md | 3 + system/base/fail2ban/meta/argument_specs.yml | 10 ++ system/base/fail2ban/tasks/main.yml | 38 ++++ .../fail2ban/templates/jail.d/sshd.local.j2 | 5 + system/base/fail2ban/templates/jail.local.j2 | 32 ++++ system/base/fstrim/tasks/main.yml | 6 + system/base/logs/files/ignore | 12 ++ system/base/logs/files/logcheck.conf | 96 ++++++++++ system/base/logs/tasks/main.yml | 19 ++ system/base/mail/meta/argument_specs.yml | 25 +++ system/base/mail/tasks/main.yml | 80 +++++++++ system/base/mail/templates/aliases.j2 | 14 ++ system/base/mail/templates/mailname.j2 | 1 + system/base/mail/templates/mailutils.conf.j2 | 3 + system/base/mail/templates/postfix/main.cf.j2 | 58 +++++++ .../mail/templates/postfix/sasl_passwd.j2 | 1 + system/base/motd/meta/argument_specs.yml | 10 ++ system/base/motd/tasks/main.yml | 11 ++ system/base/nftables/defaults/main.yml | 3 + system/base/nftables/meta/argument_specs.yml | 15 ++ system/base/nftables/tasks/main.yml | 29 ++++ .../base/nftables/templates/nftables.conf.j2 | 41 +++++ system/base/ntp/meta/argument_specs.yml | 7 + system/base/ntp/tasks/main.yml | 14 ++ system/base/root/files/su | 61 +++++++ system/base/root/tasks/main.yml | 11 ++ system/base/sshd/defaults/main.yml | 2 + system/base/sshd/meta/argument_specs.yml | 14 ++ system/base/sshd/tasks/main.yml | 28 +++ system/base/sshd/templates/99-local.conf.j2 | 19 ++ .../files/system/status-mail@.service | 7 + .../files/user/status-mail@.service | 6 + .../base/systemd_mail/meta/argument_specs.yml | 7 + system/base/systemd_mail/tasks/main.yml | 33 ++++ .../system/systemd-mail-systemctl-status.j2 | 11 ++ .../user/systemd-mail-systemctl-status.j2 | 11 ++ .../unattended_upgrades/files/20auto-upgrades | 2 + .../files/50unattended-upgrades | 164 ++++++++++++++++++ .../base/unattended_upgrades/tasks/main.yml | 16 ++ system/base/user/files/bashrc | 119 +++++++++++++ system/base/user/files/tmux.conf | 1 + system/base/user/tasks/main.yml | 23 +++ system/base/utils/tasks/main.yml | 13 ++ vpn/base/files/ip-link-add.sh | 8 + vpn/base/tasks/main.yml | 13 ++ vpn/bridge/files/pre-down-br0-inet.nft | 4 + vpn/bridge/files/pre-down-br0-ipv4.nft | 4 + vpn/bridge/meta/argument_specs.yml | 27 +++ vpn/bridge/tasks/main.yml | 52 ++++++ vpn/bridge/templates/br0.j2 | 26 +++ vpn/bridge/templates/post-up-br0-inet.nft.j2 | 5 + vpn/bridge/templates/post-up-br0-ipv4.nft.j2 | 23 +++ vpn/wireguard/files/pre-down-wg0-inet.nft | 4 + vpn/wireguard/files/pre-down-wg0-ipv4.nft | 4 + vpn/wireguard/meta/argument_specs.yml | 42 +++++ vpn/wireguard/tasks/main.yml | 64 +++++++ .../templates/post-up-wg0-inet.nft.j2 | 9 + .../templates/post-up-wg0-ipv4.nft.j2 | 12 ++ vpn/wireguard/templates/wg0.conf.j2 | 27 +++ vpn/wireguard/templates/wg0.j2 | 32 ++++ 60 files changed, 1437 insertions(+) create mode 100644 README.md create mode 100644 system/base/fail2ban/meta/argument_specs.yml create mode 100644 system/base/fail2ban/tasks/main.yml create mode 100644 system/base/fail2ban/templates/jail.d/sshd.local.j2 create mode 100644 system/base/fail2ban/templates/jail.local.j2 create mode 100644 system/base/fstrim/tasks/main.yml create mode 100644 system/base/logs/files/ignore create mode 100644 system/base/logs/files/logcheck.conf create mode 100644 system/base/logs/tasks/main.yml create mode 100644 system/base/mail/meta/argument_specs.yml create mode 100644 system/base/mail/tasks/main.yml create mode 100644 system/base/mail/templates/aliases.j2 create mode 100644 system/base/mail/templates/mailname.j2 create mode 100644 system/base/mail/templates/mailutils.conf.j2 create mode 100644 system/base/mail/templates/postfix/main.cf.j2 create mode 100644 system/base/mail/templates/postfix/sasl_passwd.j2 create mode 100644 system/base/motd/meta/argument_specs.yml create mode 100644 system/base/motd/tasks/main.yml create mode 100644 system/base/nftables/defaults/main.yml create mode 100644 system/base/nftables/meta/argument_specs.yml create mode 100644 system/base/nftables/tasks/main.yml create mode 100755 system/base/nftables/templates/nftables.conf.j2 create mode 100644 system/base/ntp/meta/argument_specs.yml create mode 100644 system/base/ntp/tasks/main.yml create mode 100644 system/base/root/files/su create mode 100644 system/base/root/tasks/main.yml create mode 100644 system/base/sshd/defaults/main.yml create mode 100644 system/base/sshd/meta/argument_specs.yml create mode 100644 system/base/sshd/tasks/main.yml create mode 100644 system/base/sshd/templates/99-local.conf.j2 create mode 100644 system/base/systemd_mail/files/system/status-mail@.service create mode 100644 system/base/systemd_mail/files/user/status-mail@.service create mode 100644 system/base/systemd_mail/meta/argument_specs.yml create mode 100644 system/base/systemd_mail/tasks/main.yml create mode 100644 system/base/systemd_mail/templates/system/systemd-mail-systemctl-status.j2 create mode 100644 system/base/systemd_mail/templates/user/systemd-mail-systemctl-status.j2 create mode 100644 system/base/unattended_upgrades/files/20auto-upgrades create mode 100644 system/base/unattended_upgrades/files/50unattended-upgrades create mode 100644 system/base/unattended_upgrades/tasks/main.yml create mode 100644 system/base/user/files/bashrc create mode 100644 system/base/user/files/tmux.conf create mode 100644 system/base/user/tasks/main.yml create mode 100644 system/base/utils/tasks/main.yml create mode 100644 vpn/base/files/ip-link-add.sh create mode 100644 vpn/base/tasks/main.yml create mode 100644 vpn/bridge/files/pre-down-br0-inet.nft create mode 100644 vpn/bridge/files/pre-down-br0-ipv4.nft create mode 100644 vpn/bridge/meta/argument_specs.yml create mode 100644 vpn/bridge/tasks/main.yml create mode 100644 vpn/bridge/templates/br0.j2 create mode 100644 vpn/bridge/templates/post-up-br0-inet.nft.j2 create mode 100644 vpn/bridge/templates/post-up-br0-ipv4.nft.j2 create mode 100644 vpn/wireguard/files/pre-down-wg0-inet.nft create mode 100644 vpn/wireguard/files/pre-down-wg0-ipv4.nft create mode 100644 vpn/wireguard/meta/argument_specs.yml create mode 100644 vpn/wireguard/tasks/main.yml create mode 100644 vpn/wireguard/templates/post-up-wg0-inet.nft.j2 create mode 100644 vpn/wireguard/templates/post-up-wg0-ipv4.nft.j2 create mode 100644 vpn/wireguard/templates/wg0.conf.j2 create mode 100644 vpn/wireguard/templates/wg0.j2 diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e76b91 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Ansible Roles + +A collection of roles I find useful for different playbooks. diff --git a/system/base/fail2ban/meta/argument_specs.yml b/system/base/fail2ban/meta/argument_specs.yml new file mode 100644 index 0000000..cdecb95 --- /dev/null +++ b/system/base/fail2ban/meta/argument_specs.yml @@ -0,0 +1,10 @@ +--- +argument_specs: + main: + options: + ansible_port: + type: "int" + required: true + system_base_fail2ban_ignoreip: + type: "str" + required: true diff --git a/system/base/fail2ban/tasks/main.yml b/system/base/fail2ban/tasks/main.yml new file mode 100644 index 0000000..720dac5 --- /dev/null +++ b/system/base/fail2ban/tasks/main.yml @@ -0,0 +1,38 @@ +--- +- name: "install fail2ban" + ansible.builtin.apt: + name: "fail2ban" + +- name: "configure fail2ban" + ansible.builtin.template: + src: "./jail.local.j2" + dest: "/etc/fail2ban/jail.local" + mode: 0644 + register: system_base_fail2ban_conf + +- name: "configure fail2ban sshd jail" + ansible.builtin.template: + src: "./jail.d/sshd.local.j2" + dest: "/etc/fail2ban/jail.d/sshd.local" + mode: 0644 + register: system_base_fail2ban_sshd_jail + +- name: "enable fail2ban" + ansible.builtin.systemd: + name: "fail2ban" + enabled: true + +- name: "start fail2ban" + ansible.builtin.systemd: + name: "fail2ban" + state: "started" + register: system_base_fail2ban_start + +- name: "restart fail2ban" + ansible.builtin.systemd: + name: "fail2ban" + state: "restarted" + when: + (system_base_fail2ban_conf.changed or + system_base_fail2ban_sshd_jail.changed) and + not system_base_fail2ban_start.changed diff --git a/system/base/fail2ban/templates/jail.d/sshd.local.j2 b/system/base/fail2ban/templates/jail.d/sshd.local.j2 new file mode 100644 index 0000000..9699bb8 --- /dev/null +++ b/system/base/fail2ban/templates/jail.d/sshd.local.j2 @@ -0,0 +1,5 @@ +[sshd] +enabled = true +port = {{ ansible_port }} +findtime = 1d +bantime = 2w diff --git a/system/base/fail2ban/templates/jail.local.j2 b/system/base/fail2ban/templates/jail.local.j2 new file mode 100644 index 0000000..56b0523 --- /dev/null +++ b/system/base/fail2ban/templates/jail.local.j2 @@ -0,0 +1,32 @@ +[DEFAULT] + +# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban +# will not ban a host which matches an address in this list. Several addresses +# can be defined using space (and/or comma) separator. +ignoreip = 127.0.0.1/8 ::1 {{ system_base_fail2ban_ignoreip }} + +# "bantime" is the number of seconds that a host is banned. +bantime = 1d + +# Destination email address used solely for the interpolations in jail.{conf,local,d/*} +# configuration files. +destemail = root + +# Sender email address used solely for some actions +sender = fail2ban + +# Specify chain where jumps would need to be added in ban-actions expecting parameter chain. Chain +# variable needs to be overridden in jail.local, as the uppercase `chain = INPUT` declaration in +# jail.conf shadows proper lowercase declaration in nftables-common.conf. +chain = input + +# Default banning action (e.g. iptables, iptables-new, iptables-multiport, shorewall, etc) It is +# used to define action_* variables. Can be overridden globally or per section within jail.local +# file. Use nftables instead of iptables. +banaction = nftables[type=multiport] +banaction_allports = nftables[type=allports] + +# Choose default action. To change, just override value of 'action' with the interpolation to the +# chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local globally (section +# [DEFAULT]) or per specific section. +action = %(action_mw)s diff --git a/system/base/fstrim/tasks/main.yml b/system/base/fstrim/tasks/main.yml new file mode 100644 index 0000000..de61297 --- /dev/null +++ b/system/base/fstrim/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: "enable fstrim.timer" + ansible.builtin.systemd: + name: "fstrim.timer" + enabled: true + state: "started" diff --git a/system/base/logs/files/ignore b/system/base/logs/files/ignore new file mode 100644 index 0000000..730cb2d --- /dev/null +++ b/system/base/logs/files/ignore @@ -0,0 +1,12 @@ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ systemd\[[0-9]+\]: (Starting|Stopping) [ +[:alnum:]/\-]+\.(\.\.)?$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ systemd\[[0-9]+\]: Finished (Cleanup of Temporary Directories|Online ext4 Metadata Check for All Filesystems|Podman auto-update service)\.$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ systemd\[[0-9]+\]: (apt-daily\.service|apt-daily-upgrade\.service|man-db\.service|sanoid\.service|syncoid-batch\.service): Consumed ([0-9]{1,2}min )?[0-9]{1,2}\.[0-9]{3}s CPU time\.$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ systemd\[[0-9]+\]: rsyslog\.service: Sent signal SIGHUP to main process [[:digit:]]+ (rsyslogd) on client request\.$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ systemd\[[0-9]+\]: var-lib-containers-storage-overlay\.mount: Succeeded\.$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ sanoid\[[0-9]+\]: INFO: .*$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ sanoid\[[0-9]+\]: taking snapshot .*$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ syncoid-batch\[[0-9]+\]: INFO: .*$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ syncoid-batch\[[0-9]+\]: NEWEST SNAPSHOT: .*$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ syncoid-batch\[[0-9]+\]: Sending incremental .*$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ systemd\[[0-9]+\]: Finished (Snapshot ZFS filesystems|Prune ZFS snapshots|Replicate snapshots using syncoid)\.$ +^[[:alpha:]]{3} [ :[:digit:]]{11} [._[:alnum:]\-]+ kernel: \[[0-9]+\.[0-9]+\] audit: type=1326 audit\([.:0-9]+): auid=[0-9]+ uid=[0-9]+ gid=[0-9]+ ses=[0-9]+ subj=unconfined pid=[0-9]+ comm="git-remote-http" exe="/usr/libexec/git-core/git-remote-https" sig=0 arch=c000003e syscall=324 compat=0 ip=[[:alnum:]]+ code=0x50000$ diff --git a/system/base/logs/files/logcheck.conf b/system/base/logs/files/logcheck.conf new file mode 100644 index 0000000..c9dddeb --- /dev/null +++ b/system/base/logs/files/logcheck.conf @@ -0,0 +1,96 @@ +# The following variable settings are the initial default values, +# which can be uncommented and modified to alter logcheck's behaviour + +# Controls the format of date-/time-stamps in subject lines: +# Alternatively, set the format to suit your locale + +#DATE="$(date +'%Y-%m-%d %H:%M')" + +# Controls the presence of boilerplate at the top of each message: +# Alternatively, set to "0" to disable the introduction. +# +# If the files /etc/logcheck/header.txt and /etc/logcheck/footer.txt +# are present their contents will be read and used as the header and +# footer of any generated mails. + +#INTRO=1 + +# Controls the level of filtering: +# Can be Set to "workstation", "server" or "paranoid" for different +# levels of filtering. Defaults to server if not set. + +REPORTLEVEL="server" + +# Controls the address mail goes to: +# *NOTE* the script does not set a default value for this variable! +# Should be set to an offsite "emailaddress@some.domain.tld" + +SENDMAILTO="root" + +# Send the results as attachment or not. +# 0=not as attachment; 1=as attachment; 2=as gzip attachment +# Default is 0 + +MAILASATTACH=0 + +# Should the hostname in the subject of generated mails be fully qualified? + +FQDN=1 + +# Controls whether "sort -u" is used on log entries (which will +# eliminate duplicates but destroy the original ordering); the +# default is to use "sort -k 1,3 -s": +# Alternatively, set to "1" to enable unique sorting + +#SORTUNIQ=0 + +# Controls whether /etc/logcheck/cracking.ignore.d is scanned for +# exceptions to the rules in /etc/logcheck/cracking.d: +# Alternatively, set to "1" to enable cracking.ignore support + +#SUPPORT_CRACKING_IGNORE=0 + +# Controls the base directory for rules file location +# This must be an absolute path + +#RULEDIR="/etc/logcheck" + +# Controls if syslog-summary is run over each section. +# Alternatively, set to "1" to enable extra summary. +# HINT: syslog-summary needs to be installed. + +#SYSLOGSUMMARY=0 + +# Controls Subject: lines on logcheck reports: + +#ATTACKSUBJECT="Security Alerts" +#SECURITYSUBJECT="Security Events" +#EVENTSSUBJECT="System Events" + +# Controls [logcheck] prefix on Subject: lines + +#ADDTAG="no" + +# Previous versions of logcheck always sent messages in 7bit encoding, +# even if that resulted in RFC-violating messages. For example, really +# long syslog lines would generate too-long SMTP lines, which are +# rejected at least by Debian's default exim configuration. The new +# default is to let mime-construct pick an appropriate encoding, but you +# can override it by setting the below (to any of the encodings +# supported by mime-construct). You may need to do this if you have +# tools handling logcheck emails that don't understand MIME encoding. + +#MIMEENCODING= + +# Set a different location for temporary files than /tmp +# this is useful if your /tmp is small and you are getting +# errors such as: +# cp: writing `/tmp/logcheck.y12449/checked': No space left on device +# /usr/sbin/logcheck: line 161: cannot create temp file for here document: No space left on device +# mail: /tmp/mail.RsXXXXpc2eAx: No space left on device +# Null message body; hope that's ok +# +# If this is happening, likely you will want to change the following to be some other +# location, such as /var/tmp + +TMP="/tmp" diff --git a/system/base/logs/tasks/main.yml b/system/base/logs/tasks/main.yml new file mode 100644 index 0000000..8e0ae84 --- /dev/null +++ b/system/base/logs/tasks/main.yml @@ -0,0 +1,19 @@ +--- +- name: "install logcheck and logrotate" + ansible.builtin.apt: + name: + - "logcheck" + - "logrotate" + +- name: "configure logcheck" + ansible.builtin.copy: + src: "./logcheck.conf" + dest: "/etc/logcheck/logcheck.conf" + mode: 0640 + +- name: "logs : configure logcheck ignores" + ansible.builtin.copy: + src: "./ignore" + dest: "/etc/logcheck/ignore.d.server/{{ ansible_hostname }}" + group: "logcheck" + mode: 0644 diff --git a/system/base/mail/meta/argument_specs.yml b/system/base/mail/meta/argument_specs.yml new file mode 100644 index 0000000..e6d018b --- /dev/null +++ b/system/base/mail/meta/argument_specs.yml @@ -0,0 +1,25 @@ +--- +argument_specs: + main: + options: + ansible_hostname: + type: "str" + required: true + system_base_mail_disable_dns: + type: "bool" + required: true + system_mail_domain: + type: "str" + required: true + system_mail_smtp_server: + type: "str" + required: true + system_mail_smtp_port: + type: "int" + required: true + system_mail_smtp_user: + type: "str" + required: true + system_mail_smtp_pass: + type: "str" + required: true diff --git a/system/base/mail/tasks/main.yml b/system/base/mail/tasks/main.yml new file mode 100644 index 0000000..e5e2017 --- /dev/null +++ b/system/base/mail/tasks/main.yml @@ -0,0 +1,80 @@ +--- +- name: "configure mailname" + ansible.builtin.template: + src: "./mailname.j2" + dest: "/etc/mailname" + mode: 0644 + register: system_mail_mailname + +- name: "configure mailutils" + ansible.builtin.template: + src: "./mailutils.conf.j2" + dest: "/etc/mailutils.conf" + mode: 0644 + +- name: "install postfix" + ansible.builtin.apt: + name: + - "postfix" + - "ca-certificates" + - "libsasl2-modules" + +- name: "configure aliases" + ansible.builtin.template: + src: "./aliases.j2" + dest: "/etc/aliases" + mode: 0644 + register: system_mail_aliases + +- name: "update aliases" + ansible.builtin.command: + cmd: "newaliases" + when: + system_mail_aliases.changed + +- name: "configure postfix" + ansible.builtin.template: + src: "./postfix/main.cf.j2" + dest: "/etc/postfix/main.cf" + mode: 0644 + register: system_mail_postfix_conf + +- name: "configure credentials" + ansible.builtin.template: + src: "./postfix/sasl_passwd.j2" + dest: "/etc/postfix/sasl_passwd" + mode: 0600 + register: system_mail_postfix_credentials + +- name: "create hash database" + ansible.builtin.command: + cmd: "postmap /etc/postfix/sasl_passwd" + when: + system_mail_postfix_credentials.changed + +- name: "set hash database permissions" + ansible.builtin.file: + path: "/etc/postfix/sasl_passwd.db" + mode: 0600 + +- name: "enable postfix" + ansible.builtin.systemd: + name: "postfix" + enabled: true + +- name: "start postfix" + ansible.builtin.systemd: + name: "postfix" + state: "started" + register: system_mail_postfix_start + +- name: "restart postfix" + ansible.builtin.systemd: + name: "postfix" + state: "restarted" + when: + (system_mail_mailname.changed or + system_mail_aliases.changed or + system_mail_postfix_conf.changed or + system_mail_postfix_credentials.changed) and + not system_mail_postfix_start.changed diff --git a/system/base/mail/templates/aliases.j2 b/system/base/mail/templates/aliases.j2 new file mode 100644 index 0000000..7871817 --- /dev/null +++ b/system/base/mail/templates/aliases.j2 @@ -0,0 +1,14 @@ +# /etc/aliases +mailer-daemon: postmaster +postmaster: root +nobody: root +hostmaster: root +usenet: root +news: root +webmaster: root +www: root +ftp: root +abuse: root +noc: root +security: root +root: root@{{ system_mail_domain }} diff --git a/system/base/mail/templates/mailname.j2 b/system/base/mail/templates/mailname.j2 new file mode 100644 index 0000000..7337004 --- /dev/null +++ b/system/base/mail/templates/mailname.j2 @@ -0,0 +1 @@ +{{ ansible_hostname }}.{{ system_mail_domain }} diff --git a/system/base/mail/templates/mailutils.conf.j2 b/system/base/mail/templates/mailutils.conf.j2 new file mode 100644 index 0000000..4816a41 --- /dev/null +++ b/system/base/mail/templates/mailutils.conf.j2 @@ -0,0 +1,3 @@ +address { + email-domain {{ ansible_hostname }}.{{ system_mail_domain }}; +}; diff --git a/system/base/mail/templates/postfix/main.cf.j2 b/system/base/mail/templates/postfix/main.cf.j2 new file mode 100644 index 0000000..053fbee --- /dev/null +++ b/system/base/mail/templates/postfix/main.cf.j2 @@ -0,0 +1,58 @@ +# See /usr/share/postfix/main.cf.dist for a commented, more complete version + + +# Debian specific: Specifying a file name will cause the first +# line of that file to be used as the name. The Debian default +# is /etc/mailname. +myorigin = /etc/mailname + +smtpd_banner = $myhostname ESMTP +biff = no + +# appending .domain is the MUA's job. +append_dot_mydomain = no + +# Uncomment the next line to generate "delayed mail" warnings +#delay_warning_time = 4h + +readme_directory = no + +# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on +# fresh installs. +compatibility_level = 2 + + + +# TLS parameters +smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem +smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key +smtpd_tls_security_level=may + +smtp_tls_CApath=/etc/ssl/certs +smtp_tls_security_level=encrypt +smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache +smtp_tls_wrappermode = yes + + +smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination +myhostname = {{ ansible_hostname }}.{{ system_mail_domain }} +alias_maps = hash:/etc/aliases +alias_database = hash:/etc/aliases +mydestination = $myhostname, {{ ansible_hostname }}, localhost +relayhost = [{{ system_mail_smtp_server }}]:{{ system_mail_smtp_port }} +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 +mailbox_size_limit = 0 +recipient_delimiter = + +inet_interfaces = loopback-only +inet_protocols = all + +# SASL parameters +smtp_sasl_auth_enable = yes +smtp_sasl_security_options = noanonymous +smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd +{% if system_base_mail_disable_dns %} + +smtp_host_lookup=native +disable_dns_lookups = yes +ignore_mx_lookup_error = yes +{% endif %} diff --git a/system/base/mail/templates/postfix/sasl_passwd.j2 b/system/base/mail/templates/postfix/sasl_passwd.j2 new file mode 100644 index 0000000..462cd54 --- /dev/null +++ b/system/base/mail/templates/postfix/sasl_passwd.j2 @@ -0,0 +1 @@ +[{{ system_mail_smtp_server }}]:{{ system_mail_smtp_port }} {{ system_mail_smtp_user }}:{{ system_mail_smtp_pass }} diff --git a/system/base/motd/meta/argument_specs.yml b/system/base/motd/meta/argument_specs.yml new file mode 100644 index 0000000..b1d3163 --- /dev/null +++ b/system/base/motd/meta/argument_specs.yml @@ -0,0 +1,10 @@ +--- +argument_specs: + main: + options: + ansible_hostname: + type: "str" + required: true + system_base_motd_dir: + type: "str" + required: false diff --git a/system/base/motd/tasks/main.yml b/system/base/motd/tasks/main.yml new file mode 100644 index 0000000..090d3b6 --- /dev/null +++ b/system/base/motd/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: "set motd" + ansible.builtin.copy: + src: "{{ item }}" + dest: "/etc/motd" + mode: 0644 + loop: "{{ [lookup('ansible.builtin.first_found', _file_path, skip=true)] | flatten }}" + vars: + _file_path: "{{ system_base_motd_dir }}/{{ ansible_hostname }}" + when: + system_base_motd_dir is defined diff --git a/system/base/nftables/defaults/main.yml b/system/base/nftables/defaults/main.yml new file mode 100644 index 0000000..defedd9 --- /dev/null +++ b/system/base/nftables/defaults/main.yml @@ -0,0 +1,3 @@ +--- +system_base_additional_tcp_ports: [] +system_base_udp_ports: [] diff --git a/system/base/nftables/meta/argument_specs.yml b/system/base/nftables/meta/argument_specs.yml new file mode 100644 index 0000000..f800ce8 --- /dev/null +++ b/system/base/nftables/meta/argument_specs.yml @@ -0,0 +1,15 @@ +--- +argument_specs: + main: + options: + ansible_port: + type: "int" + required: true + system_base_additional_tcp_ports: + type: "list" + elements: "int" + required: true + system_base_udp_ports: + type: "list" + elements: "int" + required: true diff --git a/system/base/nftables/tasks/main.yml b/system/base/nftables/tasks/main.yml new file mode 100644 index 0000000..2fa2ff1 --- /dev/null +++ b/system/base/nftables/tasks/main.yml @@ -0,0 +1,29 @@ +--- +- name: "install nftables" + ansible.builtin.apt: + name: "nftables" + +- name: "configure nftables" + ansible.builtin.template: + src: "./nftables.conf.j2" + dest: "/etc/nftables.conf" + mode: 0755 + register: system_base_nftables_conf + +- name: "enable nftables" + ansible.builtin.systemd: + name: "nftables" + enabled: true + +- name: "start nftables" + ansible.builtin.systemd: + name: "nftables" + state: "started" + register: system_base_nftables_start + +- name: "reload nftables configuration" + ansible.builtin.command: + cmd: "nft -f /etc/nftables.conf" + when: + system_base_nftables_conf.changed and + not system_base_nftables_start.changed diff --git a/system/base/nftables/templates/nftables.conf.j2 b/system/base/nftables/templates/nftables.conf.j2 new file mode 100755 index 0000000..ea73105 --- /dev/null +++ b/system/base/nftables/templates/nftables.conf.j2 @@ -0,0 +1,41 @@ +#!/usr/sbin/nft -f + +table inet filter +delete table inet filter + +table inet filter { + chain input { + type filter hook input priority 0; + + # Accept any localhost traffic. + iif lo accept; + + # Accept traffic originated from us. + ct state established,related accept; + + # Allow ICMP packets. + # Note that for IPv6 nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert are needed to not break connectivity. + ip6 nexthdr icmpv6 icmpv6 type { echo-request, destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept; + ip protocol icmp icmp type { echo-request, destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept; + + # Drop invalid connections. + ct state invalid drop; + + # TCP ports. + tcp dport { {{ [ansible_port] | union(system_base_additional_tcp_ports) | join(", ") }} } ct state new accept; + +{% if system_base_udp_ports %} + # UDP ports. + udp dport { {{ system_base_udp_ports | join(", ") }} } accept; + +{% endif %} + # Count and drop any other traffic. + counter drop; + } + chain forward { + type filter hook forward priority 0; + } + chain output { + type filter hook output priority 0; + } +} diff --git a/system/base/ntp/meta/argument_specs.yml b/system/base/ntp/meta/argument_specs.yml new file mode 100644 index 0000000..b86ba1b --- /dev/null +++ b/system/base/ntp/meta/argument_specs.yml @@ -0,0 +1,7 @@ +--- +argument_specs: + main: + options: + system_base_ntp_timezone: + type: "str" + required: true diff --git a/system/base/ntp/tasks/main.yml b/system/base/ntp/tasks/main.yml new file mode 100644 index 0000000..37f7bd4 --- /dev/null +++ b/system/base/ntp/tasks/main.yml @@ -0,0 +1,14 @@ +--- +- name: "install systemd-timesyncd" + ansible.builtin.apt: + name: "systemd-timesyncd" + +- name: "enable systemd-timesyncd" + ansible.builtin.systemd: + name: "systemd-timesyncd" + enabled: true + state: started + +- name: "set timezone" + community.general.timezone: + name: "{{ system_base_ntp_timezone }}" diff --git a/system/base/root/files/su b/system/base/root/files/su new file mode 100644 index 0000000..6e1ec1d --- /dev/null +++ b/system/base/root/files/su @@ -0,0 +1,61 @@ +# +# The PAM configuration file for the Shadow `su' service +# + +# This allows root to su without passwords (normal operation) +auth sufficient pam_rootok.so + +# Uncomment this to force users to be a member of group wheel +# before they can use `su'. You can also add "group=foo" +# to the end of this line if you want to use a group other +# than the default "wheel" (but this may have side effect of +# denying "root" user, unless she's a member of "foo" or explicitly +# permitted earlier by e.g. "sufficient pam_rootok.so"). +# (Replaces the `SU_WHEEL_ONLY' option from login.defs) +auth required pam_wheel.so + +# Uncomment this if you want wheel members to be able to +# su without a password. +# auth sufficient pam_wheel.so trust + +# Uncomment this if you want members of a specific group to not +# be allowed to use su at all. +# auth required pam_wheel.so deny group=nosu + +# Uncomment and edit /etc/security/time.conf if you need to set +# time restrainst on su usage. +# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs +# as well as /etc/porttime) +# account requisite pam_time.so + +# This module parses environment configuration file(s) +# and also allows you to use an extended config +# file /etc/security/pam_env.conf. +# +# parsing /etc/environment needs "readenv=1" +session required pam_env.so readenv=1 +# locale variables are also kept into /etc/default/locale in etch +# reading this file *in addition to /etc/environment* does not hurt +session required pam_env.so readenv=1 envfile=/etc/default/locale + +# Defines the MAIL environment variable +# However, userdel also needs MAIL_DIR and MAIL_FILE variables +# in /etc/login.defs to make sure that removing a user +# also removes the user's mail spool file. +# See comments in /etc/login.defs +# +# "nopen" stands to avoid reporting new mail when su'ing to another user +session optional pam_mail.so nopen + +# Sets up user limits according to /etc/security/limits.conf +# (Replaces the use of /etc/limits in old login) +session required pam_limits.so + +# The standard Unix authentication modules, used with +# NIS (man nsswitch) as well as normal /etc/passwd and +# /etc/shadow entries. +@include common-auth +@include common-account +@include common-session + + diff --git a/system/base/root/tasks/main.yml b/system/base/root/tasks/main.yml new file mode 100644 index 0000000..304d9cb --- /dev/null +++ b/system/base/root/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: "disable root shell" + ansible.builtin.user: + name: "root" + shell: "/usr/sbin/nologin" + +- name: "disable su for non-wheel users" + ansible.builtin.copy: + src: "./su" + dest: "/etc/pam.d/su" + mode: 0644 diff --git a/system/base/sshd/defaults/main.yml b/system/base/sshd/defaults/main.yml new file mode 100644 index 0000000..6ba68ff --- /dev/null +++ b/system/base/sshd/defaults/main.yml @@ -0,0 +1,2 @@ +--- +system_base_additional_ssh_users: [] diff --git a/system/base/sshd/meta/argument_specs.yml b/system/base/sshd/meta/argument_specs.yml new file mode 100644 index 0000000..c1a296a --- /dev/null +++ b/system/base/sshd/meta/argument_specs.yml @@ -0,0 +1,14 @@ +--- +argument_specs: + main: + options: + ansible_port: + type: "int" + required: true + system_base_ssh_user: + type: "str" + required: true + system_base_additional_ssh_users: + type: "list" + elements: "str" + required: true diff --git a/system/base/sshd/tasks/main.yml b/system/base/sshd/tasks/main.yml new file mode 100644 index 0000000..230a9dc --- /dev/null +++ b/system/base/sshd/tasks/main.yml @@ -0,0 +1,28 @@ +--- +# SSH must be installed so we don't bother with installing it. + +- name: "configure sshd" + ansible.builtin.template: + src: "./99-local.conf.j2" + dest: "/etc/ssh/sshd_config.d/99-local.conf" + mode: 0600 + register: system_base_sshd_conf + +- name: "enable sshd" + ansible.builtin.systemd: + name: "sshd" + enabled: true + +- name: "start sshd" + ansible.builtin.systemd: + name: "sshd" + state: "started" + register: system_base_sshd_start + +- name: "restart sshd" + ansible.builtin.systemd: + name: "sshd" + state: "restarted" + when: + system_base_sshd_conf.changed and + not system_base_sshd_start.changed diff --git a/system/base/sshd/templates/99-local.conf.j2 b/system/base/sshd/templates/99-local.conf.j2 new file mode 100644 index 0000000..173a7f7 --- /dev/null +++ b/system/base/sshd/templates/99-local.conf.j2 @@ -0,0 +1,19 @@ +# SSH daemon configuration. Note that sshd_config(5) states "For each keyword, the first obtained +# value will be used." This is why files < 00 which are read earlier override the settings below. + +Port {{ ansible_port }} + +# Completely disable root login via ssh. +PermitRootLogin no + +# Explicitly set the list of allowed ssh users. +AllowUsers {{ [system_base_ssh_user] | union(system_base_additional_ssh_users) | join(" ") }} + +# SSH enabled only via ssh-key. +PasswordAuthentication no + +# No X window forwarding. +X11Forwarding no + +# Check in with the client every now and then. +ClientAliveInterval 120 diff --git a/system/base/systemd_mail/files/system/status-mail@.service b/system/base/systemd_mail/files/system/status-mail@.service new file mode 100644 index 0000000..989dcc4 --- /dev/null +++ b/system/base/systemd_mail/files/system/status-mail@.service @@ -0,0 +1,7 @@ +[Unit] +Description=Status email for %i + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/systemd-mail-systemctl-status %i +Group=systemd-journal diff --git a/system/base/systemd_mail/files/user/status-mail@.service b/system/base/systemd_mail/files/user/status-mail@.service new file mode 100644 index 0000000..6fb82f9 --- /dev/null +++ b/system/base/systemd_mail/files/user/status-mail@.service @@ -0,0 +1,6 @@ +[Unit] +Description=Status email for %i + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/systemd-mail-systemctl-status %i diff --git a/system/base/systemd_mail/meta/argument_specs.yml b/system/base/systemd_mail/meta/argument_specs.yml new file mode 100644 index 0000000..70c76c4 --- /dev/null +++ b/system/base/systemd_mail/meta/argument_specs.yml @@ -0,0 +1,7 @@ +--- +argument_specs: + main: + options: + ansible_hostname: + type: "str" + required: true diff --git a/system/base/systemd_mail/tasks/main.yml b/system/base/systemd_mail/tasks/main.yml new file mode 100644 index 0000000..1b84d7f --- /dev/null +++ b/system/base/systemd_mail/tasks/main.yml @@ -0,0 +1,33 @@ +--- +- name: "systemd mail root script" + ansible.builtin.template: + src: "./system/systemd-mail-systemctl-status.j2" + dest: "/usr/local/sbin/systemd-mail-systemctl-status" + mode: 0755 + +- name: "systemd mail user script" + ansible.builtin.template: + src: "./user/systemd-mail-systemctl-status.j2" + dest: "/usr/local/bin/systemd-mail-systemctl-status" + mode: 0755 + +- name: "systemd mail root service" + ansible.builtin.copy: + src: "./system/status-mail@.service" + dest: "/etc/systemd/system/status-mail@.service" + mode: 0644 + register: system_base_system_status_mail_service_file + +- name: "systemd mail user service" + ansible.builtin.copy: + src: "./user/status-mail@.service" + dest: "/etc/systemd/user/status-mail@.service" + mode: 0644 + register: system_base_user_status_mail_service_file + +- name: "systemd daemon reload" + ansible.builtin.systemd: + daemon_reload: true + when: + system_base_system_status_mail_service_file.changed or + system_base_user_status_mail_service_file.changed diff --git a/system/base/systemd_mail/templates/system/systemd-mail-systemctl-status.j2 b/system/base/systemd_mail/templates/system/systemd-mail-systemctl-status.j2 new file mode 100644 index 0000000..938705e --- /dev/null +++ b/system/base/systemd_mail/templates/system/systemd-mail-systemctl-status.j2 @@ -0,0 +1,11 @@ +#!/bin/sh + +/usr/sbin/sendmail -t < +Subject: systemctl status $1 on {{ ansible_hostname }} +Content-Transfer-Encoding: 8bit +Content-Type: text/plain; charset=UTF-8 + +$(systemctl status --full "$1") +SYSTEMDMAIL diff --git a/system/base/systemd_mail/templates/user/systemd-mail-systemctl-status.j2 b/system/base/systemd_mail/templates/user/systemd-mail-systemctl-status.j2 new file mode 100644 index 0000000..21f87a0 --- /dev/null +++ b/system/base/systemd_mail/templates/user/systemd-mail-systemctl-status.j2 @@ -0,0 +1,11 @@ +#!/bin/sh + +/usr/sbin/sendmail -t < +Subject: systemctl --user status $1 on {{ ansible_hostname }} +Content-Transfer-Encoding: 8bit +Content-Type: text/plain; charset=UTF-8 + +$(systemctl --user status --full "$1") +SYSTEMDMAIL diff --git a/system/base/unattended_upgrades/files/20auto-upgrades b/system/base/unattended_upgrades/files/20auto-upgrades new file mode 100644 index 0000000..8d6d7c8 --- /dev/null +++ b/system/base/unattended_upgrades/files/20auto-upgrades @@ -0,0 +1,2 @@ +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; diff --git a/system/base/unattended_upgrades/files/50unattended-upgrades b/system/base/unattended_upgrades/files/50unattended-upgrades new file mode 100644 index 0000000..69ad7b6 --- /dev/null +++ b/system/base/unattended_upgrades/files/50unattended-upgrades @@ -0,0 +1,164 @@ +// Unattended-Upgrade::Origins-Pattern controls which packages are +// upgraded. +// +// Lines below have the format "keyword=value,...". A +// package will be upgraded only if the values in its metadata match +// all the supplied keywords in a line. (In other words, omitted +// keywords are wild cards.) The keywords originate from the Release +// file, but several aliases are accepted. The accepted keywords are: +// a,archive,suite (eg, "stable") +// c,component (eg, "main", "contrib", "non-free") +// l,label (eg, "Debian", "Debian-Security") +// o,origin (eg, "Debian", "Unofficial Multimedia Packages") +// n,codename (eg, "jessie", "jessie-updates") +// site (eg, "http.debian.net") +// The available values on the system are printed by the command +// "apt-cache policy", and can be debugged by running +// "unattended-upgrades -d" and looking at the log file. +// +// Within lines unattended-upgrades allows 2 macros whose values are +// derived from /etc/debian_version: +// ${distro_id} Installed origin. +// ${distro_codename} Installed codename (eg, "buster") +Unattended-Upgrade::Origins-Pattern { + // Codename based matching: + // This will follow the migration of a release through different + // archives (e.g. from testing to stable and later oldstable). + // Software will be the latest available for the named release, + // but the Debian release itself will not be automatically upgraded. +// "origin=Debian,codename=${distro_codename}-updates"; +// "origin=Debian,codename=${distro_codename}-proposed-updates"; + "origin=Debian,codename=${distro_codename},label=Debian"; + "origin=Debian,codename=${distro_codename},label=Debian-Security"; + "origin=Debian,codename=${distro_codename}-security,label=Debian-Security"; + + // Archive or Suite based matching: + // Note that this will silently match a different release after + // migration to the specified archive (e.g. testing becomes the + // new stable). +// "o=Debian,a=stable"; +// "o=Debian,a=stable-updates"; +// "o=Debian,a=proposed-updates"; +// "o=Debian Backports,a=${distro_codename}-backports,l=Debian Backports"; +}; + +// Python regular expressions, matching packages to exclude from upgrading +Unattended-Upgrade::Package-Blacklist { + // The following matches all packages starting with linux- +// "linux-"; + + // Use $ to explicitely define the end of a package name. Without + // the $, "libc6" would match all of them. +// "libc6$"; +// "libc6-dev$"; +// "libc6-i686$"; + + // Special characters need escaping +// "libstdc\+\+6$"; + + // The following matches packages like xen-system-amd64, xen-utils-4.1, + // xenstore-utils and libxenstore3.0 +// "(lib)?xen(store)?"; + + // For more information about Python regular expressions, see + // https://docs.python.org/3/howto/regex.html +}; + +// This option allows you to control if on a unclean dpkg exit +// unattended-upgrades will automatically run +// dpkg --force-confold --configure -a +// The default is true, to ensure updates keep getting installed +//Unattended-Upgrade::AutoFixInterruptedDpkg "true"; + +// Split the upgrade into the smallest possible chunks so that +// they can be interrupted with SIGTERM. This makes the upgrade +// a bit slower but it has the benefit that shutdown while a upgrade +// is running is possible (with a small delay) +//Unattended-Upgrade::MinimalSteps "true"; + +// Install all updates when the machine is shutting down +// instead of doing it in the background while the machine is running. +// This will (obviously) make shutdown slower. +// Unattended-upgrades increases logind's InhibitDelayMaxSec to 30s. +// This allows more time for unattended-upgrades to shut down gracefully +// or even install a few packages in InstallOnShutdown mode, but is still a +// big step back from the 30 minutes allowed for InstallOnShutdown previously. +// Users enabling InstallOnShutdown mode are advised to increase +// InhibitDelayMaxSec even further, possibly to 30 minutes. +//Unattended-Upgrade::InstallOnShutdown "false"; + +// Send email to this address for problems or packages upgrades +// If empty or unset then no email is sent, make sure that you +// have a working mail setup on your system. A package that provides +// 'mailx' must be installed. E.g. "user@example.com" +Unattended-Upgrade::Mail "root"; + +// Set this value to one of: +// "always", "only-on-error" or "on-change" +// If this is not set, then any legacy MailOnlyOnError (boolean) value +// is used to chose between "only-on-error" and "on-change" +//Unattended-Upgrade::MailReport "on-change"; + +// Remove unused automatically installed kernel-related packages +// (kernel images, kernel headers and kernel version locked tools). +//Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; + +// Do automatic removal of newly unused dependencies after the upgrade +//Unattended-Upgrade::Remove-New-Unused-Dependencies "true"; + +// Do automatic removal of unused packages after the upgrade +// (equivalent to apt-get autoremove) +//Unattended-Upgrade::Remove-Unused-Dependencies "false"; + +// Automatically reboot *WITHOUT CONFIRMATION* if +// the file /var/run/reboot-required is found after the upgrade +//Unattended-Upgrade::Automatic-Reboot "false"; + +// Automatically reboot even if there are users currently logged in +// when Unattended-Upgrade::Automatic-Reboot is set to true +//Unattended-Upgrade::Automatic-Reboot-WithUsers "true"; + +// If automatic reboot is enabled and needed, reboot at the specific +// time instead of immediately +// Default: "now" +//Unattended-Upgrade::Automatic-Reboot-Time "02:00"; + +// Use apt bandwidth limit feature, this example limits the download +// speed to 70kb/sec +//Acquire::http::Dl-Limit "70"; + +// Enable logging to syslog. Default is False +// Unattended-Upgrade::SyslogEnable "false"; + +// Specify syslog facility. Default is daemon +// Unattended-Upgrade::SyslogFacility "daemon"; + +// Download and install upgrades only on AC power +// (i.e. skip or gracefully stop updates on battery) +// Unattended-Upgrade::OnlyOnACPower "true"; + +// Download and install upgrades only on non-metered connection +// (i.e. skip or gracefully stop updates on a metered connection) +// Unattended-Upgrade::Skip-Updates-On-Metered-Connections "true"; + +// Verbose logging +// Unattended-Upgrade::Verbose "false"; + +// Print debugging information both in unattended-upgrades and +// in unattended-upgrade-shutdown +// Unattended-Upgrade::Debug "false"; + +// Allow package downgrade if Pin-Priority exceeds 1000 +// Unattended-Upgrade::Allow-downgrade "false"; + +// When APT fails to mark a package to be upgraded or installed try adjusting +// candidates of related packages to help APT's resolver in finding a solution +// where the package can be upgraded or installed. +// This is a workaround until APT's resolver is fixed to always find a +// solution if it exists. (See Debian bug #711128.) +// The fallback is enabled by default, except on Debian's sid release because +// uninstallable packages are frequent there. +// Disabling the fallback speeds up unattended-upgrades when there are +// uninstallable packages at the expense of rarely keeping back packages which +// could be upgraded or installed. +// Unattended-Upgrade::Allow-APT-Mark-Fallback "true"; diff --git a/system/base/unattended_upgrades/tasks/main.yml b/system/base/unattended_upgrades/tasks/main.yml new file mode 100644 index 0000000..ee77d8b --- /dev/null +++ b/system/base/unattended_upgrades/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: "install unattended-upgrades" + ansible.builtin.apt: + name: "unattended-upgrades" + +- name: "configure unattended-upgrades" + ansible.builtin.copy: + src: "./50unattended-upgrades" + dest: "/etc/apt/apt.conf.d/50unattended-upgrades" + mode: 0644 + +- name: "enable unattended-upgrades" + ansible.builtin.copy: + src: "./20auto-upgrades" + dest: "/etc/apt/apt.conf.d/20auto-upgrades" + mode: 0644 diff --git a/system/base/user/files/bashrc b/system/base/user/files/bashrc new file mode 100644 index 0000000..7c143a2 --- /dev/null +++ b/system/base/user/files/bashrc @@ -0,0 +1,119 @@ +# ~/.bashrc: executed by bash(1) for non-login shells. +# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) +# for examples + +# If not running interactively, don't do anything +case $- in + *i*) ;; + *) return;; +esac + +# TMUX +if which tmux >/dev/null 2>&1; then + # If not inside a tmux session, and if no session is started, start a new session + test -z "$TMUX" && (tmux attach || tmux new-session) +fi + +# don't put duplicate lines or lines starting with space in the history. +# See bash(1) for more options +HISTCONTROL=ignoreboth + +# append to the history file, don't overwrite it +shopt -s histappend + +# for setting history length see HISTSIZE and HISTFILESIZE in bash(1) +HISTSIZE=1000 +HISTFILESIZE=2000 + +# check the window size after each command and, if necessary, +# update the values of LINES and COLUMNS. +shopt -s checkwinsize + +# If set, the pattern "**" used in a pathname expansion context will +# match all files and zero or more directories and subdirectories. +#shopt -s globstar + +# make less more friendly for non-text input files, see lesspipe(1) +#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" + +# set variable identifying the chroot you work in (used in the prompt below) +if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then + debian_chroot=$(cat /etc/debian_chroot) +fi + +# set a fancy prompt (non-color, unless we know we "want" color) +case "$TERM" in + xterm-color|*-256color) color_prompt=yes;; +esac + +# uncomment for a colored prompt, if the terminal has the capability; turned +# off by default to not distract the user: the focus in a terminal window +# should be on the output of commands, not on the prompt +#force_color_prompt=yes + +if [ -n "$force_color_prompt" ]; then + if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then + # We have color support; assume it's compliant with Ecma-48 + # (ISO/IEC-6429). (Lack of such support is extremely rare, and such + # a case would tend to support setf rather than setaf.) + color_prompt=yes + else + color_prompt= + fi +fi + +if [ "$color_prompt" = yes ]; then + PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' +else + PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' +fi +unset color_prompt force_color_prompt + +# If this is an xterm set the title to user@host:dir +case "$TERM" in +xterm*|rxvt*) + PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" + ;; +*) + ;; +esac + +# enable color support of ls and also add handy aliases +if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + #alias dir='dir --color=auto' + #alias vdir='vdir --color=auto' + + #alias grep='grep --color=auto' + #alias fgrep='fgrep --color=auto' + #alias egrep='egrep --color=auto' +fi + +# colored GCC warnings and errors +#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' + +# some more ls aliases +#alias ll='ls -l' +#alias la='ls -A' +#alias l='ls -CF' + +# Alias definitions. +# You may want to put all your additions into a separate file like +# ~/.bash_aliases, instead of adding them here directly. +# See /usr/share/doc/bash-doc/examples in the bash-doc package. + +if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases +fi + +# enable programmable completion features (you don't need to enable +# this, if it's already enabled in /etc/bash.bashrc and /etc/profile +# sources /etc/bash.bashrc). +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi diff --git a/system/base/user/files/tmux.conf b/system/base/user/files/tmux.conf new file mode 100644 index 0000000..7d95498 --- /dev/null +++ b/system/base/user/files/tmux.conf @@ -0,0 +1 @@ +source ~/.tmux/tmux.conf diff --git a/system/base/user/tasks/main.yml b/system/base/user/tasks/main.yml new file mode 100644 index 0000000..d857c1a --- /dev/null +++ b/system/base/user/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- block: + + - name: "clone tmux dotfiles" + ansible.builtin.git: + repo: "https://git.wojciechkozlowski.eu/config/tmux.git" + dest: ".tmux" + recursive: true + + # On first tmux launch install plugins with + - name: "configure tmux" + ansible.builtin.copy: + src: "./tmux.conf" + dest: ".tmux.conf" + mode: 0644 + + - name: "configure bashrc" + ansible.builtin.copy: + src: "./bashrc" + dest: ".bashrc" + mode: 0644 + + become: false diff --git a/system/base/utils/tasks/main.yml b/system/base/utils/tasks/main.yml new file mode 100644 index 0000000..01a87af --- /dev/null +++ b/system/base/utils/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: "install utility programs" + ansible.builtin.apt: + name: + - "acl" + - "git" + - "htop" + - "man" + - "perl" + - "rsync" + - "tmux" + - "tcpdump" + - "traceroute" diff --git a/vpn/base/files/ip-link-add.sh b/vpn/base/files/ip-link-add.sh new file mode 100644 index 0000000..547b866 --- /dev/null +++ b/vpn/base/files/ip-link-add.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -u + +if ! ip link show dev "${1}" > /dev/null 2>&1 +then + ip link add "${@}" +fi diff --git a/vpn/base/tasks/main.yml b/vpn/base/tasks/main.yml new file mode 100644 index 0000000..84c6f19 --- /dev/null +++ b/vpn/base/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: "enable ipv4 forwarding" + ansible.posix.sysctl: + name: "net.ipv4.ip_forward" + value: "1" + sysctl_file: "/etc/sysctl.d/local.conf" + reload: true + +- name: "script for creating virtual interfaces" + ansible.builtin.copy: + src: "./ip-link-add.sh" + dest: "/usr/local/sbin/ip-link-add.sh" + mode: 0755 diff --git a/vpn/bridge/files/pre-down-br0-inet.nft b/vpn/bridge/files/pre-down-br0-inet.nft new file mode 100644 index 0000000..e7b5064 --- /dev/null +++ b/vpn/bridge/files/pre-down-br0-inet.nft @@ -0,0 +1,4 @@ +#!/usr/bin/env -S nft -f + +flush table inet br0_inet +delete table inet br0_inet diff --git a/vpn/bridge/files/pre-down-br0-ipv4.nft b/vpn/bridge/files/pre-down-br0-ipv4.nft new file mode 100644 index 0000000..34d95a9 --- /dev/null +++ b/vpn/bridge/files/pre-down-br0-ipv4.nft @@ -0,0 +1,4 @@ +#!/usr/bin/env -S nft -f + +flush table ip br0_ipv4 +delete table ip br0_ipv4 diff --git a/vpn/bridge/meta/argument_specs.yml b/vpn/bridge/meta/argument_specs.yml new file mode 100644 index 0000000..1e9df8f --- /dev/null +++ b/vpn/bridge/meta/argument_specs.yml @@ -0,0 +1,27 @@ +--- +argument_specs: + main: + options: + ansible_default_ipv4: + interface: + type: "str" + required: true + local_network: + type: "str" + required: false + vpn_bridge_dnat: + type: "list" + elements: "dict" + required: true + vpn_bridge_address: + type: "str" + required: true + vpn_bridge_broadcast: + type: "str" + required: true + vpn_bridge_netmask: + type: "str" + required: true + vpn_bridge_routing_table: + type: "int" + required: false diff --git a/vpn/bridge/tasks/main.yml b/vpn/bridge/tasks/main.yml new file mode 100644 index 0000000..499eb60 --- /dev/null +++ b/vpn/bridge/tasks/main.yml @@ -0,0 +1,52 @@ +--- +- name: "post-up nftables inet script" + ansible.builtin.template: + src: "./post-up-br0-inet.nft.j2" + dest: "/usr/local/sbin/post-up-br0-inet.nft" + mode: 0755 + register: vpn_bridge_post_up_br0_inet_nft + +- name: "post-up nftables ipv4 script" + ansible.builtin.template: + src: "./post-up-br0-ipv4.nft.j2" + dest: "/usr/local/sbin/post-up-br0-ipv4.nft" + mode: 0755 + register: vpn_bridge_post_up_br0_ipv4_nft + +- name: "configure interface" + ansible.builtin.template: + src: "./br0.j2" + dest: "/etc/network/interfaces.d/br0" + mode: 0644 + validate: > + bash -c + 'if ! diff %s /etc/network/interfaces.d/br0 && ip link show dev br0 ; + then + ifdown br0 ; + fi' + register: vpn_bridge_intf + +- name: "restart interface" + ansible.builtin.shell: | + if ip link show dev br0 + then + ifdown br0 && ifup br0 + else + ifup br0 + fi + when: + vpn_bridge_post_up_br0_inet_nft.changed or + vpn_bridge_post_up_br0_ipv4_nft.changed or + vpn_bridge_intf.changed + +- name: "pre-down nftables inet script" + ansible.builtin.copy: + src: "./pre-down-br0-inet.nft" + dest: "/usr/local/sbin/pre-down-br0-inet.nft" + mode: 0755 + +- name: "pre-down nftables ipv4 script" + ansible.builtin.copy: + src: "./pre-down-br0-ipv4.nft" + dest: "/usr/local/sbin/pre-down-br0-ipv4.nft" + mode: 0755 diff --git a/vpn/bridge/templates/br0.j2 b/vpn/bridge/templates/br0.j2 new file mode 100644 index 0000000..ae777d2 --- /dev/null +++ b/vpn/bridge/templates/br0.j2 @@ -0,0 +1,26 @@ +auto br0 +iface br0 inet static + pre-up /usr/local/sbin/ip-link-add.sh $IFACE type bridge + + post-up /usr/local/sbin/post-up-$IFACE-inet.nft + post-up /usr/local/sbin/post-up-$IFACE-ipv4.nft +{% if vpn_bridge_routing_table is defined %} + post-up ip rule add dev $IFACE table {{ vpn_bridge_routing_table }} + post-up ip rule add dev $IFACE to {{ local_network }} table main priority 1 +{% endif %} + +{% if vpn_bridge_routing_table is defined %} + pre-down ip rule del dev $IFACE to {{ local_network }} table main priority 1 + pre-down ip rule del dev $IFACE table {{ vpn_bridge_routing_table }} +{% endif %} + pre-down /usr/local/sbin/pre-down-$IFACE-ipv4.nft + pre-down /usr/local/sbin/pre-down-$IFACE-inet.nft + + bridge_stp off + bridge_waitport 0 + bridge_fd 0 + bridge_ports none + + address {{ vpn_bridge_address }} + broadcast {{ vpn_bridge_broadcast }} + netmask {{ vpn_bridge_netmask }} diff --git a/vpn/bridge/templates/post-up-br0-inet.nft.j2 b/vpn/bridge/templates/post-up-br0-inet.nft.j2 new file mode 100644 index 0000000..ba234a7 --- /dev/null +++ b/vpn/bridge/templates/post-up-br0-inet.nft.j2 @@ -0,0 +1,5 @@ +#!/usr/bin/env -S nft -f + +table inet br0_inet { + +} diff --git a/vpn/bridge/templates/post-up-br0-ipv4.nft.j2 b/vpn/bridge/templates/post-up-br0-ipv4.nft.j2 new file mode 100644 index 0000000..70484bc --- /dev/null +++ b/vpn/bridge/templates/post-up-br0-ipv4.nft.j2 @@ -0,0 +1,23 @@ +#!/usr/bin/env -S nft -f + +table ip br0_ipv4 { + chain prerouting { + type nat hook prerouting priority -100; +{% for forward in vpn_bridge_dnat %} + iif {{ ansible_default_ipv4.interface }} tcp dport { {{ forward.ports | join(", ") }} } dnat to {{ forward.address }}; +{% endfor %} + } + +{% if local_network is defined %} + chain input { + type filter hook input priority 0; + ct state established,related accept; + iif br0 ip daddr {{ local_network }} drop; + } + +{% endif %} + chain postrouting { + type nat hook postrouting priority 100; + iif br0 oif {{ ansible_default_ipv4.interface }} masquerade; + } +} diff --git a/vpn/wireguard/files/pre-down-wg0-inet.nft b/vpn/wireguard/files/pre-down-wg0-inet.nft new file mode 100644 index 0000000..27813e2 --- /dev/null +++ b/vpn/wireguard/files/pre-down-wg0-inet.nft @@ -0,0 +1,4 @@ +#!/usr/bin/env -S nft -f + +flush table inet wg0_inet +delete table inet wg0_inet diff --git a/vpn/wireguard/files/pre-down-wg0-ipv4.nft b/vpn/wireguard/files/pre-down-wg0-ipv4.nft new file mode 100644 index 0000000..5f6b6b0 --- /dev/null +++ b/vpn/wireguard/files/pre-down-wg0-ipv4.nft @@ -0,0 +1,4 @@ +#!/usr/bin/env -S nft -f + +flush table ip wg0_ipv4 +delete table ip wg0_ipv4 diff --git a/vpn/wireguard/meta/argument_specs.yml b/vpn/wireguard/meta/argument_specs.yml new file mode 100644 index 0000000..f0ea2a1 --- /dev/null +++ b/vpn/wireguard/meta/argument_specs.yml @@ -0,0 +1,42 @@ +--- +argument_specs: + main: + options: + ansible_default_ipv4: + interface: + type: "str" + required: true + vpn_wireguard_role: + type: "str" + required: true + vpn_wireguard_address: + type: "str" + required: true + vpn_wireguard_netmask: + type: "str" + required: true + vpn_wireguard_port: + type: "int" + required: true + vpn_wireguard_interface_private_key: + type: "str" + required: true + vpn_wireguard_subnet: + type: "str" + required: false + vpn_wireguard_clients: + type: "list" + elem: "dict" + required: "{{ vpn_wireguard_role == 'server' }}" + vpn_wireguard_routing_table: + type: "int" + required: "{{ vpn_wireguard_role == 'client' }}" + vpn_wireguard_server_public_key: + type: "str" + required: "{{ vpn_wireguard_role == 'client' }}" + vpn_wireguard_server_preshared_key: + type: "str" + required: "{{ vpn_wireguard_role == 'client' }}" + vpn_wireguard_server_address: + type: "str" + required: "{{ vpn_wireguard_role == 'client' }}" diff --git a/vpn/wireguard/tasks/main.yml b/vpn/wireguard/tasks/main.yml new file mode 100644 index 0000000..14dee29 --- /dev/null +++ b/vpn/wireguard/tasks/main.yml @@ -0,0 +1,64 @@ +--- +- name: "install wireguard" + ansible.builtin.apt: + name: "wireguard" + +- name: "configure wireguard" + ansible.builtin.template: + src: "./wg0.conf.j2" + dest: "/etc/wireguard/wg0.conf" + mode: 0600 + register: vpn_wireguard_conf + +- name: "post-up nftables inet script" + ansible.builtin.template: + src: "./post-up-wg0-inet.nft.j2" + dest: "/usr/local/sbin/post-up-wg0-inet.nft" + mode: 0755 + register: vpn_wireguard_post_up_wg0_inet_nft + +- name: "post-up nftables ipv4 script" + ansible.builtin.template: + src: "./post-up-wg0-ipv4.nft.j2" + dest: "/usr/local/sbin/post-up-wg0-ipv4.nft" + mode: 0755 + register: vpn_wireguard_post_up_wg0_ipv4_nft + +- name: "configure interface" + ansible.builtin.template: + src: "./wg0.j2" + dest: "/etc/network/interfaces.d/wg0" + mode: 0644 + validate: > + bash -c + 'if ! diff %s /etc/network/interfaces.d/wg0 && ip link show dev wg0 ; + then + ifdown wg0 ; + fi' + register: vpn_wireguard_intf + +- name: "restart interface" + ansible.builtin.shell: | + if ip link show dev wg0 + then + ifdown wg0 && ifup wg0 + else + ifup wg0 + fi + when: + vpn_wireguard_conf.changed or + vpn_wireguard_post_up_wg0_inet_nft.changed or + vpn_wireguard_post_up_wg0_ipv4_nft.changed or + vpn_wireguard_intf.changed + +- name: "pre-down nftables inet script" + ansible.builtin.copy: + src: "./pre-down-wg0-inet.nft" + dest: "/usr/local/sbin/pre-down-wg0-inet.nft" + mode: 0755 + +- name: "pre-down nftables ipv4 script" + ansible.builtin.copy: + src: "./pre-down-wg0-ipv4.nft" + dest: "/usr/local/sbin/pre-down-wg0-ipv4.nft" + mode: 0755 diff --git a/vpn/wireguard/templates/post-up-wg0-inet.nft.j2 b/vpn/wireguard/templates/post-up-wg0-inet.nft.j2 new file mode 100644 index 0000000..dfd4b1d --- /dev/null +++ b/vpn/wireguard/templates/post-up-wg0-inet.nft.j2 @@ -0,0 +1,9 @@ +#!/usr/bin/env -S nft -f + +table inet wg0_inet { + chain forward { + type filter hook forward priority 0; + iif wg0 tcp flags syn tcp option maxseg size set rt mtu; + oif wg0 tcp flags syn tcp option maxseg size set rt mtu; + } +} diff --git a/vpn/wireguard/templates/post-up-wg0-ipv4.nft.j2 b/vpn/wireguard/templates/post-up-wg0-ipv4.nft.j2 new file mode 100644 index 0000000..a682238 --- /dev/null +++ b/vpn/wireguard/templates/post-up-wg0-ipv4.nft.j2 @@ -0,0 +1,12 @@ +#!/usr/bin/env -S nft -f + +table ip wg0_ipv4 { + +{% if vpn_wireguard_role == "server" %} + chain postrouting { + type nat hook postrouting priority 100; + iif wg0 oif {{ ansible_default_ipv4.interface }} masquerade; + } + +{% endif %} +} diff --git a/vpn/wireguard/templates/wg0.conf.j2 b/vpn/wireguard/templates/wg0.conf.j2 new file mode 100644 index 0000000..9448591 --- /dev/null +++ b/vpn/wireguard/templates/wg0.conf.j2 @@ -0,0 +1,27 @@ +[Interface] +PrivateKey = {{ vpn_wireguard_interface_private_key }} +{% if vpn_wireguard_role == "server" %} +ListenPort = {{ vpn_wireguard_port }} +{% endif %} + +{% if vpn_wireguard_role == "server" %} +{% for client in vpn_wireguard_clients %} +[Peer] +PublicKey = {{ client.public_key }} +PresharedKey = {{ client.preshared_key }} +{% if 'subnet' in client %} +AllowedIPs = {{ vpn_wireguard_subnet }},{{ client.subnet }} +{% else %} +AllowedIPs = {{ vpn_wireguard_subnet }} +{% endif %} + +{% endfor %} +{% elif vpn_wireguard_role == "client" %} +[Peer] +PublicKey = {{ vpn_wireguard_server_public_key }} +PresharedKey = {{ vpn_wireguard_server_preshared_key }} +Endpoint = {{ vpn_wireguard_server_address }}:{{ vpn_wireguard_port }} +AllowedIPs = 0.0.0.0/0 +PersistentKeepalive = 15 + +{% endif %} diff --git a/vpn/wireguard/templates/wg0.j2 b/vpn/wireguard/templates/wg0.j2 new file mode 100644 index 0000000..303e17a --- /dev/null +++ b/vpn/wireguard/templates/wg0.j2 @@ -0,0 +1,32 @@ +auto wg0 +iface wg0 inet static + pre-up /usr/local/sbin/ip-link-add.sh $IFACE type wireguard + pre-up wg setconf $IFACE /etc/wireguard/$IFACE.conf + pre-up ip link set mtu 1420 dev $IFACE + + post-up /usr/local/sbin/post-up-$IFACE-inet.nft + post-up /usr/local/sbin/post-up-$IFACE-ipv4.nft +{% if vpn_wireguard_role == "server" %} +{% for client in vpn_wireguard_clients %} +{% if 'subnet' in client %} + post-up ip route add {{ client.subnet }} dev $IFACE +{% endif %} +{% endfor %} +{% elif vpn_wireguard_role == "client" %} + post-up ip route add default dev $IFACE table {{ vpn_wireguard_routing_table }} +{% endif %} + +{% if vpn_wireguard_role == "server" %} +{% for client in vpn_wireguard_clients %} +{% if 'subnet' in client %} + pre-down ip route del {{ client.subnet }} dev $IFACE +{% endif %} +{% endfor %} +{% elif vpn_wireguard_role == "client" %} + pre-down ip route del default dev $IFACE table {{ vpn_wireguard_routing_table }} +{% endif %} + pre-down /usr/local/sbin/pre-down-$IFACE-ipv4.nft + pre-down /usr/local/sbin/pre-down-$IFACE-inet.nft + + address {{ vpn_wireguard_address }} + netmask {{ vpn_wireguard_netmask }}