env-management (dev/prod) + podman

podman
Yves G 2023-07-30 19:36:11 +02:00
parent 87936b77fd
commit e0087d54f0
36 changed files with 487 additions and 101 deletions

View File

@ -1,27 +1,8 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
# Short personal nickname that will be mostly used as part of filenames under /etc.
nickname: personal
# Hostname and IPv4 address of the DMZ.
DMZ: dmz
DMZ_IP: 192.168.1.254
# Hostname and IPv4 address of the back-end server (with all the data).
SafeZone: home
SafeZone_IP: 192.168.1.253
# Domain names that the certificate should cover.
acme_domains: 'example.org www.example.org pubsub.example.org'
# Public key that Ansible will use to manage the server, and IP address of the controller PC.
# The public key (`….pub` file) is generated as the result of running `ssh-keygen -t ed25519`.
ansible_authorized_key: 'ssh-ed25519 AAAA0000bbbb1111CCCC2222dddd3333EEEE4444ffff5555GGGG6666hhhh7777IIII me@my-pc'
ansible_master: 192.168.1.252
# System user that will build packages from AUR (https://aur.archlinux.org/).
aur_user: git
@ -53,17 +34,17 @@ dotclear_master_key: 0123456789abcdefghijklmnopqrstuvwxyz
dotclear_root: /srv/webapps/dotclear
# The default locale (https://wiki.archlinux.org/index.php/Locale).
locales_default: 'en_US.UTF-8'
locales_default: 'en_GB.UTF-8'
# All installed locales on the server.
locales_enabled: 'en_US.UTF-8 fr_FR.UTF-8 fr_FR@euro'
locales_enabled: 'en_US.UTF-8 en_GB.UTF-8'
# Enable DNSSEC in systemd-resolved (“yes” or “no”, as a character string); experimental!
dns_sec: 'no'
# DNS servers to use on the server, for example:
# FDN-1 (v4) FDN-2 (v4) FDN-1 (v6) FDN-2 (v6) OpenNIC-1 OpenNIC-2 Google
dns_hosts: '80.67.169.12 80.67.169.40 2001:910:800::12 2001:910:800::40 87.98.175.85 5.135.183.146 8.8.8.8'
# OpenNIC-1 OpenNIC-2 Google
dns_hosts: '87.98.175.85 5.135.183.146 8.8.8.8'
# Nearest NTP servers (https://www.ntppool.org/).
ntp_hosts: '0.uk.pool.ntp.org 1.uk.pool.ntp.org 2.uk.pool.ntp.org 3.uk.pool.ntp.org'
@ -79,9 +60,6 @@ fw_knock_timeout_min: 2
# Port-knocking sequence. A port may appear multiple times, but not next to each-other.
fw_portknock_seq: 1 22 333 4444 333 22 1
# The email address associated to root, for commits in the git repository that stores changes to /etc.
git_contact_email: hostmaster@example.org
# Watch new repositories inside the already-watched perimeter by default.
gitea_auto_watch_new_repos: 'true'
@ -216,14 +194,11 @@ http_pfx_transmission: /torrent
# URL prefix of Wallabag (social sharing of bookmarks).
http_pfx_wallabag: /bookmarks
# Subdomain-name that will serve DNS packets for Iodine (DNS tunnel). Choose it short!
iodine_domain: dt.example.org
# Network associated with the DNS tunnel (IP address of the server on this network, “/”, bits for the network-mask).
iodine_net: '172.16.12.1/28'
# Password of the DNS tunnel.
iodine_password: '_t_r___e@6358'
iodine_password: 'dns-passwd'
# Location of Kodi state data (not the media contents).
kodi_data: /var/lib/kodi
@ -249,20 +224,9 @@ lam_passwordMustNotContainUser: 'true'
# Title for LDAP-Account-Manager in the SSOwat portal.
lam_sso_title: Directory
# Additional ACL for LDAP.
# This is typically used to give extra powers to users, for example regarding aliases management.
ldap_extra_acl: |
access to dn.subtree="ou=Aliases,dc=example,dc=org"
by dn.base="uid=me,ou=Users,dc=example,dc=org" write
by self read
by * read
# Organization-name for this home-server LDAP directory.
ldap_o_name: 'Home'
# Root of the LDAP directory. Usually the domain-name with commas instead of dots, and “dc=” in front of each level.
ldap_root: dc=example,dc=org
# Password of the root user (administrator) in OpenLDAP.
ldap_rootpw: 'OE104995à6&o_zKR4'
@ -273,7 +237,7 @@ ldap_rootpw_sha: '{SSHA}Raa3TlvDPZTjdM44nKZQt+hDvQRvaMDC'
# Custom system groups and memberships, declared in LDAP.
# This is the right place to declare a group in which to put all real and system users, who will be allowed to read media contents.
ldap_system_groups: '[
{"cn": "registered", "gidNumber": 1200}
{"cn": "registered", "gidNumber": 1200},
{"cn": "media", "gidNumber": 1201}
]'
ldap_system_group_members: '[
@ -339,30 +303,12 @@ loolwsd_lang: en
# by all of the LibreOffice Online, after which we start cleaning up idle documents”.
loolwsd_maxmem_asdouble: '80.0'
# Non-system mail aliases (stored in LDAP, in contrast to system aliases, which are stored in /etc/mail/aliases).
# Each entry in the list contains:
# — alias: a unique mail alias, either new or with existing associated recipients;
# — member: the login name of the user to add as a recipient for the alias.
mail_alias_memberships: '[
{"alias": "shop", "member": "you"},
{"alias": "throwable", "member": "me"},
{"alias": "family", "member": "me"},
{"alias": "family", "member": "you"}
]'
# DKIM selector to use (see http://yalis.fr/cms/index.php/post/2014/01/31/Why-buy-a-domain-name-Secure-mail%2E).
# See the “dmz_exim” role for the storage of the private and public keys.
mail_dkim_selector: home
# Actual Linux user, that receives all system emails (for root, postmaster, hostmaster…).
mail_forward_root_to: me
# IPv6 address of the ISPs smarthost when the ISP does not handle SMTP on IPv6 (example: smtp.bbox.fr).
mail_ignore_ip: '2001:860:e2ef::f503:0:2'
# All local mail destinations, which include managed domains, as well as host names.
mail_local_domains: 'home dmz localhost example.org *.example.org *.local'
# Maximum number of SPAM-filter workers.
mail_max_spam_workers: 5
@ -392,22 +338,6 @@ motion_cloud_password: password
motion_cloud_id: app_id_xxxxx
motion_cloud_key: xxxxxxxxxx…xxxxxxxxxx
motion_email_recipient: hostmaster@localhost
motion_cameras: '[
{
"id": 1, "name": "street door",
"url": "rtsp://user:password@street.example.org:554/videoMain",
"width": 640, "height": 360,
"mask_file": "example_mask_640_360.pgm",
"framerate": 5
},
{
"id": 2, "name": "garden door",
"url": "rtsp://user:password@garden.example.org:554/videoMain",
"width": 640, "height": 360,
"mask_file": null,
"framerate": 5
}
]'
motion_web_title: "Video surveillance"
# Name of the Movim database in PostgreSQL.
@ -435,9 +365,6 @@ movim_private_port: 33333
# — the web address for updating web applications…
net_allowed_domains: 'checkip.dns.he.net dyn.dns.he.net freedns.afraid.org download.dotclear.org dotaddict.org api.movim.eu'
# Start Of Authority: the root domain name configured on the server.
net_soa: example.org
# Subdomain for the XMPP multi-user chat component.
net_subdom_muc: muc
@ -539,13 +466,13 @@ prosody_db_password: prosody
sane_drivers: epson2
# Space-separated list of pacman mirrors to use.
software_mirrors: 'mirror.archlinux.ikoula.com archlinux.vi-di.fr'
software_mirrors: 'mirror.cyberbits.eu archlinux.vi-di.fr'
# Software that will get removed if present, on next run of the playbook (JSON list).
software_to_del: '["dhcpcd"]'
# Comma-separated list of software that pacman should not automatically upgrade.
software_to_ignore: 'linux,linux-firmware,linux-headers'
software_to_ignore: 'linux,linux-firmware,linux-headers,nginx-mainline'
# Environment variables that SSH may keep for remote connections.
ssh_accept_env: 'LANG LC_*'
@ -573,7 +500,7 @@ ssh_bastion_pwd_sha512: '$6$ZN4I.yIVUj0amxqe$5dBx1d34tNm9NMmmFV3UxZ0V2ecmOjefK5d
ssh_clientalive_interval: 600
# Servers timezone.
timezone: Europe/Paris
timezone: Europe/Dublin
# TLS ciphers to enable in TLS-terminating software (HAProxy, Nginx…).
# See https://wiki.mozilla.org/Security/Server_Side_TLS.
@ -607,9 +534,6 @@ wallabag_db_user: wallabag
# Password for the PostgreSQL user who owns the Wallabag database.
wallabag_db_password: wallabag
# Space-separated list of the XMPP accounts that are considered administrators of the XMPP service.
xmpp_admins: 'me@example.org'
# Network hosts from which registration is possible (else it is forbidden).
# Registration of hosted users is automatic.
xmpp_registration_hosts: '127.0.0.1 192.168.1.254 192.168.1.253 192.168.1.252'

View File

@ -0,0 +1 @@
../../../00_common_all.yaml

67
env/dev/group_vars/all/all.yaml vendored Normal file
View File

@ -0,0 +1,67 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
env: dev
# Short personal nickname that will be mostly used as part of filenames under /etc.
nickname: mydev
# Hostname and IPv4 address of the DMZ.
DMZ: front-dev
DMZ_IP: 10.0.2.4
# Hostname and IPv4 address of the back-end server (with all the data).
SafeZone: back-dev
SafeZone_IP: 10.0.2.3
# Domain names that the certificate should cover.
acme_domains: 'mydev.uk muc.mydev.uk pubsub.mydev.uk ssh.mydev.uk'
# Public key that Ansible will use to manage the server, and IP address of the controller PC.
# The public key (`….pub` file) is generated as the result of running `ssh-keygen -t ed25519`.
ansible_authorized_key: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPsidHzJhnXXRdWo4NUVmkMORcNN9k9RYaU4eSYgZ3hW me@my-pc'
ansible_master: 192.168.1.252
# The email address associated to root, for commits in the git repository that stores changes to /etc.
git_contact_email: hostmaster@mydev.uk
# Subdomain-name that will serve DNS packets for Iodine (DNS tunnel). Choose it short!
iodine_domain: dt.mydev.uk
# Additional ACL for LDAP.
# This is typically used to give extra powers to users, for example regarding aliases management.
ldap_extra_acl: |
access to dn.subtree="ou=Aliases,dc=mydev,dc=uk"
by dn.base="uid=me,ou=Users,dc=mydev,dc=uk" write
by self read
by * read
# Root of the LDAP directory. Usually the domain-name with commas instead of dots, and “dc=” in front of each level.
ldap_root: dc=mydev,dc=uk
# Non-system mail aliases (stored in LDAP, in contrast to system aliases, which are stored in /etc/mail/aliases).
# Each entry in the list contains:
# — alias: a unique mail alias, either new or with existing associated recipients;
# — member: the login name of the user to add as a recipient for the alias.
mail_alias_memberships: '[
{"alias": "us", "member": "me"},
{"alias": "us", "member": "you"}
]'
# DKIM selector to use (see http://yalis.uk/cms/index.php/post/2014/01/31/Why-buy-a-domain-name-Secure-mail%2E).
# See the “dmz_exim” role for the storage of the private and public keys.
mail_dkim_selector: back-dev
# All local mail destinations, which include managed domains, as well as host names.
mail_local_domains: 'back-dev front-dev localhost mydev.uk *.mydev.uk *.local'
# Motion monitored cameras
motion_cameras: '[
]'
# Start Of Authority: the root domain name configured on the server.
net_soa: mydev.uk
# Space-separated list of the XMPP accounts that are considered administrators of the XMPP service.
xmpp_admins: 'me@mydev.uk you@mydev.uk'

8
env/dev/group_vars/back.yaml vendored Normal file
View File

@ -0,0 +1,8 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
hostname: back-dev
ssh_allowed_users: 'root me you'
software_to_add: '[]'

8
env/dev/group_vars/front.yaml vendored Normal file
View File

@ -0,0 +1,8 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
hostname: front-dev
ssh_allowed_users: 'root {{ssh_bastion_user}}'
software_to_add: '[]'

9
env/dev/group_vars/front_chroot.yaml vendored Normal file
View File

@ -0,0 +1,9 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
hostname: front-dev
chroot: "/var/lib/machines/{{DMZ}}"
ssh_allowed_users: 'root {{ssh_bastion_user}}'
software_to_add: '[]'

9
env/dev/hosts vendored Normal file
View File

@ -0,0 +1,9 @@
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
[back]
back-dev ansible_connection=containers.podman.podman
[front]
front-dev ansible_connection=containers.podman.podman

View File

@ -0,0 +1 @@
../../../00_common_all.yaml

83
env/prod/group_vars/all/all.yaml vendored Normal file
View File

@ -0,0 +1,83 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
env: prod
# Short personal nickname that will be mostly used as part of filenames under /etc.
nickname: personal
# Hostname and IPv4 address of the DMZ.
DMZ: dmz
DMZ_IP: 192.168.1.254
# Hostname and IPv4 address of the back-end server (with all the data).
SafeZone: home
SafeZone_IP: 192.168.1.253
# Domain names that the certificate should cover.
acme_domains: 'example.org muc.example.org pubsub.example.org ssh.example.org'
# Public key that Ansible will use to manage the server, and IP address of the controller PC.
# The public key (`….pub` file) is generated as the result of running `ssh-keygen -t ed25519`.
ansible_authorized_key: 'ssh-ed25519 AAAA0000bbbb1111CCCC2222dddd3333EEEE4444ffff5555GGGG6666hhhh7777IIII me@my-pc'
ansible_master: 192.168.1.252
# The email address associated to root, for commits in the git repository that stores changes to /etc.
git_contact_email: hostmaster@example.org
# Subdomain-name that will serve DNS packets for Iodine (DNS tunnel). Choose it short!
iodine_domain: dt.example.org
# Additional ACL for LDAP.
# This is typically used to give extra powers to users, for example regarding aliases management.
ldap_extra_acl: |
access to dn.subtree="ou=Aliases,dc=example,dc=org"
by dn.base="uid=me,ou=Users,dc=example,dc=org" write
by self read
by * read
# Root of the LDAP directory. Usually the domain-name with commas instead of dots, and “dc=” in front of each level.
ldap_root: dc=example,dc=org
# Non-system mail aliases (stored in LDAP, in contrast to system aliases, which are stored in /etc/mail/aliases).
# Each entry in the list contains:
# — alias: a unique mail alias, either new or with existing associated recipients;
# — member: the login name of the user to add as a recipient for the alias.
mail_alias_memberships: '[
{"alias": "shop", "member": "you"},
{"alias": "throwable", "member": "me"},
{"alias": "family", "member": "me"},
{"alias": "family", "member": "you"}
]'
# DKIM selector to use (see http://yalis.fr/cms/index.php/post/2014/01/31/Why-buy-a-domain-name-Secure-mail%2E).
# See the “dmz_exim” role for the storage of the private and public keys.
mail_dkim_selector: home
# All local mail destinations, which include managed domains, as well as host names.
mail_local_domains: 'home dmz localhost example.org *.example.org *.local'
# Motion monitored cameras
motion_cameras: '[
{
"id": 1, "name": "street door",
"url": "rtsp://user:password@street.example.org:554/videoMain",
"width": 640, "height": 360,
"mask_file": "example_mask_640_360.pgm",
"framerate": 5
},
{
"id": 2, "name": "garden door",
"url": "rtsp://user:password@garden.example.org:554/videoMain",
"width": 640, "height": 360,
"mask_file": null,
"framerate": 5
}
]'
# Start Of Authority: the root domain name configured on the server.
net_soa: example.org
# Space-separated list of the XMPP accounts that are considered administrators of the XMPP service.
xmpp_admins: 'me@example.org'

View File

@ -1,6 +1,6 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
hostname: home

View File

@ -1,6 +1,6 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
hostname: dmz

View File

@ -1,6 +1,6 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
hostname: dmz

View File

@ -1,5 +1,5 @@
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
[back]

View File

@ -36,6 +36,10 @@
- (update_result.stdout is defined)
- (update_result.stdout.find('there is nothing to do') == -1)
- name: setup arch-chroot (dev)
include_role: name=dev.inc allow_duplicates=true tasks_from=arch-chroot.yml
when: (env == 'dev')
### UPSTREAM END ⇒ ###
- name: merge upstream
include_role: name=etckeeper.inc allow_duplicates=true tasks_from=merge.yml

View File

@ -0,0 +1,10 @@
- name: replace /usr/bin/arch-chroot in Podman
copy:
content: |
#!/bin/bash
args=()
while [ $# -gt 1 ]; do shift; args+=("$(printf "%q" "$1")"); done
[ -t 0 ] && t=-t || t=-T
exec ssh -i ~/.ssh/id-chroot -o StrictHostKeyChecking=no $t -p 20022 -t 10.0.2.2 "${args[@]}"
dest: /usr/bin/arch-chroot
mode: 0755

View File

@ -56,6 +56,7 @@
args:
creates: "{{front_dir}}/usr/bin/ash"
when:
- (env == 'prod')
- (arch.changed)
- name: … but not for some binaries
@ -117,12 +118,12 @@
- name: init the container
include_role:
name: init
vars_from: front_chroot.yml
vars_from: front_chroot.{{env}}.yaml
- name: init SSH in the container
include_role:
name: ssh
vars_from: front_chroot.yml
vars_from: front_chroot.{{env}}.yaml
- name: ensure systemd-nspawn@.service.d exists
file:
@ -160,6 +161,7 @@
daemon_reload: true
name: "systemd-nspawn@{{DMZ}}.service"
enabled: true
when: (env == 'prod')
### LOCAL COMMIT ⇒ ###
- name: commit local changes

View File

@ -6,12 +6,19 @@
# WARNING: This file may be used inside a mounted chroot.
# The running system should not be assumed to be the target system.
- name: setup arch-chroot (dev)
include_role: name=dev.inc allow_duplicates=true tasks_from=arch-chroot.yml
when:
- (env == 'dev')
- (chroot == "")
- name: set hostname (needed by etckeeper)
copy:
content: |
{{hostname}}
dest: "{{chroot}}/etc/hostname"
mode: 0644
when: (env == 'prod')
### INIT ⇒ ###
- name: init EtcKeeper
@ -42,6 +49,7 @@
- name: set hardware clock
command: hwclock --systohc
when:
- (env == 'prod')
- (chroot == "")
- (tz.changed)
- (inventory_hostname in groups['back'])
@ -86,6 +94,7 @@
src: files/hosts
dest: "{{chroot}}/etc/hosts"
mode: 0644
when: (env == 'prod')
# Networking
- name: set systemd network settings
@ -93,6 +102,7 @@
src: "files/network_{{hostname}}/"
dest: "{{chroot}}/etc/systemd/network/"
mode: 0644
when: (env == 'prod')
register: network
- name: ensure overriding directories of network settings exist
@ -126,6 +136,7 @@
dest: /etc/systemd/system/systemd-networkd-wait-online.service.d/wait.conf
mode: 0644
when:
- (env == 'prod')
- (chroot == '')
- (inventory_hostname in groups['back'])
@ -162,6 +173,7 @@
state: link
force: true
when:
- (env == 'prod')
- (chroot == "")
- name: enable DNS service
@ -169,6 +181,7 @@
src: /usr/lib/systemd/system/systemd-resolved.service
dest: "{{chroot}}/etc/systemd/system/multi-user.target.wants/systemd-resolved.service"
state: link
when: (env == 'prod')
- name: start DNS
systemd:
@ -176,6 +189,7 @@
name: systemd-resolved.service
state: restarted
when:
- (env == 'prod')
- (chroot == "")
- DNS.changed

View File

@ -0,0 +1 @@
../../../env/dev/group_vars/front_chroot.yaml

View File

@ -0,0 +1 @@
../../../env/prod/group_vars/front_chroot.yaml

View File

@ -1 +0,0 @@
../../../group_vars/front_chroot

View File

@ -68,6 +68,7 @@
fs.inotify.max_user_watches=65536
dest: /etc/sysctl.d/max_user_watches.conf
mode: 0600
when: (env == 'prod')
notify:
- enforce max_user_watches limit
- restart minidlna.service

View File

@ -16,6 +16,7 @@
net.ipv6.conf.all.forwarding=1
dest: /etc/sysctl.d/30-ipforward.conf
mode: 0600
when: (env == 'prod')
notify:
- apply sysctl immediately
@ -25,6 +26,7 @@
net.netfilter.nf_log_all_netns=1
dest: /etc/sysctl.d/30-kernellog.conf
mode: 0600
when: (env == 'prod')
notify:
- apply sysctl immediately

View File

@ -44,6 +44,7 @@
Requires=systemd-nspawn@{{DMZ}}.service
After=systemd-nspawn@{{DMZ}}.service
dest: /etc/systemd/system/pyruse-boot@action_nftBan.service.d/nftBan_wants_{{DMZ}}.conf
when: (env == 'prod')
notify:
- restart pyruse-boot@action_nftBan.service

View File

@ -12,6 +12,7 @@
mode: 0644
notify:
- create tmpfiles
when: (env == 'prod')
### LOCAL COMMIT ⇒ ###
- name: commit local changes

View File

@ -0,0 +1,38 @@
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0JKlFe7PAPozbaoi5f+Fn8TWbtpPUMeSfjw3cp6PNCguhmBP5voO40pDS5Bfs/04arWq5KVZo38Io4RuVqPTDu7/qkzctKpl6sOXdyM8KaG1sg1wVgcVnaCxQsK7Bw2ZPWUP2JY3Z/DICn8QGrwF1qT9I8Nua9EoXt7gFz8ifEU0V47CRgOFI1bPXU6lEh2EjNKHJ8XtYJ13Jf4CvQ+pqpU8Owk/bFTwmJUvZQ8PPweC6HsAwbvsjl38zHgbaKtIRya41qg9Fv4xYH7GPBe97TmapP8vHg28n46u57KH9qIFdK/y3RP4OjgFcR8Kceh7DQe85WpDigx5HO2bcqGWxayXYvrXHAWerUQdpVc/eaMtsFGRfYQE52Sf8IfJr+bkoDS3ytTm/Bdu4gASY6UYqXXp51iMeQ200F4OWo8TA+hq/6mnoeAgFLeRn4FXrYIl4RpBxA//JrM4P7Y610KG9YbSPSqR17dFdwqe5GXhifOgyHzlr8bNM7ibKxPbj12s= root@back-dev

View File

@ -0,0 +1,38 @@
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCSZXG3Zg9EC8vEpjfDC50vOg3kmTYpYDYUJ9KcmyrwQ2lpKgdSHWtfWKrc0DRV47O2CYxtGK22mbQf8IoAZK/yNhffFIa05sCLX5fM5WuKPH+7rBdDY+c7Tc5jSwTcIAWUe9ABRmHvgdmnhO5aZVBawUlu3frmnCZyE4q4V16YjszlhWc75IOuoP+IrooufT1fE2QdK34TbNW3Ko0BTSF35BZd8r2MxS83HLR+u/BD8g/SwhsAyv1RoEFZlS9QMJHBEz4f/Nz2J2U14hlgQpe9IF+tqT1u4JyqA5XQfevw0pfR6UpIs0oMeZN1ZDviju7KAUo7QvEOWj4NUrWAKNL5TrdW4DS8q/IfHoW286uWWYDKDwphpl20/SQOdlIhyDSxSnNO3PbAU6l4U//+o5STiowr2zlC8hNT025vjjYr/bzgO4pEdEIkZTvtRZ2LCdXqcVt3DoxOmEXRoxoEFoDi/0zL0TAB+ME+khwCOgi6JlDsB8UU5ku5X2O4fB/nQ3U= root@front-dev

View File

@ -0,0 +1 @@
../../../env/dev/group_vars/front_chroot.yaml

View File

@ -0,0 +1 @@
../../../env/prod/group_vars/front_chroot.yaml

View File

@ -1 +0,0 @@
../../../group_vars/front_chroot

View File

@ -11,6 +11,7 @@
net.core.wmem_max = 4194304
dest: /etc/sysctl.d/net_core_mem_max.conf
mode: 0600
when: (env == 'prod')
notify:
- enforce net_core_mem_max limit

View File

@ -1,6 +1,6 @@
---
# The home-server project produces a multi-purpose setup using Ansible.
# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license.
# Copyright © 20182023 Y. Gablin, under the GPL-3.0-or-later license.
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
- hosts: back
@ -16,8 +16,10 @@
- front
- postinstall
- msmtp
- nfs
- transmission_nfs
- role: nfs
when: (env == 'prod')
- role: transmission_nfs
when: (env == 'prod')
- pyruse
- nftables_back
- postgresql
@ -34,8 +36,10 @@
- dovecot
- mediaplayer
- motion_back
- front_run
- acme_back
- role: front_run
when: (env == 'prod')
- role: acme_back
when: (env == 'prod')
- nextcloud_davfs
- _maintenance_stop
@ -48,8 +52,12 @@
- postinstall
- ldap
- iodine
- ddclient_HE_example
- ddclient_FreeDNS_example
- role: ddclient.inc
when: (env == 'dev')
- role: ddclient_HE_example
when: (env == 'prod')
- role: ddclient_FreeDNS_example
when: (env == 'prod')
- dmz_nginx
- ssowat
- php

100
tools/podman/Makefile Normal file
View File

@ -0,0 +1,100 @@
SHELL := /bin/bash
# https://stackoverflow.com/a/23324703
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
MY_IP := $(shell ip route | sed -nr 's/^default.* src ([^ ]+).*/\1/p')
NETWORK := 10.0.2.0
NET_BITS := 25# max 25 (no space between value and comment!)
FRONT_NAME := front-dev
FRONT_IP := 10.0.2.4
FRONT_SH_EXTRA := # empty, or must end with ;
FRONT_PODMAN_EXTRA :=
BACK_NAME := back-dev
BACK_IP := 10.0.2.3
BACK_SH_EXTRA := mkdir -p "${ROOT_DIR}/target/back.media/share/{p2p,video,my_CDs,my_MP3,photos}"; # empty, or must end with ;
BACK_PODMAN_EXTRA := -v "${ROOT_DIR}/target/back.media":/mnt/share
PODMAN_BUILD := podman build
PODMAN_RUN := podman run -d --privileged --cap-add=CAP_SYS_CHROOT --hostuser=${USER} --group-add=keep-groups -v "${ROOT_DIR}/target/shared_sockets:/run/shared_sockets:shared,U"
all:
printf "— front-dev | back-dev (implies front-dev): that container\n— rm: remove containers\n— rmi: remove images\n— clean: remove all (incl. Archlinux image)\n— ansible: install dev site\n"
rm:
podman stop back-dev; podman stop front-dev; podman rm back-dev; podman rm front-dev; rm -rf "${ROOT_DIR}/target"; true
rmi: rm
podman rmi back-img; podman rmi front-img; true
clean: rmi
podman rmi archlinux; true
ansible: back-dev
cd "${ROOT_DIR}/../.." && ansible-playbook -i env/dev -vvv site.yaml
front-img: Makefile front.Dockerfile id-dev.pub id-chroot.pub
ds=$$(find $^ -maxdepth 0 -printf %T@ | sort -t. -rn | awk -F. 'NR==1{print $$1}'); \
dt=$$(podman images --format=json | jq --arg name localhost/front-img:latest -r '.[] | select(.Names | length > 0) | select(.Names[0] == $$name) | .Created'); \
if [ -n "$$dt" ] && [ $$ds -gt $$dt ]; then \
podman stop front-dev; podman rm front-dev; podman rmi front-img; \
dt=; \
fi; \
if [ -z "$$dt" ]; then \
${PODMAN_BUILD} -t=front-img -f=front.Dockerfile "${ROOT_DIR}"; \
fi
front-dev: front-img
mkdir -p "${ROOT_DIR}/target"/front.{opt,srv}; \
${FRONT_SH_EXTRA} \
if ! podman ps | grep -qF localhost/front-img:latest; then \
rm -rf "${ROOT_DIR}/target/shared_sockets"; mkdir -m 1777 "${ROOT_DIR}/target/shared_sockets" 2>/dev/null; \
if podman ps -a | grep -qF localhost/front-img:latest; then \
podman start front-dev; \
else \
${PODMAN_RUN} --name front-dev -p 20022:22 \
--network=slirp4netns:allow_host_loopback=true,cidr=${NETWORK}/${NET_BITS},outbound_addr=${MY_IP},port_handler=slirp4netns --hostname=${FRONT_NAME} --add-host=${BACK_NAME}:${BACK_IP} \
-v "${ROOT_DIR}/target/front.opt:/opt" \
-v "${ROOT_DIR}/target/front.srv:/srv" \
${FRONT_PODMAN_EXTRA} localhost/front-img; \
fi; \
fi
back-img: Makefile back.Dockerfile id-dev.pub id-chroot
ds=$$(find $^ -maxdepth 0 -printf %T@ | sort -t. -rn | awk -F. 'NR==1{print $$1}'); \
dt=$$(podman images --format=json | jq --arg name localhost/back-img:latest -r '.[] | select(.Names | length > 0) | select(.Names[0] == $$name) | .Created'); \
if [ -n "$$dt" ] && [ $$ds -gt $$dt ]; then \
podman stop back-dev; podman rm back-dev; podman rmi back-img; \
dt=; \
fi; \
if [ -z "$$dt" ]; then \
${PODMAN_BUILD} -t=back-img -f=back.Dockerfile "${ROOT_DIR}"; \
fi
back-dev: front-dev back-img
mkdir -p "${ROOT_DIR}/target"/back.{opt,srv}; \
${BACK_SH_EXTRA} \
if ! podman ps | grep -qF localhost/back-img:latest; then \
if podman ps -a | grep -qF localhost/back-img:latest; then \
podman unshare podman mount front-dev; \
podman start back-dev; \
else \
set -x; \
frontDir="$$(podman unshare podman mount front-dev)"; \
#--cgroupns=container:front-dev \
${PODMAN_RUN} --name back-dev -p 10022:22 \
--network=slirp4netns:allow_host_loopback=true,cidr=${NETWORK}/${NET_BITS},outbound_addr=${MY_IP},port_handler=slirp4netns --hostname=${BACK_NAME} --add-host=${FRONT_NAME}:${FRONT_IP} \
--mount=type=bind,src="$${frontDir}",dst="/var/lib/machines/${FRONT_NAME}",bind-propagation=shared,relabel=shared \
-v "${ROOT_DIR}/target/back.opt:/opt" \
-v "${ROOT_DIR}/target/back.srv:/srv" \
${BACK_PODMAN_EXTRA} localhost/back-img; \
fi; \
fi
id-chroot:
ssh-keygen -t ed25519 -f "${ROOT_DIR}/id-chroot" -N ""
id-chroot.pub:
ssh-keygen -t ed25519 -f "${ROOT_DIR}/id-chroot" -N ""
.PHONY: all rm rmi clean ansible front-img front-dev back-img back-dev

View File

@ -0,0 +1,25 @@
FROM docker.io/library/archlinux
VOLUME /run/shared_sockets
VOLUME /opt
EXPOSE 22
COPY id-dev.pub /root/.ssh/authorized_keys
COPY id-chroot /root/.ssh/id-chroot
RUN sed -i '/^NoExtract/d' /etc/pacman.conf && \
pacman --noconfirm -Syu pacman-mirrorlist glibc base arch-install-scripts openssh python etckeeper git rsync && \
grep -om1 'Server.*' </etc/pacman.d/mirrorlist.pacnew >/etc/pacman.d/mirrorlist && \
chown -R root:root /root/.ssh && \
chmod 600 /root/.ssh/* && \
chmod 700 /root/.ssh && \
mkdir -p /etc/systemd/system/multi-user.target.wants && \
ln -s /usr/lib/systemd/system/systemd-timesyncd.service /etc/systemd/system/multi-user.target.wants/ && \
sed -i '/prohibit-password/s/.*/PermitRootLogin yes/' /etc/ssh/sshd_config && \
ln -s /usr/lib/systemd/system/sshd.service /etc/systemd/system/multi-user.target.wants/
# for debug…
RUN pacman --noconfirm -S nmap vim
CMD [ "/sbin/init" ]

View File

@ -0,0 +1,27 @@
FROM docker.io/library/archlinux
VOLUME /run/shared_sockets
VOLUME /opt
VOLUME /srv
EXPOSE 22
COPY id-dev.pub /root/.ssh/authorized_keys
COPY id-chroot.pub /root/.ssh/id-chroot.pub
RUN sed -i '/^NoExtract/d' /etc/pacman.conf && \
pacman --noconfirm -Syu pacman-mirrorlist glibc git $(LANG=C pacman -Si base | sed -nr 's/^Depends[^:]*: *//;t ok;b;: ok;s/ +/\n/g;p;q' | grep -vxE 'bzip2|dhcpcd|gzip|licenses|linux|lvm2|mdadm|pciutils|reiserfsprogs|systemd-sysvcompat|texinfo|usbutils|xfsprogs') busybox openssh python etckeeper && \
grep -om1 'Server.*' </etc/pacman.d/mirrorlist.pacnew >/etc/pacman.d/mirrorlist && \
cat /root/.ssh/id-chroot.pub >>/root/.ssh/authorized_keys && \
chown -R root:root /root/.ssh && \
chmod 600 /root/.ssh/* && \
chmod 700 /root/.ssh && \
mkdir -p /etc/systemd/system/multi-user.target.wants && \
ln -s /usr/lib/systemd/system/systemd-timesyncd.service /etc/systemd/system/multi-user.target.wants/ && \
sed -i '/prohibit-password/s/.*/PermitRootLogin yes/' /etc/ssh/sshd_config && \
ln -s /usr/lib/systemd/system/sshd.service /etc/systemd/system/multi-user.target.wants/
# for debug…
RUN pacman --noconfirm -S nmap vim
CMD [ "/sbin/init" ]