diff --git a/.editorconfig b/.editorconfig index 2b2153d..e09c9dd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,9 @@ insert_final_newline = true [*.yaml] indent_size = 2 +[*.exs] +indent_size = 2 + # possibly sql dumps [*.sql] indent_size = unset diff --git a/PORT_MAP.md b/PORT_MAP.md index 1111ee5..fe5117b 100644 --- a/PORT_MAP.md +++ b/PORT_MAP.md @@ -6,11 +6,6 @@ easily find available ports for other bundles. ## TCP Rule of thumb: keep ports below 10000 free for stuff that reserves ports. -| Port range | reserved for | -| ----------- | ------------ | -| 200.. | Matrix | -| 220.. | Generic Web services | - | Port | bundle | usage | | ----------- | -------------------- | ----- | | 22 | | sshd | @@ -38,6 +33,7 @@ Rule of thumb: keep ports below 10000 free for stuff that reserves ports. | 20080 | matrix-synapse | client, federation | | 20081 | matrix-synapse | prometheus metrics | | 20090 | matrix-media-repo | media_repo | +| 21000 | pleroma | pleroma | | 22000 | gitea | gitea | | 22010 | jenkins-ci | Jenkins CI | | 22020 | travelynx | Travelynx Web | diff --git a/bundles/letsencrypt/files/domains.txt b/bundles/letsencrypt/files/domains.txt index d9d7824..ea7e427 100644 --- a/bundles/letsencrypt/files/domains.txt +++ b/bundles/letsencrypt/files/domains.txt @@ -1,5 +1,3 @@ -${node.metadata['hostname']} - % for domain, aliases in sorted(node.metadata.get('letsencrypt/domains', {}).items()): ${domain} ${' '.join(sorted(aliases))} % endfor diff --git a/bundles/letsencrypt/files/letsencrypt-ensure-some-certificate b/bundles/letsencrypt/files/letsencrypt-ensure-some-certificate index 45f474a..e0248cb 100644 --- a/bundles/letsencrypt/files/letsencrypt-ensure-some-certificate +++ b/bundles/letsencrypt/files/letsencrypt-ensure-some-certificate @@ -6,7 +6,7 @@ just_check=$2 cert_path="/var/lib/dehydrated/certs/$domain" already_exists=false -if [ -f "$cert_path/privkey.pem" -a -f "$cert_path/fullchain.pem" ] +if [ -f "$cert_path/privkey.pem" -a -f "$cert_path/fullchain.pem" -a -f "$cert_path/chain.pem" ] then already_exists=true fi @@ -23,6 +23,7 @@ fi if [ "$already_exists" != true ] then + rm -r "$cert_path" mkdir -p "$cert_path" openssl req -x509 -newkey rsa:4096 -nodes -days 3650 -subj "/CN=$domain" -keyout "$cert_path/privkey.pem" -out "$cert_path/fullchain.pem" chmod 0600 "$cert_path/privkey.pem" diff --git a/bundles/nginx/files/nginx.conf b/bundles/nginx/files/nginx.conf index b2fcf10..5943dee 100644 --- a/bundles/nginx/files/nginx.conf +++ b/bundles/nginx/files/nginx.conf @@ -35,5 +35,24 @@ http { '' close; } + # GDPR compatible IP smashinator 5000000 + map $remote_addr $ip_anonym1 { + default 0.0.0; + "~(?P(\d+)\.(\d+))\.(\d+)\.\d+" $ip; + "~(?P[^:]+:[^:]+):" $ip; + } + map $remote_addr $ip_anonym2 { + default .0.0; + "~(?P(\d+)\.(\d+)\.(\d+))\.\d+" .0.0; + "~(?P[^:]+:[^:]+):" ::; + } + map $ip_anonym1$ip_anonym2 $ip_anonymized { + default 0.0.0.0; + "~(?P.*)" $ip; + } + log_format gdpr '$ip_anonymized - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"" "$http_user_agent"'; + include /etc/nginx/sites/*; } diff --git a/bundles/pleroma/files/pleroma.config.exs b/bundles/pleroma/files/pleroma.config.exs new file mode 100644 index 0000000..64c2c0b --- /dev/null +++ b/bundles/pleroma/files/pleroma.config.exs @@ -0,0 +1,35 @@ +import Config + +config :pleroma, + configurable_from_database: true + +config :pleroma, Pleroma.Web.Endpoint, + url: [host: "${node.metadata['pleroma']['url']}", scheme: "https", port: 443], + http: [port: 21000, ip: {127, 0, 0, 1}], + secret_key_base: "${node.metadata['pleroma']['secret_key']}", + secure_cookie_flag: true + +config :pleroma, :instance, + static_dir: "/var/pleroma/static/" + +config :pleroma, Pleroma.Upload, + uploader: Pleroma.Uploaders.Local, + filters: [Pleroma.Upload.Filter.Dedupe] + +config :pleroma, Pleroma.Uploaders.Local, + uploads: "/var/pleroma/uploads/" + +config :pleroma, :media_proxy, + enabled: false, + redirect_on_failure: true + #base_url: "https://cache.pleroma.social" + +# Configure your database +config :pleroma, Pleroma.Repo, + adapter: Ecto.Adapters.Postgres, + username: "pleroma", + password: "${node.metadata['postgresql']['roles']['pleroma']['password']}", + database: "pleroma", + hostname: "localhost", + pool_size: 10, + timeout: 60000 diff --git a/bundles/pleroma/files/pleroma.service b/bundles/pleroma/files/pleroma.service new file mode 100644 index 0000000..bca89ee --- /dev/null +++ b/bundles/pleroma/files/pleroma.service @@ -0,0 +1,22 @@ +[Unit] +Description=Pleroma social network +After=network.target +Requires=postgresql.service + +[Service] +User=pleroma +WorkingDirectory=/opt/pleroma +Environment="HOME=/opt/pleroma" +Environment="PLEROMA_CONFIG_PATH=/opt/pleroma/pleroma.config.exs" +Environment="PLUG_TMPDIR=/tmp/pleroma" +ExecStart=/opt/pleroma/release/bin/pleroma start +ExecStop=/opt/pleroma/release/bin/pleroma stop +Restart=on-failure + +PrivateTmp=true +ProtectHome=true +ProtectSystem=full +CapabilityBoundingSet=~CAP_SYS_ADMIN + +[Install] +WantedBy=multi-user.target diff --git a/bundles/pleroma/items.py b/bundles/pleroma/items.py new file mode 100644 index 0000000..a03b973 --- /dev/null +++ b/bundles/pleroma/items.py @@ -0,0 +1,88 @@ +version = node.metadata['pleroma']['version'] + +users = { + 'pleroma': { + 'home': '/opt/pleroma', + }, +} + +directories = { + '/opt/pleroma': {}, + '/var/pleroma': { + 'owner': 'pleroma', + }, + '/var/pleroma/uploads': { + 'owner': 'pleroma', + }, + '/var/pleroma/static': { + 'owner': 'pleroma', + }, + '/var/pleroma/static/emoji': { + 'owner': 'pleroma', + }, +} + +if node.has_bundle('zfs'): + directories['/var/pleroma']['needs'] = { + 'zfs_dataset:tank/pleroma-data', + } + +actions = { + 'pleroma_download_release': { + 'command': \ + 'cd /opt/pleroma/ && '\ + f'wget -O/opt/pleroma/pleroma.zip https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/release/{version}/download?job=amd64 && '\ + 'rm -rf release && '\ + 'unzip /opt/pleroma/pleroma.zip && '\ + 'chown -R pleroma:pleroma /opt/pleroma/release && '\ + f'echo -n "{version}" > /opt/pleroma/.bundlewrap_installed_version', + 'unless': f'[ "$(cat /opt/pleroma/.bundlewrap_installed_version)" = "{version}" ]', + 'needs': { + 'directory:/opt/pleroma', + }, + 'preceded_by': { + 'svc_systemd:pleroma:stop', + }, + 'triggers': { + 'action:pleroma_migrate_database', + 'svc_systemd:pleroma:restart', + }, + }, + 'pleroma_migrate_database': { + 'triggered': True, + 'command': \ + 'echo "CREATE EXTENSION IF NOT EXISTS citext;" | psql pleroma && '\ + 'echo "CREATE EXTENSION IF NOT EXISTS pg_trgm;" | psql pleroma && '\ + 'echo "CREATE EXTENSION IF NOT EXISTS \\\"uuid-ossp\\\";" | psql pleroma && '\ + 'sudo -u pleroma PLEROMA_CONFIG_PATH=/opt/pleroma/pleroma.config.exs /opt/pleroma/release/bin/pleroma_ctl create', + 'needs': { + 'postgres_db:pleroma', + }, + }, +} + +files = { + '/etc/systemd/system/pleroma.service': { + 'triggers': { + 'action:systemd-reload', + 'svc_systemd:pleroma:restart', + }, + }, + '/opt/pleroma/pleroma.config.exs': { + 'content_type': 'mako', + 'triggers': { + 'svc_systemd:pleroma:restart', + }, + }, +} + +svc_systemd = { + 'pleroma': { + 'needs': { + 'action:pleroma_download_release', + 'action:pleroma_migrate_database', + 'file:/etc/systemd/system/pleroma.service', + 'file:/opt/pleroma/pleroma.config.exs', + }, + }, +} diff --git a/bundles/pleroma/metadata.py b/bundles/pleroma/metadata.py new file mode 100644 index 0000000..8ba5634 --- /dev/null +++ b/bundles/pleroma/metadata.py @@ -0,0 +1,57 @@ +defaults = { + 'apt': { + 'packages': { + 'imagemagick': {}, + 'ffmpeg': {}, + 'libimage-exiftool-perl': {}, + }, + }, + 'backups': { + 'paths': { + '/var/pleroma', + }, + }, + 'zfs': { + 'datasets': { + 'tank/pleroma-data': { + 'mountpoint': '/var/pleroma', + }, + }, + }, + 'postgresql': { + 'roles': { + 'pleroma': { + 'password': repo.vault.password_for(f'{node.name} postgresql pleroma'), + }, + }, + 'databases': { + 'pleroma': { + 'owner': 'pleroma', + }, + }, + }, +} + + +@metadata_reactor.provides( + 'nginx/vhosts/pleroma', +) +def nginx(metadata): + if not node.has_bundle('nginx'): + raise DoNotRunAgain + + return { + 'nginx': { + 'vhosts': { + 'pleroma': { + 'domain': metadata.get('pleroma/url'), + 'proxy': { + '/': { + 'target': 'http://127.0.0.1:21000', + 'websockets': True, + }, + }, + }, + }, + }, + } diff --git a/bundles/postfix/metadata.py b/bundles/postfix/metadata.py index 4399e3b..9899988 100644 --- a/bundles/postfix/metadata.py +++ b/bundles/postfix/metadata.py @@ -49,7 +49,7 @@ else: 'letsencrypt/reload_after', ) def letsencrypt(metadata): - if not node.has_bundle('letsencrypt'): + if not node.has_bundle('letsencrypt') or not node.has_bundle('postfixadmin'): raise DoNotRunAgain result = { @@ -58,12 +58,9 @@ def letsencrypt(metadata): }, } - myhostname = metadata.get('postfix/myhostname', None) - - if myhostname and myhostname != metadata.get('hostname'): - result['domains'] = { - myhostname: set(), - } + result['domains'] = { + metadata.get('postfix/myhostname', metadata.get('hostname')): set(), + } return { 'letsencrypt': result, diff --git a/bundles/postgresql/items.py b/bundles/postgresql/items.py index f1d2953..f48ad74 100644 --- a/bundles/postgresql/items.py +++ b/bundles/postgresql/items.py @@ -25,10 +25,10 @@ directories = { }, # This is needed so the above purge does not remove the version # currently installed. - '/etc/postgresql/{}'.format(postgresql_version): { - 'owner': None, - 'group': None, - 'mode': None, + '/etc/postgresql/{}/main'.format(postgresql_version): { + 'owner': 'postgres', + 'group': 'postgres', + 'mode': '0755', }, } diff --git a/bundles/systemd-networkd/items.py b/bundles/systemd-networkd/items.py index 6d068d4..aa2084c 100644 --- a/bundles/systemd-networkd/items.py +++ b/bundles/systemd-networkd/items.py @@ -1,11 +1,5 @@ assert node.has_bundle('systemd') -pkg_apt = { - 'resolvconf': { - 'installed': False, - }, -} - files = { '/etc/network/interfaces': { 'delete': True, diff --git a/bundles/systemd-networkd/metadata.py b/bundles/systemd-networkd/metadata.py index 54579e4..e8dff0e 100644 --- a/bundles/systemd-networkd/metadata.py +++ b/bundles/systemd-networkd/metadata.py @@ -1,3 +1,14 @@ +defaults = { + 'apt': { + 'packages': { + 'resolvconf': { + 'installed': False, + }, + }, + }, +} + + @metadata_reactor.provides( 'interfaces', ) diff --git a/bundles/zfs/items.py b/bundles/zfs/items.py index ad09841..1322250 100644 --- a/bundles/zfs/items.py +++ b/bundles/zfs/items.py @@ -19,6 +19,7 @@ actions = { 'zfs_dataset:', 'zfs_pool:', }, + 'comment': 'If this fails, do a dist-upgrade, reinstall zfs-dkms, reboot', }, } diff --git a/data/backup/keys/htz-cloud.pleroma.key.vault b/data/backup/keys/htz-cloud.pleroma.key.vault new file mode 100644 index 0000000..06e6894 --- /dev/null +++ b/data/backup/keys/htz-cloud.pleroma.key.vault @@ -0,0 +1 @@ +encrypt$gAAAAABgMXllIGiB__clFctfOC6T4qRhFDrh_WJZU745-DZef2UpKCy0gz_2FlDAIqrNceL-Ahz1AXZrsdHUKPYAZ5AW4ne0b0G6uHQENYB0xv-ZqA3MZS26gzvNM7ejhyTCM1zO1j6ePgIxfZlaalNcuLIRAphuhu7KkJA8sGaoUMjdTqVWJUjj4Le8KHcS-s7PhB1XjkyHYxb0cKFgPxs1CgHWVjfCviVnl3yFAF1aLvYsbNcpzM_RGGIIA9YsO3yPQ8Mfk4B3truuNg1mdNaunpnhoTImF2cSNoI64f2mVaSNxxRXm1NG2qUJkZN8ZQlW8k7A1w_zUwHw9-JaimZejfPWrhew7krAbPQWEqOz7Km0RkQdbzFzxWECDIOQ_Z87n_yEFLSN3sAHA0eQ-a6oqj5Ybga5p9eeNNdOYAZyU_6KfSl9U6XSKT16brAXnsZevWQHk06ObdOPhJW5SMIQwk0TZXUOMZ11T0o0-2IMGBngOjoOxqt7gjZoiLFt4c8BkFcDkpTj25asyG2iF-2jWZ1cY91F5nDkIE3CSQzD7DYANyTI7ik9qACiY25bBYOwo9HS9TEcE-wDS2_jKolFFmEx5EFdxzIpSXdWB7EznbizgqAtu2eYubASKlBKILpeVZiqKZi8 \ No newline at end of file diff --git a/data/backup/keys/htz-cloud.pleroma.pub b/data/backup/keys/htz-cloud.pleroma.pub new file mode 100644 index 0000000..a699ae1 --- /dev/null +++ b/data/backup/keys/htz-cloud.pleroma.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK7vz3CMmmHmnWZs4b+Ohh4wnUgcME8PvZscjgS+91Qd kunsi@kunsi-t470.kunbox.net diff --git a/data/nginx/files/extras/htz-cloud.pleroma/pleroma b/data/nginx/files/extras/htz-cloud.pleroma/pleroma new file mode 100644 index 0000000..3f9e6a0 --- /dev/null +++ b/data/nginx/files/extras/htz-cloud.pleroma/pleroma @@ -0,0 +1,4 @@ + client_max_body_size 16m; + + access_log /var/log/nginx/pleroma.log gdpr; + error_log /var/log/nginx/error.log; diff --git a/data/powerdns/files/bind-zones/cybert-media.net b/data/powerdns/files/bind-zones/cybert-media.net new file mode 100644 index 0000000..1e50f26 --- /dev/null +++ b/data/powerdns/files/bind-zones/cybert-media.net @@ -0,0 +1,7 @@ +${header} + +$ORIGIN cybert-media.net. + +@ IN A 159.69.11.231 + IN AAAA 2a01:4f8:c2c:c410::1 + IN TXT "v=spf1 a ~all" diff --git a/nodes/htz-cloud/pleroma.py b/nodes/htz-cloud/pleroma.py new file mode 100644 index 0000000..549ac5c --- /dev/null +++ b/nodes/htz-cloud/pleroma.py @@ -0,0 +1,52 @@ +nodes['htz-cloud.pleroma'] = { + 'bundles': { + 'pleroma', + 'postgresql', + 'zfs', + }, + 'groups': { + 'debian-buster', + 'webserver', + }, + 'metadata': { + 'interfaces': { + 'eth0': { + 'ips': { + '159.69.11.231', + '2a01:4f8:c2c:c410::1/64', + }, + 'gateway4': '172.31.1.1', + 'gateway6': 'fe80::1', + }, + }, + 'cron': { + 'auto-authorize-sm-users': '* * * * * root echo "UPDATE users SET approval_pending=false WHERE email LIKE \'\\%@seibert-media.net\' AND approval_pending=true;" | psql pleroma >/dev/null', + }, + 'nginx': { + 'vhosts': { + 'pleroma': { + 'extras': True, + }, + }, + }, + 'pleroma': { + 'version': '2.2.2', + 'url': 'cybert-media.net', + 'secret_key': vault.decrypt('encrypt$gAAAAABgMVXXclfxVY022fM0Fdf94Oh3sxVlK0lYyBO_CsQFEbZcMua3w1oJY8_9d1JcrCJSSeBRTDnt-ZkRCQ6xKoALo8Rl7s9DPxa7J0vHdkggeZ3IHaOyXBcBPdx8vILyKDLHRXacaynOUBOjy6RIl6Qf2wH1ASbphCcjD-Njricg4PG6Rcixm87fF60rLBjAAkRoz5ZQnXlut1rhjLj-z-7UpA68fkeyPVJXbroWBJdmvCUt92dwjuGARsku2XI22mVvjtJJ'), + }, + 'postfix': { + 'myhostname': 'cybert-media.net', + }, + 'vm': { + 'cpu': 1, + 'ram': 2, + }, + 'zfs': { + 'pools': { + 'tank': { + 'device': '/dev/sdb', + }, + }, + }, + }, +} diff --git a/nodes/htz/ex42-1048908.py b/nodes/htz/ex42-1048908.py index 247534c..a408cf2 100644 --- a/nodes/htz/ex42-1048908.py +++ b/nodes/htz/ex42-1048908.py @@ -49,6 +49,7 @@ nodes['htz.ex42-1048908'] = { # No need to create a bundle just to install packages, # configs will be managed by users nevertheless. + 'mosh': {}, 'weechat': {}, 'weechat-core': {}, 'weechat-curses': {},