Initial commit

This commit is contained in:
Wojciech Kozlowski 2022-12-20 19:47:11 +01:00
commit 466fb54aab
60 changed files with 1437 additions and 0 deletions

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# Ansible Roles
A collection of roles I find useful for different playbooks.

View File

@ -0,0 +1,10 @@
---
argument_specs:
main:
options:
ansible_port:
type: "int"
required: true
system_base_fail2ban_ignoreip:
type: "str"
required: true

View File

@ -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

View File

@ -0,0 +1,5 @@
[sshd]
enabled = true
port = {{ ansible_port }}
findtime = 1d
bantime = 2w

View File

@ -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

View File

@ -0,0 +1,6 @@
---
- name: "enable fstrim.timer"
ansible.builtin.systemd:
name: "fstrim.timer"
enabled: true
state: "started"

View File

@ -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$

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 }}

View File

@ -0,0 +1 @@
{{ ansible_hostname }}.{{ system_mail_domain }}

View File

@ -0,0 +1,3 @@
address {
email-domain {{ ansible_hostname }}.{{ system_mail_domain }};
};

View File

@ -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 %}

View File

@ -0,0 +1 @@
[{{ system_mail_smtp_server }}]:{{ system_mail_smtp_port }} {{ system_mail_smtp_user }}:{{ system_mail_smtp_pass }}

View File

@ -0,0 +1,10 @@
---
argument_specs:
main:
options:
ansible_hostname:
type: "str"
required: true
system_base_motd_dir:
type: "str"
required: false

View File

@ -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

View File

@ -0,0 +1,3 @@
---
system_base_additional_tcp_ports: []
system_base_udp_ports: []

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -0,0 +1,7 @@
---
argument_specs:
main:
options:
system_base_ntp_timezone:
type: "str"
required: true

View File

@ -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 }}"

61
system/base/root/files/su Normal file
View File

@ -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

View File

@ -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

View File

@ -0,0 +1,2 @@
---
system_base_additional_ssh_users: []

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,6 @@
[Unit]
Description=Status email for %i
[Service]
Type=oneshot
ExecStart=/usr/local/bin/systemd-mail-systemctl-status %i

View File

@ -0,0 +1,7 @@
---
argument_specs:
main:
options:
ansible_hostname:
type: "str"
required: true

View File

@ -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

View File

@ -0,0 +1,11 @@
#!/bin/sh
/usr/sbin/sendmail -t <<SYSTEMDMAIL
To: root
From: systemd <systemd>
Subject: systemctl status $1 on {{ ansible_hostname }}
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset=UTF-8
$(systemctl status --full "$1")
SYSTEMDMAIL

View File

@ -0,0 +1,11 @@
#!/bin/sh
/usr/sbin/sendmail -t <<SYSTEMDMAIL
To: root
From: $(whoami).systemd <systemd>
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

View File

@ -0,0 +1,2 @@
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

View File

@ -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";

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
source ~/.tmux/tmux.conf

View File

@ -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 <Ctrl + a + I>
- 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

View File

@ -0,0 +1,13 @@
---
- name: "install utility programs"
ansible.builtin.apt:
name:
- "acl"
- "git"
- "htop"
- "man"
- "perl"
- "rsync"
- "tmux"
- "tcpdump"
- "traceroute"

View File

@ -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

13
vpn/base/tasks/main.yml Normal file
View File

@ -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

View File

@ -0,0 +1,4 @@
#!/usr/bin/env -S nft -f
flush table inet br0_inet
delete table inet br0_inet

View File

@ -0,0 +1,4 @@
#!/usr/bin/env -S nft -f
flush table ip br0_ipv4
delete table ip br0_ipv4

View File

@ -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

52
vpn/bridge/tasks/main.yml Normal file
View File

@ -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

View File

@ -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 }}

View File

@ -0,0 +1,5 @@
#!/usr/bin/env -S nft -f
table inet br0_inet {
}

View File

@ -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;
}
}

View File

@ -0,0 +1,4 @@
#!/usr/bin/env -S nft -f
flush table inet wg0_inet
delete table inet wg0_inet

View File

@ -0,0 +1,4 @@
#!/usr/bin/env -S nft -f
flush table ip wg0_ipv4
delete table ip wg0_ipv4

View File

@ -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' }}"

View File

@ -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

View File

@ -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;
}
}

View File

@ -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 %}
}

View File

@ -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 %}

View File

@ -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 }}