Compare commits

..

1 commit

Author SHA1 Message Date
b638cd7310 WIP: matrix 2023-02-25 15:06:17 +01:00
137 changed files with 974 additions and 445 deletions

37
.gitignore vendored
View file

@ -1,38 +1 @@
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
.terraform.lock.hcl
*.tfbackend
.vault_password .vault_password

8
Makefile Normal file
View file

@ -0,0 +1,8 @@
all:
ansible-playbook playbooks/all.yml
backup:
ansible-playbook playbooks/backup.yml
%:
ansible-playbook playbooks/all.yml --tags "$@"

View file

@ -1,23 +1,59 @@
# Max # Homeservers
Max is our VM running all of our web servers, provisioned with Terraform and configured with Ansible. This repository contains Ansible scripts to setup our home servers.
The `common` role executes some common OS tasks.
The `docker` role installs Docker.
The other roles are specifically for the various services we run.
## Running services ## Running services
All services below are implemented using Docker: All services below are running under Docker, except NSD and Borg.
- Authoritative DNS using [NSD](https://www.nlnetlabs.nl/projects/nsd/about/) (ns.pizzapim.nl)
- Reverse proxy using [Traefik](https://doc.traefik.io/traefik/) - Reverse proxy using [Traefik](https://doc.traefik.io/traefik/)
- Git server using [Forgejo](https://forgejo.org/) ([git.pim.kunis.nl](https://git.pim.kunis.nl)) - Git server using [Forgejo](https://forgejo.org/) ([git.pizzapim.nl](https://git.pizzapim.nl))
- Static website using [Jekyll](https://jekyllrb.com/) ([pim.kunis.nl](https://pim.kunis.nl)) - Static website using [Jekyll](https://jekyllrb.com/) ([pizzapim.nl](https://pizzapim.nl))
- File sychronisation using [Syncthing](https://syncthing.net/) - File sychronisation using [Syncthing](https://syncthing.net/)
- Microblogging server using [Mastodon](https://joinmastodon.org/) ([social.pizzapim.nl](https://social.pizzapim.nl)) - Microblogging server using [Mastodon](https://joinmastodon.org/) ([social.pizzapim.nl](https://social.pizzapim.nl))
- Calendar and contact synchronisation using [Radicale](https://radicale.org/v3.html) ([dav.pim.kunis.nl](https://dav.pim.kunis.nl)) - Calendar and contact synchronisation using [Radicale](https://radicale.org/v3.html) ([dav.pizzapim.nl](https://dav.pizzapim.nl))
- KMS server using [vlmcsd](https://github.com/Wind4/vlmcsd) - KMS server using [vlmcsd](https://github.com/Wind4/vlmcsd)
- Cloud file storage using [Seafile](https://www.seafile.com) - Cloud file storage using [Seafile](https://www.seafile.com)
- Disposable mail server using [Inbucket](https://inbucket.org) - Inbucket disposable webmail, Mailinator alternative (https://inbucket.org)
- Digital toolbox using [Cyberchef](https://cyberchef.geokunis2.nl)
- Jitsi Meet (https://meet.jit.si) - Jitsi Meet (https://meet.jit.si)
- Backups using [Borg](https://www.borgbackup.org/) and [Borgmatic](https://torsion.org/borgmatic/)
- RSS feed reader using [FreshRSS](https://miniflux.app/) - RSS feed reader using [FreshRSS](https://miniflux.app/)
- Metrics using [Prometheus](https://prometheus.io/) - Metrics using [Prometheus](https://prometheus.io/)
- Latex editor using [Overleaf](https://www.overleaf.com/) ([latex.pim.kunis.nl](https://latex.pim.kunis.nl))
- Markdown editor using [Hedgedoc](https://hedgedoc.org/) ## Possible future services
- matrix
- peertube?
- Pixelfed?
- Prometheus
- Concourse CI?
## TODO
- Clear view of what services + which versions we are running. This way, we can track security updates better.
- Host tobb website?
- Move from Ubuntu to Debian
- move Mastodon to pim.kunis.nl
- Podman
- Replace watchtower with Podman features
- Move nginx static content server to this repo
- Move dataserver to its own repo
### NSD
#### ZSK Rollover
Could make automatic key rollovers with cron or some other tool.
#### Idempotency
Currently I always resign zones.
But for idempotency I should probably only do it if the zone has changed or the keys have changed.
### Firewall
A little more difficult because of docker networking but probably doable.

View file

@ -1,4 +1,5 @@
[defaults] [defaults]
# (pathspec) Colon separated paths in which Ansible will search for Roles.
roles_path=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles roles_path=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles
inventory=inventory inventory=inventory
vault_password_file=util/secret-service-client.sh vault_password_file=util/secret-service-client.sh

View file

@ -1,5 +0,0 @@
all:
hosts:
max:
ansible_user: root
ansible_host: max.dmz

View file

@ -1,36 +0,0 @@
- name: Wait for servers to come up
hosts: max
gather_facts: no
roles:
- 'cloudinit-wait'
- name: Start services
hosts: max
pre_tasks:
- name: Create base service directory
file:
path: "{{ base_service_dir }}"
state: directory
- name: Delete externally managed environment file
shell:
cmd: "rm /usr/lib/python*/EXTERNALLY-MANAGED"
register: rm
changed_when: "rm.rc == 0"
failed_when: "false"
roles:
- {role: 'setup-apt', tags: 'setup-apt'}
- {role: 'watchtower', tags: 'watchtower'}
- {role: 'forgejo', tags: 'forgejo'}
- {role: 'syncthing', tags: 'syncthing'}
- {role: 'kms', tags: 'kms'}
- {role: 'cyberchef', tags: 'cyberchef'}
- {role: 'radicale', tags: 'radicale'}
- {role: 'mastodon', tags: 'mastodon'}
- {role: 'seafile', tags: 'seafile'}
- {role: 'jitsi', tags: 'jitsi'}
- {role: 'freshrss', tags: 'freshrss'}
- {role: 'static', tags: 'static'}
- {role: 'inbucket', tags: 'inbucket'}
- {role: 'prometheus', tags: 'prometheus'}
- {role: 'overleaf', tags: 'overleaf'}
- {role: 'hedgedoc', tags: 'hedgedoc'}

View file

@ -1,9 +0,0 @@
- name: setup-apt
src: https://github.com/sunscrapers/ansible-role-apt.git
scm: git
- name: cloudinit-wait
src: https://git.pim.kunis.nl/pim/ansible-role-cloudinit-wait
scm: git
- name: docker
src: https://git.pim.kunis.nl/pim/ansible-role-docker
scm: git

View file

@ -1,22 +0,0 @@
version: "3.7"
services:
cyberchef-server:
image: mpepping/cyberchef
container_name: cyberchef
restart: always
labels:
- traefik.enable=true
- traefik.http.routers.cyberchef.entrypoints=websecure
- traefik.http.routers.cyberchef.rule=Host(`cyberchef.geokunis2.nl`)
- traefik.http.routers.cyberchef.tls=true
- traefik.http.routers.cyberchef.tls.certresolver=letsencrypt
- traefik.http.services.cyberchef.loadbalancer.server.port=8000
- traefik.http.routers.cyberchef.service=cyberchef
- traefik.docker.network=traefik
networks:
- traefik
networks:
traefik:
external: true

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,13 +0,0 @@
- name: Create app directory
file:
path: "{{ service_dir }}"
state: directory
- name: Copy Docker Compose script
copy:
src: "{{ role_path }}/files/docker-compose.yml"
dest: "{{ service_dir }}/docker-compose.yml"
- name: Start the Docker Compose
docker_compose:
project_src: "{{ service_dir }}"
pull: true
remove_orphans: true

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,22 +0,0 @@
- name: Create service directory
file:
path: "{{ service_dir }}"
state: directory
- name: Copy Docker Compose script
template:
src: "{{ role_path }}/templates/docker-compose.yml.j2"
dest: "{{ service_dir }}/docker-compose.yml"
- name: Create data directory
file:
path: "{{ data_dir }}"
state: directory
- name: Create uploads directory
file:
path: "{{ data_dir }}/uploads"
state: directory
mode: 0777
- name: Start the Docker Compose
docker_compose:
project_src: "{{ service_dir }}"
pull: true
remove_orphans: true

View file

@ -1,51 +0,0 @@
version: '3'
networks:
traefik:
external: true
internal:
external: false
services:
database:
image: postgres:13.4-alpine
container_name: hedgedoc-database
environment:
- POSTGRES_USER=hedgedoc
- POSTGRES_PASSWORD=password
- POSTGRES_DB=hedgedoc
volumes:
- {{ data_dir }}/database:/var/lib/postgresql/data
restart: always
networks:
- internal
app:
image: quay.io/hedgedoc/hedgedoc:1.9.7
container_name: hedgedoc
environment:
- CMD_DB_URL=postgres://hedgedoc:password@database:5432/hedgedoc
- CMD_DOMAIN={{ hedgedoc_domain }}
- CMD_PORT=3000
- CMD_URL_ADDPORT=false
- CMD_ALLOW_ANONYMOUS=true
- CMD_ALLOW_EMAIL_REGISTER=false
- CMD_PROTOCOL_USESSL=true
- CMD_SESSION_SECRET={{ session_secret }}
volumes:
- {{ data_dir }}/uploads:/hedgedoc/public/uploads
restart: always
depends_on:
- database
networks:
- traefik
- internal
labels:
- traefik.enable=true
- traefik.http.routers.hedgedoc.entrypoints=websecure
- traefik.http.routers.hedgedoc.rule=Host(`{{ hedgedoc_domain }}`)
- traefik.http.routers.hedgedoc.tls=true
- traefik.http.routers.hedgedoc.tls.certresolver=letsencrypt
- treafik.http.routers.hedgedoc.service=hedgedoc
- traefik.http.services.hedgedoc.loadbalancer.server.port=3000
- traefik.docker.network=traefik

View file

@ -1,14 +0,0 @@
service_name: hedgedoc
data_dir: "{{ base_data_dir }}/{{ service_name }}"
service_dir: "{{ base_service_dir }}/{{ service_name }}"
hedgedoc_domain: "md.{{ domain_name_pim }}"
session_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
30633835386265643561343033326536653166343630396139303137613138383233666565666330
3032613865333836656566626435383165396539323837350a376331306464643766373839386638
65653865343539633636323833343964636332636461386434386432306230343833343431363134
6563373138626637650a633932313862326231666330343662343765666166373961376237396434
33396131353830323063326266623862353731653665626466653335656434303033353333353164
61613535373037646565386131383631366338616565373261396136616433393462313537313861
35313661616365373231373963323865393635626132343138363230313431636333363130346239
32656335333635613736

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,13 +0,0 @@
- name: Create service directory
file:
path: "{{ service_dir }}"
state: directory
- name: Copy Docker Compose script
template:
src: "{{ role_path }}/templates/docker-compose.yml.j2"
dest: "{{ service_dir }}/docker-compose.yml"
- name: Start the Docker Compose
docker_compose:
project_src: "{{ service_dir }}"
pull: true
remove_orphans: true

View file

@ -1,107 +0,0 @@
version: '2.2'
networks:
traefik:
external: true
internal:
external: false
services:
sharelatex:
restart: always
image: sharelatex/sharelatex
container_name: overleaf
networks:
- traefik
- internal
depends_on:
overleaf-mongodb:
condition: service_healthy
overleaf-redis:
condition: service_started
links:
- overleaf-mongodb
- overleaf-redis
stop_grace_period: 60s
volumes:
- {{ data_dir }}/overleaf/sharelatex_data:/var/lib/sharelatex
labels:
- traefik.enable=true
- traefik.http.routers.overleaf.entrypoints=websecure
- traefik.http.routers.overleaf.rule=Host(`latex.pim.kunis.nl`)
- traefik.http.routers.overleaf.tls=true
- traefik.http.routers.overleaf.tls.certresolver=letsencrypt
- treafik.http.routers.overleaf.service=overleaf
- traefik.http.services.overleaf.loadbalancer.server.port=80
- traefik.docker.network=traefik
environment:
SHARELATEX_APP_NAME: Overleaf Community Edition
SHARELATEX_MONGO_URL: mongodb://overleaf-mongodb:27017/sharelatex
# Same property, unfortunately with different names in
# different locations
SHARELATEX_REDIS_HOST: overleaf-redis
REDIS_HOST: overleaf-redis
ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file'
# Enables Thumbnail generation using ImageMagick
ENABLE_CONVERSIONS: 'true'
# Disables email confirmation requirement
EMAIL_CONFIRMATION_DISABLED: 'true'
# temporary fix for LuaLaTex compiles
# see https://github.com/overleaf/overleaf/issues/695
TEXMFVAR: /var/lib/sharelatex/tmp/texmf-var
## Set for SSL via nginx-proxy
#VIRTUAL_HOST: 103.112.212.22
SHARELATEX_SITE_URL: https://latex.pim.kunis.nl
# SHARELATEX_NAV_TITLE: Our ShareLaTeX Instance
# SHARELATEX_HEADER_IMAGE_URL: http://somewhere.com/mylogo.png
SHARELATEX_ADMIN_EMAIL: pim@kunis.nl
# SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"},{"text": "Another page I want to link to can be found <a href=\"here\">here</a>"} ]'
# SHARELATEX_RIGHT_FOOTER: '[{"text": "Hello I am on the Right"} ]'
SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@kunis.nl"
SHARELATEX_EMAIL_SMTP_HOST: "smtp.tweak.nl"
SHARELATEX_EMAIL_SMTP_PORT: 587
SHARELATEX_EMAIL_SMTP_USER: ""
SHARELATEX_EMAIL_SMTP_PASS: ""
# SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
# SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false
# SHARELATEX_EMAIL_SMTP_NAME: '127.0.0.1'
# SHARELATEX_EMAIL_SMTP_LOGGER: true
# SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by department x"
overleaf-mongodb:
restart: always
image: mongo:4.4
container_name: overleaf-mongodb
networks:
- internal
expose:
- 27017
volumes:
- {{ data_dir }}/overleaf/mongo_data:/data/db
healthcheck:
test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
interval: 10s
timeout: 10s
retries: 5
overleaf-redis:
restart: always
image: redis:5
container_name: overleaf-redis
networks:
- internal
expose:
- 6379
volumes:
- {{ data_dir }}/overleaf/redis_data:/data

View file

@ -1,3 +0,0 @@
service_name: overleaf
data_dir: "{{ base_data_dir}}/{{service_name}}"
service_dir: "{{ base_service_dir}}/{{service_name}}"

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1 +0,0 @@
testje

View file

@ -1,2 +0,0 @@
dependencies:
- role: traefik

View file

@ -1,2 +0,0 @@
dependencies:
- role: docker

View file

@ -1,2 +0,0 @@
dependencies:
- role: docker

View file

@ -0,0 +1,8 @@
borg_public_key: "AAAAC3NzaC1lZDI1NTE5AAAAIBTag7YToG5W+H2kEUz40kOH+7cs0Lp3owFFKkmHBiWM"
dataserver_public_key: "AAAAC3NzaC1lZDI1NTE5AAAAIJsLVptkoOwmxs6DnenN8u7Q1Tm/Psh0QdI6vjrTgb6D"
kingston1tb_mount_point: "/mnt/kingston1TB"
backup_location: "{{ kingston1tb_mount_point }}/homeserver_backup"
admin_public_keys:
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINUZp4BCxf7uLa1QWonx/Crf8tYZ5MKIZ+EuaBa82LrV user@user-laptop"
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOodpLr+FDRyKyHjucHizNLVFHZ5AQmE9GmxMnOsSoaw pimkunis@thinkpadpim"

View file

@ -0,0 +1 @@
kingston1tb_uuid: "622a8d81-aa2f-460b-a563-c3cdb6285609"

View file

@ -1,6 +1,5 @@
base_data_dir: /mnt/data base_data_dir: /data
base_service_dir: /srv base_service_dir: /srv
domain_name_pim: pim.kunis.nl
# Additional open ports # Additional open ports
jitsi_videobridge_port: 54562 jitsi_videobridge_port: 54562
@ -8,8 +7,6 @@ git_ssh_port: 56287
prometheus_port: 8081 prometheus_port: 8081
traefik_api_port: 8080 traefik_api_port: 8080
internal_forgejo_port: 3000 # Needed to pull from a repository from another docker container. internal_forgejo_port: 3000 # Needed to pull from a repository from another docker container.
internal_matrix_port: 3001 # Needed for proxying through NGINX
docker_daemon_config: domain_name_pim: pim.kunis.nl
default-address-pools:
- base: "10.204.0.0/16"
size: 24

12
inventory/hosts.yml Normal file
View file

@ -0,0 +1,12 @@
all:
children:
homeserver:
hosts:
max:
ansible_user: root
ansible_host: max.lan
dataserver:
hosts:
lewis:
ansible_user: root
ansible_host: lewis.lan

23
playbooks/all.yml Normal file
View file

@ -0,0 +1,23 @@
- name: Setup homeserver
hosts: homeserver
roles:
- {role: 'ssh', tags: 'ssh'}
- {role: 'watchtower', tags: 'watchtower'}
- {role: 'borg', tags: 'borg'}
- {role: 'nsd', tags: 'nsd'}
- {role: 'forgejo', tags: 'forgejo'}
- {role: 'syncthing', tags: 'syncthing'}
- {role: 'kms', tags: 'kms'}
- {role: 'radicale', tags: 'radicale'}
- {role: 'mastodon', tags: 'mastodon'}
- {role: 'seafile', tags: 'seafile'}
- {role: 'jitsi', tags: 'jitsi'}
- {role: 'freshrss', tags: 'freshrss'}
- {role: 'static', tags: 'static'}
- {role: 'inbucket', tags: 'inbucket'}
- {role: 'prometheus', tags: 'prometheus'}
- {role: 'matrix', tags: 'matrix'}
- name: Setup dataserver
hosts: dataserver
roles:
- {role: 'dataserver', tags: 'dataserver'}

7
playbooks/backup.yml Normal file
View file

@ -0,0 +1,7 @@
- name: Create backup
hosts: homeserver
tasks:
- name: Create backup
command:
cmd: systemctl start backup.service

View file

@ -0,0 +1,10 @@
[Unit]
Description=Backup data daily
[Timer]
OnCalendar=*-*-* 3:00:00
Persistent=true
RandomizedDelaySec=1h
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,25 @@
$ANSIBLE_VAULT;1.1;AES256
39646436383433653539316135323332303832633864366363313031636534353531386638323037
6364366663313964633239613261373733333736316534390a306262373634303536353365396138
35626433353935633534353636613232623531303765636139363139646265653361353164656363
3465316438373734330a636563346263633332353962353033336565356435353739646263343339
38633832343230393631633434323231313438336537383930646562356264346534663235323035
31643861306134663662353938643861393861333838633338613131363136333766353131313666
30393437616539643263386331343166636434323435666636386562353239373330336462653636
38306161393634356636613334323038366365626138326365303063313564653365313063643432
66306664356662326638363736366462343636393466303432323661323431393337306132386531
65663736643565363634373461666631356439373935353734636535636538626630666462653636
33363730626662313336633132393437666533363136643464653462646561393861376464366238
35383136333939653265366336356234613166353162366365346462633639396335653432353964
35303964633339356531343437393231303936623465383265666134316335666531636337383563
30326530396439363438396439313264643765366663343439646333326664633231626662666463
38616235353730346239396265306230623135626332636330666461333864306664346637396233
61343535396230363938306162313938363063353934323764656538666337656431363634333739
62373234356131373931333736373136343166636465643065643337386539376361383965343762
33633837626637393832366332343332303361306230626131346539323538383365316535666532
30666439643263653835666430393439396239333464336133316264323234643361336434343763
61306133373335353563646331303562326139613133356139366632363738316461633739333161
33666531653239626362363364346566373430656538356166346363333531656433393034333232
65353139623435383330353864336132313031656362386538626464313264333231653831373834
33363632616430303763616366356131323265313337323836396264623539316436616333383933
62653865623831626330

2
roles/borg/meta/main.yml Normal file
View file

@ -0,0 +1,2 @@
dependencies:
- role: common

38
roles/borg/tasks/main.yml Normal file
View file

@ -0,0 +1,38 @@
- name: Install borg
apt:
pkg:
- borgbackup
- borgmatic
- name: Create borg service directory
file:
path: "{{ service_dir }}"
state: directory
- name: Copy borg backup configuration
template:
src: "{{ role_path }}/templates/backup.yml.j2"
dest: "{{ service_dir }}/backup.yml"
- name: Copy private key
copy:
src: "{{ role_path }}/files/id_ed25519"
dest: "{{ service_dir }}/id_ed25519"
mode: 0600
- name: Copy systemd timer backup service
template:
src: "{{ role_path }}/templates/backup.service.j2"
dest: "/etc/systemd/system/backup.service"
register: service
- name: Copy systemd timer backup timer
copy:
src: "{{ role_path }}/files/backup.timer"
dest: "/etc/systemd/system/backup.timer"
register: timer
- name: Enable systemd timer
systemd:
name: backup.timer
enabled: true
state: started
daemon_reload: "{{ 'yes' if service.changed or timer.changed else 'no' }}"
- name: Restore backup
command:
cmd: "borgmatic extract --archive latest --destination / --config {{ service_dir }}/backup.yml"
creates: /data

View file

@ -0,0 +1,6 @@
[Unit]
Description=Backup data using borgmatic
[Service]
ExecStart=/usr/bin/borgmatic --config {{ service_dir }}/backup.yml
Type=oneshot

View file

@ -0,0 +1,17 @@
location:
source_directories:
- {{ base_data_dir }}
repositories:
- ssh://root@lewis.lan/{{ backup_location }}
retention:
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
storage:
ssh_command: ssh -i {{ service_dir }}/id_ed25519
unknown_unencrypted_repo_access_is_ok: true
hooks:
before_everything:
- systemctl stop docker docker.socket
after_everything:
- systemctl start docker

View file

@ -1,2 +1,2 @@
service_name: cyberchef service_name: borg
service_dir: "{{ base_service_dir }}/{{ service_name }}" service_dir: "{{ base_service_dir }}/{{ service_name }}"

View file

@ -0,0 +1,4 @@
nameserver 192.168.30.1
nameserver 1.1.1.1
nameserver 1.0.0.1
search lan

View file

@ -0,0 +1,26 @@
- name: APT upgrade
apt:
autoremove: true
upgrade: yes
state: latest
update_cache: yes
cache_valid_time: 86400 # One day
- name: Create base service directory
file:
path: "{{ base_service_dir }}"
state: directory
- name: Disable systemd-resolved
systemd:
name: systemd-resolved
enabled: false
state: stopped
- name: Copy resolv.conf
copy:
src: "{{ role_path }}/files/resolv.conf"
dest: /etc/resolv.conf
follow: true
- name: Add dataserver to known hosts
known_hosts:
name: "lewis.lan"
key: "lewis.lan ssh-ed25519 {{ dataserver_public_key }}"
state: present

View file

@ -0,0 +1,25 @@
$ANSIBLE_VAULT;1.1;AES256
38633038656332643033396338303864343332636434633331366266383235316235313236646361
6634313931303637616535373966316165656564366437330a393465356237626631303063363061
62323737343635316139636664663937333233323737376238656566633037613938383737306132
6237633230623962320a643433323532646261366532346234653332323336653162366433626465
31386461393535303730333865356364646137386634643630353831383039353763396536313439
30333335623364306166346232303862633636633066323062313531363234396362653232316261
36666132623030323332623334323632636639646239363032626364646334643461346662616366
39656266643937663531656137353031353130366238326535383261333539353439353566313537
38353632353039643530613766313033313063333331333733613939383731663262623766626266
64363061306166353633333634363332633461346538316661666364626639366132356434343631
61373432633863643237386435386633366161393934646562343261386335353638353033343932
62393633366163613064393966663830646237613265396462376238396639363566363865303861
36343666326632626166323430303137323236346137346131623636653236353061343633383437
61396534636166353038626162376335363137636164616631646261366332303135306237356432
61626261656332666536343039316333303431653931666233363366613166663266663130656633
39316363326532653665626136393135373863383234326638303466353930653038303433643536
30666237363230306634333162396562623034386232666465343631306433373764626634613635
63343965623163356536626162613863373033396565366361353538323933656165653932653937
34666538353139636366333765363733336134396566613134303530633666326165306131353535
33653133663166333964326330366530643730363861626261666366383334613661303762636663
34376531343732346630643466616638323537633665373333346162306361393836326533636630
61656335306337643930613662613832626530653630343566643661356666313331316438366538
37333166636639363838303665626137643731626338356662656338393335343239376635303633
35663237653238313133

View file

@ -0,0 +1,44 @@
- name: Add admins' authorized keys
authorized_key:
key: "{{ item }}"
user: "{{ ansible_user_id }}"
loop: "{{ admin_public_keys }}"
- name: Copy host public key
template:
src: "{{ role_path }}/templates/ssh_host_ed25519_key.pub.j2"
dest: "/etc/ssh/ssh_host_ed25519_key.pub"
mode: 0644
- name: Copy host private key
copy:
src: "{{ role_path }}/files/ssh_host_ed25519_key"
dest: "/etc/ssh/ssh_host_ed25519_key"
mode: 0600
- name: APT upgrade
apt:
autoremove: true
upgrade: yes
state: latest
update_cache: yes
cache_valid_time: 86400 # One day
- name: Create extra disk moint point
file:
path: "{{ kingston1tb_mount_point }}"
state: directory
- name: Mount extra disk
ansible.posix.mount:
path: "{{kingston1tb_mount_point }}"
src: "UUID={{ kingston1tb_uuid }}"
fstype: ext4
passno: 1
state: present
- name: Install borg
apt:
name: borgbackup
- name: Add Borg public key
authorized_key:
key: "ssh-ed25519 {{ borg_public_key }} root@max"
user: "{{ ansible_user_id }}"
- name: Create Borg repository
command:
cmd: "borg init -e none {{ backup_location }}"
creates: "{{ backup_location }}"

View file

@ -0,0 +1 @@
ssh-ed25519 {{ dataserver_public_key }} root@lewis

View file

@ -0,0 +1,7 @@
{
"default-address-pools":
[
{"base":"10.204.0.0/16","size":24}
]
}

View file

@ -0,0 +1,41 @@
- name: Install Docker prerequisites
apt:
pkg:
- ca-certificates
- curl
- gnupg
- lsb-release
- python3-pip
- name: Add Docker APT key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /etc/apt/keyrings/docker.gpg
- name: Add Docker repository
apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
register: apt_repository
- name: Update APT cache
apt:
update_cache: true
when: apt_repository.changed
- name: Install Docker packages
apt:
pkg:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
- name: Install Docker modules for Python
pip:
name:
- docker
- docker-compose
- name: Copy daemon.json
copy:
src: "{{ role_path }}/files/daemon.json"
dest: /etc/docker/daemon.json
- name: Start Docker
systemd:
name: docker
enabled: true
state: started

View file

@ -0,0 +1,16 @@
- name: Install firewalld
apt:
pkg:
- firewalld
state: latest
update_cache: true
- name: Allow SSH
firewalld:
service: ssh
permanent: yes
state: enabled
- name: Start firewalld
systemd:
enabled: true
name: sshd
state: started

View file

@ -0,0 +1,4 @@
dependencies:
- role: common
- role: docker
- role: traefik

View file

@ -4,7 +4,6 @@ RUN_USER = git
[repository] [repository]
ROOT = /data/git/repositories ROOT = /data/git/repositories
DEFAULT_BRANCH = master
[repository.local] [repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
@ -39,7 +38,6 @@ CHARSET = utf8
[indexer] [indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
ISSUE_INDEXER_TYPE = db
[session] [session]
PROVIDER_CONFIG = /data/gitea/sessions PROVIDER_CONFIG = /data/gitea/sessions

View file

@ -12,10 +12,10 @@ services:
- USER_UID=1000 - USER_UID=1000
- USER_GID=1000 - USER_GID=1000
restart: always restart: always
networks:
- traefik
ports: ports:
- "{{ internal_forgejo_port }}:3000" - "{{ internal_forgejo_port }}:3000"
networks:
- traefik
volumes: volumes:
- {{ data_dir }}:/data - {{ data_dir }}:/data
- {{ service_dir }}/conf:/data/gitea/conf - {{ service_dir }}/conf:/data/gitea/conf

View file

@ -3,6 +3,7 @@ data_dir: "{{ base_data_dir }}/{{ service_name }}"
service_dir: "{{ base_service_dir }}/{{ service_name }}" service_dir: "{{ base_service_dir }}/{{ service_name }}"
git_domain: "git.{{ domain_name_pim }}" git_domain: "git.{{ domain_name_pim }}"
forgejo: forgejo:
root_url: "https://{{ git_domain }}" root_url: "https://{{ git_domain }}"
mailer_host: "smtp.tweak.nl" mailer_host: "smtp.tweak.nl"

View file

@ -0,0 +1,4 @@
dependencies:
- role: common
- role: docker
- role: traefik

View file

@ -11,8 +11,10 @@ services:
options: options:
max-size: 10m max-size: 10m
volumes: volumes:
- {{ data_dir }}/data:/var/www/FreshRSS/data # Recommended volume for FreshRSS persistent data such as configuration and SQLite databases
- {{ data_dir }}/extensions:/var/www/FreshRSS/extensions - /data/freshrss/data:/var/www/FreshRSS/data
# Optional volume for storing third-party extensions
- /data/freshrss/extensions:/var/www/FreshRSS/extensions
environment: environment:
TZ: Europe/Amsterdam TZ: Europe/Amsterdam
CRON_MIN: '2,32' CRON_MIN: '2,32'

View file

@ -1,2 +1,4 @@
dependencies: dependencies:
- role: common
- role: docker - role: docker

View file

@ -0,0 +1,4 @@
dependencies:
- role: common
- role: docker
- role: traefik

4
roles/kms/meta/main.yml Normal file
View file

@ -0,0 +1,4 @@
dependencies:
- role: common
- role: docker

View file

@ -0,0 +1,4 @@
dependencies:
- role: common
- role: docker
- role: traefik

View file

@ -0,0 +1,32 @@
version: 1
formatters:
precise:
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
formatter: precise
loggers:
synapse.storage.SQL:
# beware: increasing this to DEBUG will make synapse log sensitive
# information such as access tokens.
level: INFO
root:
level: INFO
handlers: [console]
disable_existing_loggers: false

View file

@ -0,0 +1,8 @@
$ANSIBLE_VAULT;1.1;AES256
38363633306139626564313833363364653037613238396266303133663231643739373237666662
6639636136303666353639353632373530326263633264350a616465313137663731393464383263
65373565343462633733366636343766656666396531383638363232363565646364663035353333
3236383136353065660a353631326630623165366631666639343864633531383238643131373363
64303565363439343064393039323265623364633738373163373339376134643966333032326564
61646536633335633938336438663430643461623230666163636561303430393732663062393461
346332333463636566326364663465306565

View file

@ -0,0 +1,31 @@
- name: Create app directory
file:
path: "{{ service_dir }}"
state: directory
- name: Copy signing key
copy:
src: "{{ role_path }}/files/matrix.log.config"
dest: "{{ service_dir }}/matrix.log.config"
- name: Copy Docker Compose script
template:
src: "{{ role_path }}/templates/docker-compose.yml.j2"
dest: "{{ service_dir }}/docker-compose.yml"
- name: Copy homeserver.yaml
template:
src: "{{ role_path }}/templates/homeserver.yaml.j2"
dest: "{{ service_dir }}/homeserver.yaml"
register: homeserver
- name: Copy signing key
copy:
src: "{{ role_path }}/files/matrix.signing.key"
dest: "{{ service_dir }}/matrix.signing.key"
- name: Create data directory
file:
path: "{{ data_dir }}"
state: directory
- name: Start the Docker Compose
docker_compose:
project_src: "{{ service_dir }}"
pull: true
remove_orphans: true
restarted: "{{ homeserver.changed }}"

View file

@ -0,0 +1,41 @@
version: '3'
services:
synapse:
image: docker.io/matrixdotorg/synapse:v1.77.0
restart: unless-stopped
environment:
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
volumes:
- /data/matrix/uploads:/data/uploads
- /data/matrix/media:/data/media
- /srv/matrix/homeserver.yaml:/data/homeserver.yaml
- /srv/matrix/matrix.log.config:/data/matrix.log.config
- /srv/matrix/matrix.signing.key:/data/matrix.signing.key
depends_on:
- db
networks:
- traefik
ports:
- "{{ internal_matrix_port }}:8008"
labels:
- traefik.enable=true
- traefik.http.routers.matrix.entryPoints=websecure
- traefik.http.routers.matrix.rule=Host(`{{ matrix_domain }}`)
- traefik.http.routers.matrix.tls=true
- traefik.http.routers.matrix.tls.certResolver=letsencrypt
- traefik.http.routers.matrix.service=matrix
- traefik.http.services.matrix.loadbalancer.server.port=8008
db:
image: docker.io/postgres:12-alpine
environment:
- POSTGRES_USER=synapse
- POSTGRES_PASSWORD={{ database_password }}
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- /data/matrix/schemas:/var/lib/postgresql/data
networks:
traefik:
external: true

View file

@ -0,0 +1,35 @@
# Configuration file for Synapse.
#
# For more information on how to configure Synapse, including a complete accounting of
# each option, go to docs/usage/configuration/config_documentation.md or
# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html
server_name: "{{ matrix_domain }}"
pid_file: /data/homeserver.pid
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
args:
user: synapse
password: "{{ database_password }}"
host: db
cp_min: 5
cp_max: 10
log_config: "/data/matrix.log.config"
media_store_path: "/data/media"
registration_shared_secret: "{{ registration_shared_secret }}"
report_stats: false
macaroon_secret_key: "{{ macaroon_secret_key }}"
form_secret: "{{ form_secret }}"
signing_key_path: "/data/matrix.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
# vim:ft=yaml

View file

@ -0,0 +1,41 @@
service_name: matrix
data_dir: "{{ base_data_dir }}/{{ service_name }}"
service_dir: "{{ base_service_dir }}/{{ service_name }}"
matrix_domain: "matrix.{{ domain_name_pim }}"
registration_shared_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
66643364393432353763666361383331316635356431636530663162643134653939306438366533
3463393262623364336430363638356439393461393237650a626630633963343530643565323633
35613636386365393035666366636534306266613935653136666430366330323032653164363066
6531323364383131360a616465336164303030643132336264646333346666626138386331636164
65366438356238383234386662363631316334613439613739303165613363636261643934656665
32653764373939373739666263653261343036636365316566623934343261653436613962343335
343132326461336338323938326264666630
macaroon_secret_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
61656638626162383134356238393031346464623930363636376136633038623836323737633463
3733383661663339313965636134373037366235613562340a376334666266623438313066346166
64333564613438313861396632633464386236356236313461373461613632346538343837343264
3363623135613063300a333932363036353063653931616361363934633239653732343737373536
31366265383939303664623565633435626530316430323036663261353334336264306162653361
38306437616333316638396161393164393766356566323362343565663630306465663133333733
343039623366313961393136356239373837
form_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
38646165646636353331323565343033396431623338633734653838633032363930323637656637
3931643733343537343534386137313737383562346534300a353535633239626332393831613661
39366230313234663930363962386336646639393566356437623937393062353134303138363734
6430653164656339660a613234313464653138313331333137646331323338346230643630636466
35383837356633303061663362626439653030333063383532373663316330373737323736326562
37313034363262346333343166343231316264303934366565643466396164333166643561373365
656533393033356363303933353231376466
database_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
38393732313834343631626234353261653536646434343561613264626162363839303432333133
3635333330626263666430353931666635393738643163300a633231343334666331373936333565
36376164396464623233613033636562626630623730633730666333363437613234636638356630
3336373235336232630a353732653331623963313865333765633965353630363733386534313639
38643839323733393031373139376662326134653965646366663631396464393861636538313563
3934363539366139346633626433396438663739393332663030

View file

@ -0,0 +1 @@
geokunis2.nl. IN DNSKEY 257 3 15 8DFshejNxv4d9ZkSRY53kEay06aOhHm77EOYNSZFp/w= ;{id = 64014 (ksk), size = 256b}

View file

@ -0,0 +1,10 @@
$ANSIBLE_VAULT;1.1;AES256
33306239336639653065343862633935396534373739613332356638343037646530333331343835
6464303336356534653431663938383732383863366238320a663430613133363134336264343734
31343731373239613330633935636137646133616334353565663061356566666465326261306362
3463633863626666330a383461656632346361646365383234653963333561366463373331346539
30633237346532633634636537663936353337353331393663363363363566663738643632363761
66323032383862306635656130366261303161636232633561313630316537626262356532313131
63616437633333346431303539306433613130373934393036356563316365373966346536353764
39343038373162303933653335393432636332613038366531353432346332333936656464626536
64633030353336616561656539313863306534633863633835333531306533313930

View file

@ -0,0 +1 @@
pizzapim.nl. IN DNSKEY 257 3 15 PL2LJmmaooqVFVIrvdFzS+X0YiEgz+fLlr7jm54nX/E= ;{id = 47515 (ksk), size = 256b}

View file

@ -0,0 +1,10 @@
$ANSIBLE_VAULT;1.1;AES256
36343534663736653462386238363734646238306365393233633530663039656335623961663131
6436373566336464336330326438656137646536656333370a386539613239343962373562653264
66616530343235333964343332386234666266643933393531323066666164623862633962376666
3230333539393335630a653532396665383536633164643534303461636135653737616137313034
33653838653538623934353631393636363937333831313036643334343261363836393235313235
36613966343431333364336437393430653366643263643130376437663164353361633735616332
35656666353037643739356133303064633166323535323265323134363963316566323165643165
36656264353962346530323830623432616238653966613433616235336539396461376162316564
61643465323165643961303639653466663961333531663133636666643437333233

24
roles/nsd/files/nsd.conf Normal file
View file

@ -0,0 +1,24 @@
server:
ip-address: enp3s0
server-count: 1
verbosity: 1
hide-version: yes
zonesdir: "/etc/nsd/zones"
ip-transparent: yes
ip-freebind: yes
zone:
name: pizzapim.nl
zonefile: pizzapim.nl.signed
provide-xfr: 87.253.155.96/27 NOKEY
provide-xfr: 157.97.168.160/27 NOKEY
zone:
name: geokunis2.nl
zonefile: geokunis2.nl.signed
provide-xfr: 87.253.155.96/27 NOKEY
provide-xfr: 157.97.168.160/27 NOKEY
zone:
name: pim.kunis.nl
zonefile: pim.kunis.nl

View file

@ -0,0 +1,26 @@
$ORIGIN geokunis2.nl.
$TTL 60
geokunis2.nl. IN SOA ns.geokunis2.nl. niels.kunis.nl. 2023021700 1800 3600 1209600 3600
NS ns.geokunis2.nl.
NS ns0.transip.net.
NS ns1.transip.nl.
NS ns2.transip.eu.
A 84.245.14.149
AAAA 2a02:58:19a:f730:b62e:99ff:fe77:1bda
; MX 0 .
; TXT "v=spf1 -all"
CAA 0 issue "letsencrypt.org"
mail IN A 84.245.14.149
MX 10 mail.geokunis2.nl
jenl IN A 217.123.41.225
wg IN A 84.245.14.149
wg IN AAAA 2a02:58:19a:f710:45aa:5179:2b45:376d
wg4 IN A 84.245.14.149
wg6 IN AAAA 2a02:58:19a:f710:45aa:5179:2b45:376d
kms IN A 84.245.14.149
files IN A 84.245.14.149
files IN AAAA 2a02:58:19a:f730:b62e:99ff:fe77:1bda
_dmarc IN TXT "v=DMARC1; p=reject; fo=0; adkim=s; aspf=s; pct=100; rf=afrf; sp=reject"
ns A 84.245.14.149
AAAA 2a02:58:19a:f730:b62e:99ff:fe77:1bda

View file

@ -0,0 +1,20 @@
$ORIGIN pim.kunis.nl.
$TTL 60
pim.kunis.nl. IN SOA ns.pim.kunis.nl. pim.kunis.nl. 2023022500 1800 3600 1209600 3600
NS ns.pim.kunis.nl.
A 84.245.14.149
TXT "v=spf1 ~all"
_dmarc IN TXT "v=DMARC1; p=reject; aspf=s; adkim=s; rua=mailto:wpux1bq8@ag.eu.dmarcian.com;"
www IN A 84.245.14.149
ns IN A 84.245.14.149
social IN CNAME www.pim.kunis.nl.
dav IN CNAME www.pim.kunis.nl.
git IN CNAME www.pim.kunis.nl.
meet IN CNAME www.pim.kunis.nl.
rss IN CNAME www.pim.kunis.nl.
matrix IN CNAME www.pim.kunis.nl.

View file

@ -0,0 +1,19 @@
$ORIGIN pizzapim.nl.
$TTL 60
pizzapim.nl. IN SOA ns.pizzapim.nl. pim.kunis.nl. 2023020900 1800 3600 1209600 3600
NS ns.pizzapim.nl.
NS ns0.transip.net.
NS ns1.transip.nl.
NS ns2.transip.eu.
A 84.245.14.149
TXT "v=spf1 ~all"
CAA 0 issue "letsencrypt.org"
_dmarc IN TXT "v=DMARC1; p=reject; aspf=s; adkim=s; rua=mailto:wpux1bq8@ag.eu.dmarcian.com;"
social IN A 84.245.14.149
AAAA 2a02:58:19a:f730:b62e:99ff:fe77:1bda
ns IN A 84.245.14.149
AAAA 2a02:58:19a:f730:b62e:99ff:fe77:1bda

2
roles/nsd/meta/main.yml Normal file
View file

@ -0,0 +1,2 @@
dependencies:
- role: common

70
roles/nsd/tasks/main.yml Normal file
View file

@ -0,0 +1,70 @@
- name: Install nsd
apt:
pkg:
- nsd
- ldnsutils
- name: Copy nsd.conf
copy:
src: "{{ role_path }}/files/nsd.conf"
dest: /etc/nsd/nsd.conf
- name: Create zones directory
file:
path: /etc/nsd/zones
state: directory
- name: Copy zone files
copy:
src: "{{ role_path }}/files/zones/"
dest: /etc/nsd/zones
- name: Create keys directory
file:
path: /etc/nsd/keys
state: directory
- name: Copy KSK private keys
template:
src: "{{ item }}"
dest: "/etc/nsd/keys/{{ item | basename }}"
with_fileglob:
- "{{ role_path }}/files/keys/*.ksk.private"
- name: Copy KSK keys
copy:
src: "{{ item }}"
dest: "/etc/nsd/keys/{{ item | basename }}"
with_fileglob:
- "{{ role_path }}/files/keys/*.ksk.key"
- name: Check if ZSKs exist
stat:
path: "/etc/nsd/keys/K{{ item | basename }}.zsk.key"
register: zsks_exists
with_fileglob:
- "{{ role_path }}/files/zones/*"
- name: Create ZSK
command:
cmd: "ldns-keygen -a ED25519 {{ item.item | basename }}"
chdir: /etc/nsd/keys
register: create_zsk
when: not item.stat.exists and (item.item | basename) in sign_zones
with_items: "{{ zsks_exists.results }}"
- name: Rename ZSK key
command:
cmd: "mv {{ item.stdout }}.key K{{ item.item.item | basename }}.zsk.key"
chdir: /etc/nsd/keys
when: item.changed and (item.item | basename) in sign_zones
with_items: "{{ create_zsk.results }}"
- name: Rename ZSK private key
command:
cmd: "mv {{ item.stdout }}.private K{{ item.item.item | basename }}.zsk.private"
chdir: /etc/nsd/keys
when: item.changed and (item.item | basename) in sign_zones
with_items: "{{ create_zsk.results }}"
- name: Sign zones
command:
cmd: "ldns-signzone {{ item | basename }} /etc/nsd/keys/K{{ item | basename }}.zsk /etc/nsd/keys/K{{ item | basename }}.ksk"
chdir: /etc/nsd/zones
when: (item | basename) in sign_zones
with_fileglob:
- "{{ role_path }}/files/zones/*"
- name: Restart NSD
systemd:
name: nsd
enabled: true
state: reloaded

3
roles/nsd/vars/main.yml Normal file
View file

@ -0,0 +1,3 @@
sign_zones:
- geokunis2.nl
- pizzapim.nl

View file

@ -1,2 +1,3 @@
dependencies: dependencies:
- role: common
- role: docker - role: docker

View file

@ -9,7 +9,7 @@ stock = utf-8
[auth] [auth]
realm = Radicale - Password Required realm = Radicale - Password Required
type = htpasswd type = htpasswd
htpasswd_filename = /config/users htpasswd_filename = /radicale/users
htpasswd_encryption = md5 htpasswd_encryption = md5
[rights] [rights]

View file

@ -0,0 +1,4 @@
dependencies:
- role: common
- role: docker
- role: traefik

Some files were not shown because too many files have changed in this diff Show more