manage lewis with nix

move docker swarm ansible to this repo
move thecloud ansible to this repo
support data disks in terraform
This commit is contained in:
Pim Kunis 2023-12-25 19:22:22 +01:00
parent d7ef46b642
commit 111bf68a0a
92 changed files with 2730 additions and 26 deletions

View file

@ -0,0 +1,5 @@
# shoarma ansible
This requires a rootless docker daemon on the Ansible host.
See: https://docs.docker.com/engine/security/rootless/
Also you need jsondiff for docker stack.

View file

@ -0,0 +1,9 @@
[defaults]
roles_path=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles
inventory=inventory
interpreter_python=/usr/bin/python3
remote_user = root
vault_password_file=$HOME/.config/home/ansible-vault-secret
[diff]
always = True

View file

@ -0,0 +1,81 @@
data_directory_base: /mnt/data
git_ssh_port: 56287
elasticsearch_port: 14653
fluent_forward_port: 24224
concourse_public_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBSVLcr617iJt+hqLFSsOQy1JeueLIAj1eRfuI+KeZAu pim@x260"
nfs_shares:
- name: nextcloud_data
path: /mnt/data/nextcloud/data
- name: radicale
path: /mnt/data/radicale
- name: freshrss_data
path: /mnt/data/freshrss/data
- name: freshrss_extensions
path: /mnt/data/freshrss/extensions
- name: pihole_data
path: /mnt/data/pihole/data
- name: pihole_dnsmasq
path: /mnt/data/pihole/dnsmasq
- name: hedgedoc_uploads
path: /mnt/data/hedgedoc/uploads
- name: traefik_acme
path: /mnt/data/traefik/acme
- name: seafile_data
path: /mnt/data/seafile/data
- name: seafile_db
path: /mnt/data/seafile/db
- name: mastodon_system
path: /mnt/data/mastodon/system
- name: mastodon_redis
path: /mnt/data/mastodon/redis
- name: forgejo
path: /mnt/data/forgejo
- name: overleaf
path: /mnt/data/overleaf/data
- name: overleaf_redis
path: /mnt/data/overleaf/redis
- name: overleaf_mongodb
path: /mnt/data/overleaf/mongodb
- name: prometheus_data
path: /mnt/data/prometheus/data
- name: elasticsearch_certs
path: /mnt/data/elasticsearch/certs
- name: elasticsearch_data
path: /mnt/data/elasticsearch/data
- name: grafana_data
path: /mnt/data/grafana/data
- name: kitchenowl_data
path: /mnt/data/kitchenowl/data
- name: ampache_mysql
path: /mnt/data/ampache/mysql
- name: ampache_config
path: /mnt/data/ampache/config
- name: music
path: /mnt/data/nextcloud/data/data/pim/files/Music
database_passwords:
nextcloud: !vault |
$ANSIBLE_VAULT;1.1;AES256
66326230303135303930363761316534313439383365376231623661316635393839336431313262
3832626365376533646561653863316364313135343366330a356136343938666133356532613263
39663037623232363266376335643834353735363431636535386566643763386463353962663930
3466343563353162320a376437353933656166323364323166376663323531373338656563653463
33346263626430616164613937363836343430383233393061643231346661656539623938333631
3632373964346139316637663364646132636636373461613534
hedgedoc: !vault |
$ANSIBLE_VAULT;1.1;AES256
63363464666633663762393135333362613966636338623533393132376338343339653431396465
6634643863623163366235393434343662313735363438610a373065363361326565633766633835
38383637343230363031636634623930666365333739323162313937656239646166613738393965
3533666462303563360a313233306335396234393932396331313238376464363964363839396164
66366662356135343035363935616664613831626131376330643133313530636431613266636165
6265613666616164373637356235396165383662333561393939
mastodon: !vault |
$ANSIBLE_VAULT;1.1;AES256
63616366396665663161376161373735626466353464393963333136336335376662326232613639
6166333137376131633761623163306165386562666639640a313136386431373161306331626638
34643433396232383962643964386631313632393161316261353331346163333261336666646563
6232666231653732630a396638396462323464613033306662313463663262626430363432663465
63623935303861663565633739363539326435623561396535623034663735373232336633303037
6266323136316238343963613332396261346337646264646162

View file

@ -0,0 +1,13 @@
all:
hosts:
manager:
ansible_host: maestro.dmz
thecloud:
ansible_host: thecloud.dmz
children:
workers:
hosts:
bancomart:
ansible_host: bancomart.dmz
vpay:
ansible_host: vpay.dmz

View file

@ -0,0 +1,9 @@
---
- name: Remove a Docker swarm stack
hosts: manager
tasks:
- name: Remove the stack
docker_stack:
name: "{{ stack }}"
state: absent

View file

@ -0,0 +1,72 @@
---
- name: Wait for Cloud-init to finish
hosts: manager, workers
gather_facts: no
roles:
- cloudinit_wait
- name: Initialize Docker Swarm nodes
hosts: manager, workers
pre_tasks:
- 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:
- setup_apt
post_tasks:
- name: Install Docker
include_role:
name: docker
vars:
docker_daemon_config: {}
# log-driver: fluentd
# log-opts:
# fluentd-address: "localhost:22222"
# tag: "docker.{{ '{{' }}.Name{{ '}}' }}"
- name: Setup Docker Swarm manager
hosts: manager
tasks:
- name: Install pip packages
pip:
name:
- jsondiff
- pyyaml
- name: Create Docker Swarm
docker_swarm:
- name: Get Docker Swarm manager info
docker_swarm_info:
nodes: yes
nodes_filters:
name: manager
register: swarm_info
- hosts: workers
tasks:
- name: Join Docker Swarm
docker_swarm:
state: join
join_token: "{{ hostvars.manager.swarm_info.swarm_facts.JoinTokens.Worker }}"
remote_addrs:
- "{{ hostvars.manager.ansible_default_ipv4.address }}"
- hosts: manager
tasks:
- name: Add concourse to authorized keys
authorized_key:
user: root
key: "{{ concourse_public_key }}"
- hosts: manager, workers
tasks:
- name: Increase vm.max_map_count
sysctl:
name: vm.max_map_count
value: 262144

View file

@ -0,0 +1,23 @@
---
- name: Start Docker stacks
hosts: manager
roles:
- {role: traefik, tags: traefik}
- {role: forgejo, tags: forgejo}
# - {role: seafile, tags: seafile}
- {role: radicale, tags: radicale}
# - {role: mastodon, tags: mastodon}
- {role: freshrss, tags: freshrss}
- {role: hedgedoc, tags: hedgedoc}
# - {role: overleaf, tags: overleaf}
- {role: cyberchef, tags: cyberchef}
- {role: inbucket, tags: inbucket}
- {role: kms, tags: kms}
- {role: swarm_dashboard, tags: swarm_dashboard}
# - {role: shephard, tags: shephard}
- {role: pihole, tags: pihole}
- {role: nextcloud, tags: nextcloud}
- {role: syncthing, tags: syncthing}
# - {role: monitoring, tags: monitoring}
- {role: kitchenowl, tags: kitchenowl}
# - {role: ampache, tags: ampache}

View file

@ -0,0 +1,25 @@
---
- name: Create databases and NFS shares
hosts: thecloud
handlers:
- name: reload nfs
systemd:
name: nfs-kernel-server
state: restarted
tasks:
- name: Create nfs shares
with_items: "{{ nfs_shares }}"
copy:
dest: "/etc/exports.d/{{ item.name }}.exports"
content: "{{ item.path }} *(rw,sync,no_subtree_check,no_root_squash)"
notify: reload nfs
- name: Create databases
with_items: "{{ database_passwords | dict2items }}"
include_role:
name: postgresql_database
vars:
database_name: "{{ item.key }}"
database_user: "{{ item.key }}"
database_password: "{{ item.value }}"

View file

@ -0,0 +1,14 @@
---
roles:
- name: setup_apt
src: https://github.com/sunscrapers/ansible-role-apt.git
scm: git
- name: docker
src: https://git.kun.is/pim/ansible-role-docker
scm: git
- name: cloudinit_wait
src: https://git.kun.is/pim/ansible-role-cloudinit-wait
scm: git
- name: postgresql_database
src: https://git.kun.is/home/ansible-role-postgresql-database
scm: git

View file

@ -0,0 +1,56 @@
# vi: ft=yaml
version: '3.7'
networks:
traefik:
external: true
volumes:
ampache_mysql:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/ampache/mysql"
ampache_config:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/ampache/config"
music:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/nextcloud/data/data/pim/files/Music"
services:
ampache:
image: ampache/ampache:6
volumes:
- type: volume
source: ampache_mysql
target: /var/lib/mysql
volume:
nocopy: true
- type: volume
source: ampache_config
target: /var/www/config
volume:
nocopy: true
- type: volume
source: music
target: /media
read_only: true
volume:
nocopy: true
networks:
- traefik
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.ampache.entrypoints=websecure
- traefik.http.routers.ampache.rule=Host(`music.kun.is`)
- traefik.http.routers.ampache.tls=true
- traefik.http.routers.ampache.tls.certresolver=letsencrypt
- traefik.http.routers.ampache.service=ampache
- traefik.http.services.ampache.loadbalancer.server.port=80
- traefik.docker.network=traefik

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: ampache
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,20 @@
# vi: ft=yaml
version: "3.7"
networks:
traefik:
external: true
services:
cyberchef:
image: mpepping/cyberchef
networks:
- traefik
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.cyberchef.entrypoints=websecure
- traefik.http.services.cyberchef.loadbalancer.server.port=8000
- traefik.http.routers.cyberchef.rule=Host(`cyberchef.geokunis2.nl`)
- traefik.http.routers.cyberchef.tls=true
- traefik.http.routers.cyberchef.tls.certresolver=letsencrypt

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: cyberchef
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,106 @@
# vi: ft=yaml
version: '3'
networks:
traefik:
external: true
discourse:
services:
discourse-app:
image: tiredofit/discourse:latest
deploy:
placement:
constraints:
- "node.labels.discourse == true"
labels:
- traefik.enable=true
- traefik.http.routers.discourse.entrypoints=localsecure
- traefik.http.routers.discourse.rule=Host(`tuindersweijde.geokunis2.nl`)
- traefik.http.services.discourse.loadbalancer.server.port=3000
- traefik.http.routers.discourse.tls=true
- traefik.http.routers.discourse.tls.certresolver=letsencrypt
volumes:
- type: bind
source: /mnt/data/discourse/logs
target: /data/logs
- type: bind
source: /mnt/data/discourse/uploads
target: /data/uploads
- type: bind
source: /mnt/data/discourse/backups
target: /data/backups
environment:
- TIMEZONE=Europe/Amsterdam
- CONTAINER_NAME=discourse-app
- DB_HOST=discourse-db
- DB_NAME=discourse
- DB_USER=discourse
- DB_PASS={{ database_password }}
- REDIS_HOST=discourse-redis
- SITE_HOSTNAME=discourse.pim.kunis.nl
- SMTP_PORT=25
- SMTP_HOST=smtp.tweak.nl
- DEVELOPER_EMAILS=niels@kunis.nl
networks:
- traefik
- discourse
discourse-db:
image: tiredofit/postgres:15-latest
deploy:
placement:
constraints:
- "node.labels.discourse == true"
volumes:
- type: bind
source: /mnt/data/discourse/database
target: /var/lib/postgresql/data
environment:
- TIMEZONE=Europe/Amsterdam
- CONTAINER_NAME=discourse-db
- POSTGRES_DB=discourse
- POSTGRES_USER=discourse
- POSTGRES_PASSWORD={{ database_password }}
- SUPERUSER_PASS={{ database_password }}
networks:
- discourse
discourse-redis:
image: tiredofit/redis:7
deploy:
placement:
constraints:
- "node.labels.discourse == true"
volumes:
- type: bind
source: /mnt/data/discourse/redis
target: /var/lib/redis
environment:
- TIMEZONE=Europe/Amsterdam
- CONTAINER_NAME=discourse-redis
networks:
- discourse
# discourse-db-backup:
# image: tiredofit/db-backup
# volumes:
# - ./dbbackup:/backup
# environment:
# - TIMEZONE=America/Vancouver
# - CONTAINER_NAME=discourse-db-backup
# - DB_HOST=discourse-db
# - DB_TYPE=postgres
# - DB_NAME=discourse
# - DB_USER=discourse
# - DB_PASS=password
# - DB_DUMP_FREQ=1440
# - DB_DUMP_BEGIN=0000
# - DB_CLEANUP_TIME=8640
# networks:
# - discourse

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: discourse
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,7 @@
database_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
32323538323136633363393961343364363933396431376437633361333232383938336531396537
3730306164303364363739376633633431383534646135380a326663396338396461623037613637
37666630333433393764373864346262383536656664343631386234386438333263626166363633
3735633562323361330a353735366562663631363634343438326562646533663839383932343263
38366538336466373733313130616330326238653966623037343336326132356565

View file

@ -0,0 +1,109 @@
APP_NAME = Forgejo: Beyond coding. We forge.
RUN_MODE = prod
RUN_USER = git
WORK_PATH=/data/gitea
[repository]
ROOT = /data/git/repositories
DEFAULT_BRANCH = master
[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[server]
APP_DATA_PATH = /data/gitea
DOMAIN = {{ git_domain }}
SSH_DOMAIN = {{ git_domain }}
HTTP_PORT = 3000
ROOT_URL = {{ root_url }}
DISABLE_SSH = false
SSH_PORT = {{ git_ssh_port }}
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
LFS_JWT_SECRET = {{ lfs_jwt_secret }}
OFFLINE_MODE = false
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = sqlite3
HOST = localhost:3306
NAME = gitea
USER = root
PASSWD =
LOG_SQL = false
SCHEMA =
SSL_MODE = disable
CHARSET = utf8
[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
ISSUE_INDEXER_TYPE = db
[session]
PROVIDER_CONFIG = /data/gitea/sessions
PROVIDER = file
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
ENABLE_FEDERATED_AVATAR = false
[attachment]
PATH = /data/gitea/attachments
[log]
MODE = console
LEVEL = info
logger.router.MODE = console
ROOT_PATH = /data/gitea/log
logger.access.MODE=console
[security]
INSTALL_LOCK = true
SECRET_KEY =
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = *
INTERNAL_TOKEN = {{ internal_token }}
PASSWORD_HASH_ALGO = pbkdf2
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = false
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.localhost
[lfs]
PATH = /data/git/lfs
[mailer]
ENABLED = true
SMTP_ADDR = {{ mailer_host }}
SMTP_PORT = 587
FROM = {{ mailer_from }}
USER =
PASSWD =
[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = false
[repository.pull-request]
DEFAULT_MERGE_STYLE = merge
[repository.signing]
DEFAULT_TRUST_MODEL = committer
[ui]
DEFAULT_THEME = forgejo-light
[oauth2]
ENABLE=false

View file

@ -0,0 +1,56 @@
# vi: ft=yaml
version: "3"
networks:
traefik:
external: true
configs:
config:
external: true
name: "{{ config.config_name }}"
volumes:
forgejo:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/forgejo"
services:
forgejo:
image: codeberg.org/forgejo/forgejo:1.20
environment:
- USER_UID=1000
- USER_GID=1000
networks:
- traefik
ports:
- "{{ git_ssh_port }}:22"
volumes:
- type: volume
source: forgejo
target: /data
volume:
nocopy: true
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
deploy:
placement:
constraints:
- node.role == manager
labels:
- traefik.port=443
- traefik.enable=true
- traefik.http.routers.forgejo.entrypoints=websecure
- traefik.http.routers.forgejo.rule=Host(`{{ git_domain }}`)
- traefik.http.routers.forgejo.tls=true
- traefik.http.routers.forgejo.tls.certresolver=letsencrypt
- traefik.http.routers.forgejo.service=forgejo
- traefik.http.services.forgejo.loadbalancer.server.port=3000
- traefik.docker.network=traefik
- traefik.http.middlewares.set-forwarded-for.headers.hostsProxyHeaders=X-Forwarded-For
- traefik.http.routers.forgejo.middlewares=set-forwarded-for
configs:
- source: config
target: /data/gitea/conf/app.ini

View file

@ -0,0 +1,13 @@
- name: Create Docker config
docker_config:
name: forgejo_config
data: "{{ lookup('template', '{{ role_path }}/app.ini.j2') }}"
use_ssh_client: true
rolling_versions: true
register: config
- name: Deploy Docker stack
docker_stack:
name: forgejo
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,23 @@
git_domain: "git.kun.is"
root_url: "https://{{ git_domain }}"
mailer_host: "smtp.tweak.nl"
mailer_from: "git@kunis.nl"
lfs_jwt_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
66613032363837346461326131303839646332646233633736623865346135623739343233396165
6530326162323466623939393133623336366466343837620a613532616365646137326138383235
32313264653262656564336531646662323039623865393366616536633531306430336137313862
3361373539373561390a653236306433393737616561306236343362396438366134313032656233
35626364373961613361366138383566353463626136393861383934326263383336393766623063
3434656437663165376635326139383065383861386133623765
internal_token: !vault |
$ANSIBLE_VAULT;1.1;AES256
62633334656235613035343830326237633637626639363465313861323734393766636464303862
3936306561343863316630616164616537323537333262650a336337303232623832636666353038
64313134383330646537356432383332386238373835656663313431373939373630373566396339
6561643037383666340a643464326531623731303564646464376239613263643761643766623930
37623362326561346262306331376663313661633635323435333339396138383134303364306532
37353264363737643965643932356336633734316534303262336461313038626538396536333964
36353635323731353061393430656166363263366437313434336139616666326335633037663336
37353665613938613731316330396461343632643039643864343164303937613263343262623964
33366364636339623633653035313736653563363064646233383437373431373232

View file

@ -0,0 +1,52 @@
# vi: ft=yaml
version: "3"
networks:
traefik:
external: true
volumes:
data:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/freshrss/data"
extensions:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/freshrss/extensions"
services:
freshrss:
image: freshrss/freshrss:edge
networks:
- traefik
volumes:
- type: volume
source: data
target: /var/www/FreshRSS/data
volume:
nocopy: true
- type: volume
source: extensions
target: /var/www/FreshRSS/extensions
volume:
nocopy: true
environment:
TZ: Europe/Amsterdam
CRON_MIN: '2,32'
ADMIN_EMAIL: pim@kunis.nl
ADMIN_PASSWORD: {{ admin_password }}
ADMIN_API_PASSWORD: {{ admin_password }}
PUBLISHED_PORT: 443
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.freshrss.entrypoints=websecure
- traefik.http.routers.freshrss.rule=Host(`rss.kun.is`)
- traefik.http.routers.freshrss.tls=true
- traefik.http.routers.freshrss.tls.certresolver=letsencrypt
- traefik.http.routers.freshrss.service=freshrss
- traefik.http.services.freshrss.loadbalancer.server.port=80
- traefik.docker.network=traefik

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: freshrss
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,8 @@
admin_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
38363734333534376665616439306566613632303739373661333338356533653334323366326130
3031316133383432366639613565656134666338326639360a633263363066613964643665316334
63373830663239393137653131326630326465343333346430376536393162383836333130353562
3336306561636134650a646433633063316431643466326161303666313765323034343233646566
66613330616463346561343561616438643763643465373839303861356133313831303338356430
6634653635383833303265316662663631376163636134666565

View file

@ -0,0 +1,44 @@
# vi: ft=yaml
version: '3'
networks:
traefik:
external: true
volumes:
uploads:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/hedgedoc/uploads"
services:
hedgedoc:
image: quay.io/hedgedoc/hedgedoc:1.9.7
environment:
- CMD_DB_URL=postgres://hedgedoc:{{ database_passwords.hedgedoc }}@192.168.30.10:5432/hedgedoc
- CMD_DOMAIN=md.kun.is
- 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:
- type: volume
source: uploads
target: /hedgedoc/public/uploads
volume:
nocopy: true
networks:
- traefik
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.hedgedoc.entrypoints=websecure
- traefik.http.routers.hedgedoc.rule=Host(`md.kun.is`)
- traefik.http.routers.hedgedoc.tls=true
- traefik.http.routers.hedgedoc.tls.certresolver=letsencrypt
- traefik.http.routers.hedgedoc.service=hedgedoc
- traefik.http.services.hedgedoc.loadbalancer.server.port=3000
- traefik.docker.network=traefik

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: hedgedoc
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,10 @@
session_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
30633835386265643561343033326536653166343630396139303137613138383233666565666330
3032613865333836656566626435383165396539323837350a376331306464643766373839386638
65653865343539633636323833343964636332636461386434386432306230343833343431363134
6563373138626637650a633932313862326231666330343662343765666166373961376237396434
33396131353830323063326266623862353731653665626466653335656434303033353333353164
61613535373037646565386131383631366338616565373261396136616433393462313537313861
35313661616365373231373963323865393635626132343138363230313431636333363130346239
32656335333635613736

View file

@ -0,0 +1,24 @@
# vi: ft=yaml
version: "3.7"
networks:
traefik:
external: true
services:
inbucket:
image: inbucket/inbucket
networks:
- traefik
ports:
- 2500:2500
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.inbucket.entrypoints=localsecure
- traefik.http.routers.inbucket.rule=Host(`inbucket.geokunis2.nl`)
- traefik.http.routers.inbucket.service=inbucket
- traefik.http.routers.inbucket.tls=true
- traefik.http.routers.inbucket.tls.certresolver=letsencrypt
- traefik.docker.network=traefik
- traefik.http.services.inbucket.loadbalancer.server.port=9000

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: inbucket
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,87 @@
# vi: ft=yaml
version: '3.5'
networks:
traefik:
external: true
jitsi:
services:
web:
image: jitsi/web:stable-8218
environment:
- DISABLE_HTTPS=1
- ENABLE_AUTH=0
- ENABLE_GUESTS=1
- ENABLE_IPV6=1
- ENABLE_LETSENCRYPT=0
- PUBLIC_URL=https://meet.pim.kunis.nl
- TZ=Europe/Amsterdam
networks:
- jitsi
- traefik
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.jitsi-web.entrypoints=websecure
- traefik.http.routers.jitsi-web.rule=Host(`meet.pim.kunis.nl`)
- traefik.http.routers.jitsi-web.tls=true
- traefik.http.routers.jitsi-web.tls.certresolver=letsencrypt
- traefik.http.services.jitsi-web.loadbalancer.server.port=80
- traefik.http.routers.jitsi-web.service=jitsi-web
- traefik.docker.network=traefik
prosody:
image: jitsi/prosody:stable-8218
expose:
- '5222'
- '5347'
- '5280'
environment:
- AUTH_TYPE=internal
- ENABLE_AUTH=0
- ENABLE_GUESTS=1
- ENABLE_IPV6=1
- ENABLE_LOBBY=1
- JIBRI_RECORDER_PASSWORD={{ jitsi_password }}
- JIBRI_XMPP_PASSWORD={{ jitsi_password }}
- JICOFO_AUTH_PASSWORD={{ jitsi_password }}
- JIGASI_XMPP_PASSWORD={{ jitsi_password }}
- JVB_AUTH_PASSWORD={{ jitsi_password }}
- PUBLIC_URL=https://meet.pim.kunis.nl
- TZ=Europe/Amsterdam
networks:
jitsi:
aliases:
- xmpp.meet.jitsi
jicofo:
image: jitsi/jicofo:stable-8218
environment:
- AUTH_TYPE=internal
- ENABLE_AUTH=0
- JICOFO_AUTH_PASSWORD={{ jitsi_password }}
- SENTRY_DSN=0
- TZ=Europe/Amsterdam
depends_on:
- prosody
networks:
- jitsi
jvb:
image: jitsi/jvb:stable-8218
ports:
- '54562:54562/udp'
environment:
- JVB_ADVERTISE_IPS=84.245.14.149,192.168.30.8
- JVB_AUTH_PASSWORD={{ jitsi_password }}
- JVB_PORT=54562
- PUBLIC_URL=https://meet.pim.kunis.nl
- SENTRY_DSN=0
- COLIBRI_REST_ENABLED=0
- TZ=Europe/Amsterdam
depends_on:
- prosody
networks:
- jitsi
- traefik

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: jitsi
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,8 @@
jitsi_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66633139653639396435333239316536326366613338646531373063306333383562613462316561
6630373435326631376362643961343936626238663332630a623631613532366539633637333032
35383031306566613466643066366361663039633864643733356366386339366265326237653739
3062313832313638330a636131393130646564366563626430346436656236333961306363633435
39353934386631633132306562396430303738393235656363356666663934626161363365343162
6130346338333734653961633037386133396332643831363939

View file

@ -0,0 +1,45 @@
# vi: ft=yaml
version: '3.7'
networks:
traefik:
external: true
kitchenowl:
volumes:
data:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/kitchenowl/data"
services:
front:
image: tombursch/kitchenowl-web:v0.4.17
depends_on:
- back
networks:
- traefik
- kitchenowl
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.kitchenowl.entrypoints=websecure
- traefik.http.routers.kitchenowl.rule=Host(`boodschappen.kun.is`)
- traefik.http.routers.kitchenowl.tls=true
- traefik.http.routers.kitchenowl.tls.certresolver=letsencrypt
- traefik.http.routers.kitchenowl.service=kitchenowl
- traefik.http.services.kitchenowl.loadbalancer.server.port=80
- traefik.docker.network=traefik
back:
image: tombursch/kitchenowl:v88
networks:
- kitchenowl
environment:
- JWT_SECRET_KEY={{ jwt_secret_key }}
volumes:
- type: volume
source: data
target: /data
volume:
nocopy: true

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: kitchenowl
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,7 @@
jwt_secret_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
37376338663532376135613331303737626633666138643132316336306164393134633639303865
3134613830323335663466373262316262353464323535300a636163633439323035643033623363
36316361656133663235333834343233363134313938656664356538366166653336656562623664
3332393330616636630a646139393937313932373963623764346134323635336539346562346635
36613637396133383664323561666464346336386233363434653765356334633831

View file

@ -0,0 +1,8 @@
# vi: ft=yaml
version: '3.7'
services:
kms:
image: teddysun/kms
ports:
- 1688:1688

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: kms
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,131 @@
# vi: ft=yaml
version: '3'
networks:
traefik:
external: true
mastodon:
volumes:
system:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/mastodon/system"
redis:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/mastodon/redis"
services:
redis:
image: redis:7-alpine
networks:
mastodon:
aliases:
- redis
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
volumes:
- type: volume
source: redis
target: /data
volume:
nocopy: true
web:
image: tootsuite/mastodon:v4.1
environment:
- 'OTP_SECRET={{ otp_secret }}'
- 'SECRET_KEY_BASE={{ secret_key_base }}'
- 'REDIS_HOST=redis'
- 'DB_HOST=192.168.30.10'
- 'DB_USER=mastodon'
- 'DB_NAME=mastodon'
- 'DB_PASS={{ database_passwords.mastodon }}'
- 'VAPID_PRIVATE_KEY={{ vapid_private_key }}'
- 'VAPID_PUBLIC_KEY=BDcpOP2ThgD13i2ENjnlVXG7QH-m3xuNE4rySx6_NBYQz34UxSM3N4nT7GUxN5zBF-Kehlv0CpqBDDa78QFiS0g='
- 'SMTP_SERVER=smtp.tweak.nl'
- 'SMTP_PORT=587'
- 'SMTP_LOGIN='
- 'SMTP_PASSWORD='
- 'SMTP_FROM_ADDRESS=mastodon@kunis.nl'
- 'LOCAL_DOMAIN=social.pizzapim.nl'
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
networks:
- mastodon
- traefik
healthcheck:
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1']
volumes:
- type: volume
source: system
target: /mastodon/public/system
volume:
nocopy: true
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.mastodon.entrypoints=websecure
- traefik.http.routers.mastodon.rule=Host(`social.pizzapim.nl`)
- traefik.http.routers.mastodon.tls=true
- traefik.http.routers.mastodon.tls.certresolver=letsencrypt
- traefik.http.services.mastodon.loadbalancer.server.port=3000
- traefik.http.routers.mastodon.service=mastodon
- traefik.docker.network=traefik
depends_on:
- redis
streaming:
image: tootsuite/mastodon:v4.1
command: node ./streaming
environment:
- 'REDIS_HOST=redis'
- 'LOCAL_DOMAIN=social.pizzapim.nl'
- 'DB_HOST=192.168.30.10'
- 'DB_USER=mastodon'
- 'DB_NAME=mastodon'
- 'DB_PASS={{ database_passwords.mastodon }}'
networks:
- mastodon
- traefik
healthcheck:
# prettier-ignore
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1']
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.mastodon-streaming.entrypoints=websecure
- "traefik.http.routers.mastodon-streaming.rule=(Host(`social.pizzapim.nl`) && PathPrefix(`/api/v1/streaming`))"
- traefik.http.routers.mastodon-streaming.service=mastodon-streaming
- traefik.http.services.mastodon-streaming.loadbalancer.server.port=4000
- traefik.http.routers.mastodon-streaming.tls=true
- traefik.http.routers.mastodon-streaming.tls.certresolver=letsencrypt
- traefik.docker.network=traefik
depends_on:
- redis
sidekiq:
image: tootsuite/mastodon:v4.1
command: bundle exec sidekiq
environment:
- 'OTP_SECRET={{ otp_secret }}'
- 'SECRET_KEY_BASE={{ secret_key_base }}'
- 'REDIS_HOST=redis'
- 'DB_HOST=192.168.30.10'
- 'DB_USER=mastodon'
- 'DB_NAME=mastodon'
- 'DB_PASS={{ database_passwords.mastodon }}'
networks:
- mastodon
volumes:
- type: volume
source: system
target: /mastodon/public/system
volume:
nocopy: true
healthcheck:
test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"]
depends_on:
- redis

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: mastodon
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,42 @@
mastodon_postgres_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
34643131323762373635383736636432643161646130373565333432323337646435656233383131
3066353734373938353162656335666536323265643162620a663562303636383737393061396331
30353538326333393031373736363933666636383866373763303237376561333061323131303062
3532316632613062310a343566393237363364613931353062636537663864383839623930383836
32613634616335616462336261303632646266326663383166366236643438616538626263343835
6539616439636364626466333163316164633631616132623665
otp_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
33303436663063313039636335623937343530323636346363306234333135306138653337313034
3337363432363734353363623738653630373536653433350a356336383235383430613934623937
36316638343439376134383635336630313065623138326630303131333136626636386361313661
6134613862366463300a313765366136343431343838363230363134613164373931623564626466
32623137666364326234383264396336636561313132313930383964656434656535663861343337
65316331323335626464626231653236313932663334316134633837646330303563633162373036
66326135656531393839343138376666623337616162653137393764306265323065356431343162
36373135303339356366356263623334373361326561396562353332323363623738626132303738
38383638616363386536386461353465353765366234353862653765376330663661326138626266
30633134643632393630323834323538326339373361363235666133303761323261336637663862
326633383933663530653230336364653461
secret_key_base: !vault |
$ANSIBLE_VAULT;1.1;AES256
32373731376166613238303535646633326162613137366165643037643966643637316265653832
3035393061616431666162373133393666653634386338350a376136653961646239656534336230
33366235343365653234333866393965643131306636373566623665646562353234323065393262
6264313430333262390a626338333932363137356338636132636133613239633537623064666438
32343063653664393530353536643963353364373830303563346163613862653161343165363062
61396630353036333634313033663962613930336637323461313731633136366365623732306337
37646265613639306133373736353365366461373264356665623236313836633565343764626238
38353637613064306162393430323662616231623965643933383339616561353963663366396363
33346332343336386266636165616135343732353365336630653334383533633831636138623733
34396266643166386130383334666565303865396135613863336261656135343564376537383634
353635336365613765363931373636363465
vapid_private_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
33376430313539346137343237313061653164343861623563656638306539373837393364326235
3435396264613533633138346231303137663763323361360a356137306330343939353732356535
33396336633966623266396265356435343633373766363637616635326563623130653039343665
3465306562306261660a303131666436333137306139366636646232333061383935353263396534
63376635393966653636316236316538656361393631626465383233386136313366363531363663
3436326431353435653666356266333835303061616436323061

View file

@ -0,0 +1,132 @@
# vi: ft=yaml
version: "3.8"
networks:
traefik:
external: true
grafana:
configs:
esdatasource:
external: true
name: "{{ esdatasource.config_name }}"
fluentconf:
external: true
name: "{{ fluentconf.config_name }}"
volumes:
escerts:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/elasticsearch/certs"
esdata:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/elasticsearch/data"
grafanadata:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/grafana/data"
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.8.1
volumes:
- type: volume
source: escerts
target: /usr/share/elasticsearch/config/certs
volume:
nocopy: true
- type: volume
source: esdata
target: /usr/share/elasticsearch/data
volume:
nocopy: true
ports:
- {{ elasticsearch_port }}:9200
environment:
- node.name=es01
- cluster.name=shoarma
- discovery.type=single-node
- bootstrap.memory_lock=true
- xpack.security.enabled=false
- xpack.security.http.ssl.enabled=false
- xpack.security.http.ssl.key=certs/es01/es01.key
- xpack.security.http.ssl.certificate=certs/es01/es01.crt
- xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
- xpack.security.transport.ssl.enabled=false
- xpack.security.transport.ssl.key=certs/es01/es01.key
- xpack.security.transport.ssl.certificate=certs/es01/es01.crt
- xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
- xpack.security.transport.ssl.verification_mode=certificate
- xpack.license.self_generated.type=basic
ulimits:
memlock:
soft: -1
hard: -1
healthcheck:
test:
[
"CMD-SHELL",
"curl http://localhost:9200 | grep -q 'You Know, for Search'",
]
interval: 10s
timeout: 10s
retries: 120
grafana:
image: grafana/grafana-oss
depends_on:
- elasticsearch
networks:
- traefik
- grafana
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.grafana.entrypoints=localsecure
- traefik.http.routers.grafana.rule=Host(`grafana.kun.is`)
- traefik.http.routers.grafana.tls=true
- traefik.http.routers.grafana.tls.certresolver=letsencrypt
- traefik.http.routers.grafana.service=grafana
- traefik.http.services.grafana.loadbalancer.server.port=3000
- traefik.docker.network=traefik
volumes:
- type: volume
source: grafanadata
target: /var/lib/grafana
volume:
nocopy: true
configs:
- source: esdatasource
target: /etc/grafana/provisioning/datasources/elasticsearch.yaml
grafana-ntfy:
image: kittyandrew/grafana-to-ntfy:master
depends_on:
- grafana
ports:
- 8080:8080
networks:
grafana:
aliases:
- grafana-ntfy
environment:
- NTFY_URL=https://ntfy.kun.is/alerts
- NTFY_BAUTH_USER=pim
- NTFY_BAUTH_PASS={{ ntfy_password }}
- BAUTH_USER=admin
- BAUTH_PASS=test
fluentd:
image: git.kun.is/pim/fluentd:1.0.3
depends_on:
- elasticsearch
ports:
- {{ fluent_forward_port }}:24224
configs:
- source: fluentconf
target: /fluentd/etc/fluent.conf

View file

@ -0,0 +1,35 @@
# vi: ft=yaml
apiVersion: 1
datasources:
- name: cpu
type: elasticsearch
access: proxy
url: http://maestro.dmz:{{ elasticsearch_port }}
jsonData:
index: 'fluentd.cpu-*'
timeField: '@timestamp'
- name: memory
type: elasticsearch
access: proxy
url: http://maestro.dmz:{{ elasticsearch_port }}
jsonData:
index: 'fluentd.memory-*'
timeField: '@timestamp'
- name: diskfree
type: elasticsearch
access: proxy
url: http://maestro.dmz:{{ elasticsearch_port }}
jsonData:
index: 'fluentd.diskfree-*'
timeField: '@timestamp'
- name: traefik_access
type: elasticsearch
access: proxy
url: http://maestro.dmz:{{ elasticsearch_port }}
jsonData:
index: 'fluentd.access.traefik-*'
timeField: '@timestamp'

View file

@ -0,0 +1,35 @@
# vi: ft=yaml
# Receive events from 24224/tcp
# This is used by log forwarding and the fluent-cat command
<source>
@type forward
port {{ fluent_forward_port }}
</source>
<filter access.**>
@type geoip
geoip_lookup_keys host
backend_library geoip2_c
<record>
latitude ${location.latitude["host"]}
longitude ${location.longitude["host"]}
</record>
skip_adding_null_record true
</filter>
<match cpu memory diskfree access.**>
@type elasticsearch
host maestro.dmz
port {{ elasticsearch_port }}
include_timestamp true
logstash_format true
logstash_prefix fluentd.${tag}
</match>
<match **>
@type null
</match>
<system>
log_level info
</system>

View file

@ -0,0 +1,21 @@
- name: Create fluentd config
docker_config:
name: fluentconf
data: "{{ lookup('template', '{{ role_path }}/fluent.conf.j2') }}"
use_ssh_client: true
rolling_versions: true
register: fluentconf
- name: Create elasticsearch data source config
docker_config:
name: esdatasource
data: "{{ lookup('template', '{{ role_path }}/elasticsearch.yml.j2') }}"
use_ssh_client: true
rolling_versions: true
register: esdatasource
- name: Deploy Docker stack
docker_stack:
name: monitoring
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,8 @@
ntfy_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
36333232393635383732336630626463633038353862333430396437333733376239343531663339
6364643930636566326463393963316263323061613032350a383930376537373437633333623639
66613439636531393761366534333134383231303637643063633537393535356536636530666665
6537653731666130610a346135373562333931646237396233613065353165623336373935386137
36313830623931313238333430346238626562353661616465333736346230396162386137363435
3362636565336639643832626165613236643466633537633236

View file

@ -0,0 +1,40 @@
# vi: ft=yaml
version: '3.8'
networks:
traefik:
external: true
volumes:
data:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/nextcloud/data"
services:
nextcloud:
image: nextcloud:27
volumes:
- type: volume
source: data
target: /var/www/html
volume:
nocopy: true
environment:
- POSTGRES_USER=nextcloud
- POSTGRES_DB=nextcloud
- POSTGRES_PASSWORD={{ database_passwords.nextcloud }}
- POSTGRES_HOST=192.168.30.10
networks:
- traefik
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.nextcloud.entrypoints=websecure
- traefik.http.routers.nextcloud.rule=Host(`cloud.kun.is`)
- traefik.http.routers.nextcloud.tls=true
- traefik.http.routers.nextcloud.tls.certresolver=letsencrypt
- traefik.http.routers.nextcloud.service=nextcloud
- traefik.http.services.nextcloud.loadbalancer.server.port=80
- traefik.docker.network=traefik

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: nextcloud
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,112 @@
# vi: ft=yaml
version: '3'
networks:
traefik:
external: true
overleaf:
volumes:
data:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/overleaf/data"
redis:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/overleaf/redis"
mongodb:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/overleaf/mongodb"
services:
sharelatex:
image: sharelatex/sharelatex:3
networks:
- traefik
- overleaf
depends_on:
- overleaf-mongodb
- overleaf-redis
stop_grace_period: 60s
volumes:
- type: volume
source: data
target: /var/lib/sharelatex
volume:
nocopy: true
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.overleaf.entrypoints=websecure
- traefik.http.routers.overleaf.rule=Host(`latex.kun.is`)
- traefik.http.routers.overleaf.tls=true
- traefik.http.routers.overleaf.tls.certresolver=letsencrypt
- traefik.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
SHARELATEX_SITE_URL: https://latex.kun.is
SHARELATEX_ADMIN_EMAIL: pim@kunis.nl
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: ""
overleaf-mongodb:
image: mongo:4.4
networks:
- overleaf
expose:
- 27017
volumes:
- type: volume
source: mongodb
target: /data/db
volume:
nocopy: true
healthcheck:
test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
interval: 10s
timeout: 10s
retries: 5
overleaf-redis:
image: redis:5
networks:
- overleaf
expose:
- 6379
volumes:
- type: volume
source: redis
target: /data
volume:
nocopy: true

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: overleaf
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,57 @@
# vi: ft=yaml
version: "3.8"
networks:
traefik:
external: true
pihole:
volumes:
data:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/pihole/data"
dnsmasq:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/pihole/dnsmasq"
services:
pihole:
image: pihole/pihole:latest
ports:
- "53:53/tcp"
- "53:53/udp"
network_mode: "host"
environment:
TZ: 'Europe/Amsterdam'
WEBPASSWORD: {{ pihole_password }}
PIHOLE_DNS_: '192.168.30.1'
volumes:
- type: volume
source: data
target: /etc/pihole
volume:
nocopy: true
- type: volume
source: dnsmasq
target: /etc/dnsmasq.d
volume:
nocopy: true
networks:
- traefik
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.pihole.entrypoints=localsecure
- traefik.http.routers.pihole.rule=Host(`pihole.kun.is`)
- traefik.http.routers.pihole.tls=true
- traefik.http.routers.pihole.tls.certresolver=letsencrypt
- traefik.http.routers.pihole.service=pihole
- traefik.http.services.pihole.loadbalancer.server.port=80
- traefik.docker.network=traefik
placement:
constraints:
- node.role == manager

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: pihole
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,8 @@
pihole_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
38616134666661363535303137373633613063613731383766303633336533373233363736333263
3461336138663861623134633031663631633666393939340a396561643132333665373430343466
36626633366232376236383434336166353638653733666566336266373739663236636334373866
3261303962613966610a643765613762396335643233383432613737316361386234663365656566
30336535326437336437383336393838306161333662346165333262383735616137653766653165
3361333436346130376261316133323963393338633838303031

View file

@ -0,0 +1,61 @@
# vi: ft=yaml
version: '3.7'
networks:
traefik:
external: true
configs:
config:
external: true
name: "{{ config.config_name }}"
users:
external: true
name: "{{ users.config_name }}"
volumes:
data:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/radicale"
services:
radicale:
image: tomsquest/docker-radicale
init: true
read_only: true
cap_drop:
- ALL
cap_add:
- SETUID
- SETGID
- CHOWN
- KILL
healthcheck:
test: curl -f http://127.0.0.1:5232 || exit 1
interval: 30s
retries: 3
volumes:
- type: volume
source: data
target: /data
volume:
nocopy: true
networks:
- traefik
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.radicale.entrypoints=websecure
- traefik.http.routers.radicale.rule=Host(`dav.kun.is`)
- traefik.http.routers.radicale.tls=true
- traefik.http.routers.radicale.tls.certresolver=letsencrypt
- traefik.http.routers.radicale.service=radicale
- traefik.http.services.radicale.loadbalancer.server.port=5232
- traefik.docker.network=traefik
configs:
- source: config
target: /config/config
- source: users
target: /config/users

View file

@ -0,0 +1,24 @@
[server]
hosts = 0.0.0.0:5232, [::]:5232
ssl = False
[encoding]
request = utf-8
stock = utf-8
[auth]
realm = Radicale - Password Required
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = md5
[rights]
type = owner_only
[storage]
type = multifilesystem
filesystem_folder = /data
[logging]
[headers]

View file

@ -0,0 +1,21 @@
- name: Create radicale config
docker_config:
name: radicale_config
data: "{{ lookup('file', '{{ role_path }}/radicale.conf') }}"
use_ssh_client: true
rolling_versions: true
register: config
- name: Create radicale users
docker_config:
name: radicale_users
data: "{{ lookup('file', '{{ role_path }}/users') }}"
use_ssh_client: true
rolling_versions: true
register: users
- name: Deploy Docker stack
docker_stack:
name: radicale
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1 @@
pim:$apr1$GUiTihkS$dDCkaUxFx/O86m6NCy/yQ.

View file

@ -0,0 +1,73 @@
# vi: ft=yaml
version: '3'
networks:
traefik:
external: true
seafile:
volumes:
data:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/seafile/data"
db:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/seafile/db"
services:
db:
image: mariadb:10.5
environment:
- MYSQL_ROOT_PASSWORD={{ db_root_passwd }}
- MYSQL_LOG_CONSOLE=true
volumes:
- type: volume
source: db
target: /var/lib/mysql
volume:
nocopy: true
networks:
- seafile
memcached:
image: memcached:1.6
entrypoint: memcached -m 256
networks:
- seafile
seafile:
image: seafileltd/seafile-mc:9.0.10
volumes:
- type: volume
source: data
target: /shared
volume:
nocopy: true
environment:
- DB_HOST=db
- DB_ROOT_PASSWD={{ db_root_passwd }}
- TIME_ZONE=Europe/Amsterdam
- SEAFILE_ADMIN_EMAIL={{ seafile_admin_email }}
- SEAFILE_ADMIN_PASSWORD={{ seafile_admin_password }}
- SEAFILE_SERVER_LETSENCRYPT=false
- SEAFILE_SERVER_HOSTNAME={{ seafile_domain }}
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.seafile.entrypoints=websecure
- traefik.http.routers.seafile.rule=Host(`{{ seafile_domain }}`)
- traefik.http.routers.seafile.tls=true
- traefik.http.routers.seafile.tls.certresolver=letsencrypt
- traefik.http.services.seafile.loadbalancer.server.port=80
- traefik.http.routers.seafile.service=seafile
- traefik.docker.network=traefik
depends_on:
- db
- memcached
networks:
- traefik
- seafile

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: seafile
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,18 @@
db_root_passwd: !vault |
$ANSIBLE_VAULT;1.1;AES256
62376163363033396161363264613836623734623835316439666331356464636633393237313236
3731383961393939366165393537663435356166643966650a353132616166353630333733636639
63616163346566336461313264326562393964643661613831316233326165623463613865636637
6363363331333430320a366661356232393036383765353066623334656133313661636130666238
32336561323431303730373262343534326539383932613533623232376330646230363363636135
3266336663623037663838643936366437663831356634333930
seafile_admin_email: niels@kunis.nl
seafile_admin_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
34366163396632343065636232363435633039373236363461383563363162626561653763383438
3263393539663030363532353238633262616339343436370a613631323064303637623266653832
64323834356664316265376132633863666136316239623862643962366637306238343933386134
6237396238383232360a386637303639646136653134643737393735383661626539386134643333
35313536323963303734353338636162666236343430623062373464653531353230366238326231
6661363038393534373861643261383561386536613337396539
seafile_domain: files.geokunis2.nl

View file

@ -0,0 +1,60 @@
# vi: ft=yaml
version: "3"
networks:
shephard:
services:
app:
image: mazzolino/shepherd
networks:
- shephard
environment:
TZ: 'Europe/Amsterdam'
FILTER_SERVICES: ''
IGNORELIST_SERVICES: ""
RUN_ONCE_AND_EXIT: "true"
APPRISE_SIDECAR_URL: apprise:5000
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
read_only: true
deploy:
replicas: 0
restart_policy:
condition: none
labels:
- swarm.cronjob.enable=true
- "swarm.cronjob.schedule=0 2 * * *"
- swarm.cronjob.skip-running=true
placement:
constraints:
- node.role == manager
scheduler:
image: crazymax/swarm-cronjob:latest
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
read_only: true
environment:
- "TZ=Europe/Amsterdam"
- "LOG_LEVEL=info"
- "LOG_JSON=false"
deploy:
placement:
constraints:
- node.role == manager
apprise:
image: mazzolino/apprise-microservice:0.1
environment:
NOTIFICATION_URLS: {{ apprise_urls }}
networks:
- shephard
deploy:
placement:
constraints:
- node.role == manager

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: shephard
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1 @@
apprise_urls: "apprises://apprise.kun.is:444/shephard"

View file

@ -0,0 +1,31 @@
# vi: ft=yaml
version: "3"
networks:
traefik:
external: true
services:
swarm-dashboard:
image: charypar/swarm-dashboard
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
environment:
PORT: 80
networks:
- traefik
deploy:
placement:
constraints:
- node.role == manager
labels:
- traefik.enable=true
- traefik.http.routers.swarm-dashboard.entrypoints=localsecure
- traefik.http.routers.swarm-dashboard.rule=Host(`swarm.kun.is`)
- traefik.http.routers.swarm-dashboard.tls=true
- traefik.http.routers.swarm-dashboard.tls.certresolver=letsencrypt
- traefik.http.routers.swarm-dashboard.service=swarm-dashboard
- traefik.http.services.swarm-dashboard.loadbalancer.server.port=80
- traefik.docker.network=traefik

View file

@ -0,0 +1,5 @@
- name: Deploy Docker stack
docker_stack:
name: swarm_dashboard
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,45 @@
$ANSIBLE_VAULT;1.1;AES256
37326262373466303939623263623234616338316165316466656131326339306233303834396263
3139663539356264323038306635363934363364653437350a666438396563643339353765306131
39653434373966346166323938666364323562313334323262643666373463623536363635643163
3430353230326634320a643532663765663632623031313463653765643134313538633131613663
64393533636138323833343630363639656539376163353239313231646662316532666631623734
31343364393363623164336339303631366162376131613736636131396165663835653433303134
62323265633039633865326366613366653435653261633662613737353463633663383635303562
39303933343139363132393035336332363438656333646136333330326533623763393263663563
36343038393264383639346436316134386531383338386461363538613135663863363434623339
31373236353337653838396333643638343232653066313662393165343062396137326630646430
31646566356565386532626433383163643635643930326164353766323263616665636435323339
38373837393035343737356134373831303831316464666637333231343434316632316464356564
31613464633761306330303637386230333430396665383262333530336137336236623838326333
30393861666439623536336231616563303764646563393065353432313965343330633463313564
66373539373265353765636438393633613839393830366135323139666533393165653736666335
35303736623534653635343636383662316134376332393239633262363939396263363264616637
35396261346264373930396462393638316335363833333132393061633337626331323439363131
39306264386133316137633039366638356130616438373433333635666231366136613363626133
31316230336534616430633232623430666234643836636338613730356335623434373433643935
62623266313834353163623439616533623135396134346164373363643364373939396163363837
36313432393965653664633231393564323936323933313565323337346333313233396666626361
65383031326630313263343862653063613839373131643265656237623232663761383665333939
33376531623665653037333563333034363363333435343439663761633734616461353961323434
66643833353539623265616262383265396237636631346433386638643436383230333438653462
37383235316634353262316436653163316164356261353663663565396630613434396231353538
38633330326266303838346365663839646163623264633934363938666234393131356138656439
31333161643136633836343262326136393964393635623634316532393837376162383835303435
30643339356434386264643163316165396534373064346334636132316230346437363665636563
38333835306666626637386562306433373031366136616635623765393630383939353335393930
61663832383239643363626137343661366436653864643339316537383738323335333866633537
39316339383239323131653232633833363536313431643364313937633037336564386339383433
38303939303835386263633430383061336436383062663462353762376666613530313663623261
66616266373136326433363338303365653230663763636630353034383832633239383932616365
37373236396631623866656330623632313538326330626363316262653566383633666531383738
34353830373137343236343765393665356534356238353861326165303939363236626130626363
64623164383866393630656232373164343163363433643835396236363132346235356134613564
66383364623962316564373564363631356234386535653465633864313365396438356235313163
35633366663836666337653537336334353935323364306635383238373664613530353365323366
31373831383336336237633064313938393637636231356165656631386132313734333439643733
62666435363535303530323866623139653138643831623838316432366539316236306133393764
63386133333832356365396137623332643539633236343762353138386434303632373932336139
39396364653864316435356434383761306238633164643939363864356362633135623438363861
64626339663931383133383862313031383638653266306539643061316238616266656136656530
63666239303034396133

View file

@ -0,0 +1,189 @@
<configuration version="37">
{% for folder in st.folders %}
<folder id="{{ folder.id }}" label="{{ folder.label }}" path="{{ folder.path }}" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
{% for device in folder.devices %}
<device id="{{ device }}" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
{% endfor %}
<device id="{{ st.server.id }}" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<minDiskFree unit="%">1</minDiskFree>
<versioning>
<cleanupIntervalS>3600</cleanupIntervalS>
<fsPath></fsPath>
<fsType>basic</fsType>
</versioning>
<copiers>0</copiers>
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>10</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
<caseSensitiveFS>false</caseSensitiveFS>
<junctionsAsDirs>false</junctionsAsDirs>
<syncOwnership>false</syncOwnership>
<sendOwnership>false</sendOwnership>
<syncXattrs>false</syncXattrs>
<sendXattrs>false</sendXattrs>
<xattrFilter>
<maxSingleEntrySize>1024</maxSingleEntrySize>
<maxTotalSize>4096</maxTotalSize>
</xattrFilter>
</folder>
{% endfor %}
{% for peer in st.peers %}
<device id="{{ peer.id }}" name="{{ peer.name }}" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>dynamic</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
</device>
{% endfor %}
<device id="{{ st.server.id }}" name="{{ st.server.name }}" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>dynamic</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
</device>
<gui enabled="true" tls="false" debugging="false">
<address>{{ st.gui.address }}</address>
<user>{{ st.gui.user }}</user>
<password>{{ st.gui.password }}</password>
<apikey>{{ st.gui.apikey }}</apikey>
<theme>{{ st.gui.theme }}</theme>
</gui>
<ldap></ldap>
<options>
<listenAddress>default</listenAddress>
<globalAnnounceServer>default</globalAnnounceServer>
<globalAnnounceEnabled>true</globalAnnounceEnabled>
<localAnnounceEnabled>true</localAnnounceEnabled>
<localAnnouncePort>21027</localAnnouncePort>
<localAnnounceMCAddr>[ff12::8384]:21027</localAnnounceMCAddr>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<reconnectionIntervalS>60</reconnectionIntervalS>
<relaysEnabled>true</relaysEnabled>
<relayReconnectIntervalM>10</relayReconnectIntervalM>
<startBrowser>true</startBrowser>
<natEnabled>true</natEnabled>
<natLeaseMinutes>60</natLeaseMinutes>
<natRenewalMinutes>30</natRenewalMinutes>
<natTimeoutSeconds>10</natTimeoutSeconds>
<urAccepted>3</urAccepted>
<urSeen>3</urSeen>
<urUniqueID></urUniqueID>
<urURL>https://data.syncthing.net/newdata</urURL>
<urPostInsecurely>false</urPostInsecurely>
<urInitialDelayS>1800</urInitialDelayS>
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<upgradeToPreReleases>false</upgradeToPreReleases>
<keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS>
<limitBandwidthInLan>false</limitBandwidthInLan>
<minHomeDiskFree unit="%">1</minHomeDiskFree>
<releasesURL>https://upgrades.syncthing.net/meta.json</releasesURL>
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
<tempIndexMinBlocks>10</tempIndexMinBlocks>
<trafficClass>0</trafficClass>
<setLowPriority>true</setLowPriority>
<maxFolderConcurrency>0</maxFolderConcurrency>
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
<crashReportingEnabled>true</crashReportingEnabled>
<stunKeepaliveStartS>180</stunKeepaliveStartS>
<stunKeepaliveMinS>20</stunKeepaliveMinS>
<stunServer>default</stunServer>
<databaseTuning>auto</databaseTuning>
<maxConcurrentIncomingRequestKiB>0</maxConcurrentIncomingRequestKiB>
<announceLANAddresses>true</announceLANAddresses>
<sendFullIndexOnUpgrade>false</sendFullIndexOnUpgrade>
<connectionLimitEnough>0</connectionLimitEnough>
<connectionLimitMax>0</connectionLimitMax>
<insecureAllowOldTLSVersions>false</insecureAllowOldTLSVersions>
<connectionPriorityTcpLan>10</connectionPriorityTcpLan>
<connectionPriorityQuicLan>20</connectionPriorityQuicLan>
<connectionPriorityTcpWan>30</connectionPriorityTcpWan>
<connectionPriorityQuicWan>40</connectionPriorityQuicWan>
<connectionPriorityRelay>50</connectionPriorityRelay>
<connectionPriorityUpgradeThreshold>0</connectionPriorityUpgradeThreshold>
</options>
<defaults>
<folder id="" label="" path="~" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="{{ st.server.id }}" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<minDiskFree unit="%">1</minDiskFree>
<versioning>
<cleanupIntervalS>3600</cleanupIntervalS>
<fsPath></fsPath>
<fsType>basic</fsType>
</versioning>
<copiers>0</copiers>
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>10</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
<caseSensitiveFS>false</caseSensitiveFS>
<junctionsAsDirs>false</junctionsAsDirs>
<syncOwnership>false</syncOwnership>
<sendOwnership>false</sendOwnership>
<syncXattrs>false</syncXattrs>
<sendXattrs>false</sendXattrs>
<xattrFilter>
<maxSingleEntrySize>1024</maxSingleEntrySize>
<maxTotalSize>4096</maxTotalSize>
</xattrFilter>
</folder>
<device id="" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>dynamic</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
</device>
<ignores></ignores>
</defaults>
</configuration>

View file

@ -0,0 +1,62 @@
# vi: ft=yaml
version: "3"
networks:
traefik:
external: true
configs:
config:
external: true
name: "{{ config.config_name }}"
private_key:
external: true
name: "{{ key.config_name }}"
certificate:
external: true
name: "{{ cert.config_name }}"
volumes:
nextcloud_data:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/nextcloud/data"
services:
syncthing:
image: lscr.io/linuxserver/syncthing:1.23.6
networks:
- traefik
deploy:
labels:
- traefik.enable=true
- traefik.docker.network=traefik
- traefik.http.routers.syncthing.entrypoints=localsecure
- traefik.http.routers.syncthing.rule=Host(`sync.kun.is`)
- traefik.http.routers.syncthing.service=syncthing
- traefik.http.routers.syncthing.tls=true
- traefik.http.routers.syncthing.tls.certresolver=letsencrypt
- traefik.http.services.syncthing.loadbalancer.server.port=8384
environment:
- PUID=33
- PGID=33
- TZ=Europe/Amsterdam
volumes:
- type: volume
source: nextcloud_data
target: /data
volume:
nocopy: true
configs:
- source: config
target: /config/config.xml
- source: private_key
target: /config/key.pem
uid: '33'
gid: '33'
- source: certificate
target: /config/cert.pem
uid: '33'
gid: '33'

View file

@ -0,0 +1,20 @@
$ANSIBLE_VAULT;1.1;AES256
31373963666334633437386361353532396162653439373964333935643065383836383537336238
3065306235363835343330393366326630383163633664300a653635653932663566376165623030
33666262643032383764343134326439363536353439363134353432373263316164373139633838
6336363735333862360a386235366434656336333762343330633030613437626262353934636163
38376431343934373637343631373962653262613766393561383631303563383935616630663833
62363533616235303834376233663033373531666632313237303661653265613061373131646266
31643839386134383934623632336538386462626261613039306432366564616162366435363331
34663464386630373134346264386334376334336363623137363831326338323234373662653932
33373331663065336230313731303139653036646261643535393662633165356632306536393530
30363066373064353936313461663235386465323734636263323063333365633066633736336436
38623966353634356636343833653131646131633536383339663433306130386461303735323632
64646465373533306266353932653561623363396137383532373734653462346239646562353136
64313539383566663939663734333565643637376239383337363066373639613934303633343762
37646565666635363231396139326536356533343065333731656363613731333136636561376430
35356432373537363034653231636465303135363534323766333530353433663462653837643162
39616664636464343435643039646362336634333561356438386262653231323033343662383138
66633534336232663438666632373966613335396639383836666333656235376339343538313838
39356165323361386535306664643537363764393365363639366637343332306537653962396339
323030323036393662646636303330666561

View file

@ -0,0 +1,29 @@
- name: Create cert.pem config
docker_config:
name: syncthing_cert
data: "{{ lookup('file', '{{ role_path }}/cert.pem') }}"
use_ssh_client: true
rolling_versions: true
register: cert
- name: Create key.pem config
docker_config:
name: syncthing_key
data: "{{ lookup('file', '{{ role_path }}/key.pem') }}"
use_ssh_client: true
rolling_versions: true
register: key
- name: Create config.xml config
docker_config:
name: syncthing_config
data: "{{ lookup('template', '{{ role_path }}/config.xml.j2') }}"
use_ssh_client: true
rolling_versions: true
register: config
- name: Deploy Docker stack
docker_stack:
name: syncthing
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -0,0 +1,34 @@
st:
server:
id: "IGS4TYV-TQ6X2CG-OE3M2RE-DKZWKQZ-HEKIGHT-C6EIGHL-CBP2ULE-M3WZ7QC"
name: "dd219859eab5"
gui:
address: "127.0.0.1:8384"
user: pim
password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66383234373561373935313863393338623331393233626635653637383734623539376633326561
3464633966383864306131383334633633356363636163300a393562383730613934613439663431
63653465316130626232663132626466643164313830613933363535336634313164386162643839
6235303662633931390a313230363636656639653531636131333862356363663535313133663138
38356566656161646636313766353937373433663631636265303464633437303464396537663264
66326530313661636264336634613633316462343034386134636365383736636436613065323236
323933363666353232393635376136363239
apikey: !vault |
$ANSIBLE_VAULT;1.1;AES256
31633162323338303133353838613336623961626635623165626335353263336130393166666535
3763336662326336333436333635656230393838366163660a393737303132373466633265306433
66336636666132373235646638653130633263343532353831653533656538663038326463306232
3132646634376166620a663339346239643561616362333036633363396263323761663134373630
30613730373131636262636266623363663561363863323938613832393864396633656664356534
3563626633643766643339316132383434303538636666623934
theme: default
peers:
- id: "B4Y7T5D-PHHDOFH-ZZ4VGOK-YNJINJG-VCYC272-PIE24XA-XJ5HSOD-DF3T6AJ"
name: "Pixel 4a"
folders:
- id: "rthas-wdjsw"
label: "pim"
path: "/data/data/pim/files"
devices:
- "B4Y7T5D-PHHDOFH-ZZ4VGOK-YNJINJG-VCYC272-PIE24XA-XJ5HSOD-DF3T6AJ"

View file

@ -0,0 +1,135 @@
# vi: ft=yaml
version: "3.7"
networks:
traefik:
external: true
configs:
services:
external: true
name: "{{ services.config_name }}"
volumes:
acme:
driver_opts:
type: "nfs"
o: "addr=192.168.30.10,nolock,soft,rw"
device: ":/mnt/data/traefik/acme"
services:
traefik:
image: traefik:3.0.0-beta2
networks:
- traefik
ports:
- mode: host
protocol: tcp
published: 443
target: 443
- mode: host
protocol: tcp
published: 80
target: 80
- mode: host
protocol: tcp
published: 444
target: 444
deploy:
placement:
constraints:
- node.role == manager
labels:
- traefik.enable=true
- traefik.http.routers.dashboard.entrypoints=localsecure
- traefik.http.routers.dashboard.rule=Host(`traefik.kun.is`)
- traefik.http.routers.dashboard.service=api@internal
- traefik.http.services.dashboard.loadbalancer.server.port=8080
- traefik.http.routers.dashboard.tls=true
- traefik.http.routers.dashboard.tls.certresolver=letsencrypt
- traefik.docker.network=traefik
- traefik.http.routers.esrom.entrypoints=websecure
- traefik.http.routers.esrom.service=esrom@file
- traefik.http.routers.esrom.rule=Host(`geokunis2.nl`)
- traefik.http.routers.esrom.tls=true
- traefik.http.routers.esrom.tls.certresolver=letsencrypt
- traefik.http.routers.uptime.entrypoints=localsecure
- traefik.http.routers.uptime.rule=Host(`uptime.kun.is`)
- traefik.http.routers.uptime.service=uptime@file
- traefik.http.routers.uptime.tls=true
- traefik.http.routers.uptime.tls.certresolver=letsencrypt
- traefik.http.routers.ntfy.entrypoints=websecure
- traefik.http.routers.ntfy.rule=Host(`ntfy.kun.is`)
- traefik.http.routers.ntfy.service=ntfy@file
- traefik.http.routers.ntfy.tls=true
- traefik.http.routers.ntfy.tls.certresolver=letsencrypt
- traefik.http.routers.apprise.entrypoints=localsecure
- traefik.http.routers.apprise.rule=Host(`apprise.kun.is`)
- traefik.http.routers.apprise.service=apprise@file
- traefik.http.routers.apprise.tls=true
- traefik.http.routers.apprise.tls.certresolver=letsencrypt
- traefik.http.routers.concourse.entrypoints=websecure
- traefik.http.routers.concourse.rule=Host(`ci.kun.is`)
- traefik.http.routers.concourse.service=concourse@file
- traefik.http.routers.concourse.tls=true
- traefik.http.routers.concourse.tls.certresolver=letsencrypt
- traefik.http.routers.discourse.entrypoints=websecure
- traefik.http.routers.discourse.rule=Host(`tuindersweijde.geokunis2.nl`)
- traefik.http.routers.discourse.service=discourse@file
- traefik.http.routers.discourse.tls=true
- traefik.http.routers.discourse.tls.certresolver=letsencrypt
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
- type: volume
source: acme
target: /acme
volume:
nocopy: true
configs:
- source: services
target: /etc/traefik/services.yml
command:
- --providers.docker
- --providers.docker.swarmmode
- --providers.docker.watch
- --providers.docker.exposedbydefault=false
- --providers.file.filename=/etc/traefik/services.yml
- --api
- --api.insecure=false
- --api.dashboard=true
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint=true
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
- --entrypoints.websecure.address=:443
- --entrypoints.localsecure.address=:444
- --certificatesresolvers.letsencrypt.acme=true
- --certificatesresolvers.letsencrypt.acme.email=pim@kunis.nl
- --certificatesresolvers.letsencrypt.acme.storage=/acme/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge=true
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
- --serversTransport.insecureSkipVerify=true
- --accesslog=true
- --accesslog.fields.defaultmode=keep
- --accesslog.fields.names.ClientUsername=drop
- --accesslog.fields.headers.defaultmode=keep
- --accesslog.fields.headers.names.User-Agent=keep
- --accesslog.fields.headers.names.Authorization=drop
- --accesslog.fields.headers.names.Content-Type=keep

View file

@ -0,0 +1,26 @@
http:
services:
esrom:
loadBalancer:
servers:
- url: http://esrom.dmz:80/
uptime:
loadBalancer:
servers:
- url: http://iris.dmz:3001
ntfy:
loadBalancer:
servers:
- url: http://iris.dmz:3002
apprise:
loadBalancer:
servers:
- url: http://iris.dmz:3003
concourse:
loadBalancer:
servers:
- url: http://concourse.dmz:3000
discourse:
loadBalancer:
servers:
- url: http://forum.dmz:80

View file

@ -0,0 +1,18 @@
- name: Create Traefik network
docker_network:
name: traefik
driver: overlay
- name: Create Docker config
docker_config:
name: traefik_services
data: "{{ lookup('file', '{{ role_path }}/services.yml') }}"
use_ssh_client: true
rolling_versions: true
register: services
- name: Deploy Docker stack
docker_stack:
name: traefik
compose:
- "{{ lookup('template', '{{ role_path }}/docker-stack.yml.j2') | from_yaml }}"

View file

@ -24,14 +24,14 @@ provider "libvirt" {
} }
module "setup_jefke" { module "setup_jefke" {
source = "../../terraform_modules/setup" source = "../../../terraform_modules/setup"
providers = { providers = {
libvirt = libvirt.jefke libvirt = libvirt.jefke
} }
} }
module "bancomart" { module "bancomart" {
source = "../../terraform_modules/debian" source = "../../../terraform_modules/debian"
name = "bancomart" name = "bancomart"
ram = 4096 ram = 4096
storage = 25 storage = 25
@ -41,14 +41,14 @@ module "bancomart" {
} }
module "setup_atlas" { module "setup_atlas" {
source = "../../terraform_modules/setup" source = "../../../terraform_modules/setup"
providers = { providers = {
libvirt = libvirt.atlas libvirt = libvirt.atlas
} }
} }
module "maestro" { module "maestro" {
source = "../../terraform_modules/debian" source = "../../../terraform_modules/debian"
name = "maestro" name = "maestro"
ram = 8192 ram = 8192
storage = 35 storage = 35

View file

@ -19,7 +19,7 @@ provider "libvirt" {
} }
module "hermes" { module "hermes" {
source = "../../terraform_modules/debian" source = "../../../terraform_modules/debian"
name = "hermes" name = "hermes"
ram = 1024 ram = 1024
storage = 25 storage = 25

View file

@ -0,0 +1,8 @@
[defaults]
roles_path=~/.ansible/roles:roles:/usr/share/ansible/roles:/etc/ansible/roles
inventory=inventory
vault_password_file=$HOME/.config/home/ansible-vault-secret
interpreter_python=/usr/bin/python3
[diff]
always = True

View file

@ -0,0 +1,19 @@
apt_install_packages:
- postgresql
- python3-psycopg2
- nfs-kernel-server
- qemu-guest-agent
nfs_exports: []
redis_bind_interface: 0.0.0.0
redis_requirepass: !vault |
$ANSIBLE_VAULT;1.1;AES256
37323965303638333264653936616563323235363463396330363836653865393835346263383838
3030386166316365633538353539623066626434313332390a616131303434373264633934356361
30356335643638656433326230363462373533396533366261346630353163353137333865303132
3536636165366631310a643538353331366130663464386565343331653031333061333330613532
34663932653734336239303536323331396435386332666133343033373566386562326136656330
63393766353063646361643565323238376334333637363232626139333664643065613237666532
31623032613763303136353232323837376637336431306534306336356165363039666634336433
30376464323862373833

View file

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

View file

@ -0,0 +1,6 @@
- name: apt
src: https://github.com/sunscrapers/ansible-role-apt.git
scm: git
- name: cloudinit_wait
src: https://git.kun.is/pim/ansible-role-cloudinit-wait
scm: git

View file

@ -0,0 +1,4 @@
- name: restart postgres
systemd:
name: postgresql
state: restarted

View file

@ -0,0 +1,15 @@
- name: Open postgres port
ini_file:
path: /etc/postgresql/15/main/postgresql.conf
section: null
option: listen_addresses
value: "'*'"
notify: restart postgres
- name: Change data directory
ini_file:
path: /etc/postgresql/15/main/postgresql.conf
section: null
option: data_directory
value: "'/mnt/data/postgresql'"
notify: restart postgres

View file

@ -0,0 +1,31 @@
---
- name: Wait for Cloud-Init to finish
hosts: all
gather_facts: no
roles:
- cloudinit_wait
- name: Setup NFS
hosts: thecloud
roles:
- {role: apt, tags: apt}
- {role: postgresql, tags: postgresql}
post_tasks:
- name: Ensure NFS exports directory exists
file:
path: /etc/exports.d
state: directory
- name: Start NFS
systemd:
name: nfs-kernel-server
state: started
enabled: true
- name: Enable Qemu guest agent
systemd:
name: qemu-guest-agent
state: started
enabled: true

View file

@ -0,0 +1,32 @@
terraform {
backend "pg" {
schema_name = "thecloud-data"
conn_str = "postgresql://terraform@jefke.hyp/terraformstates"
}
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "0.7.1" # https://github.com/dmacvicar/terraform-provider-libvirt/issues/1040
}
}
}
# https://libvirt.org/uri.html#libssh-and-libssh2-transport
provider "libvirt" {
uri = "qemu+ssh://root@lewis.hyp/system?known_hosts=/etc/ssh/ssh_known_hosts"
}
module "data_pool" {
source = "../../../terraform_modules/setup/data"
}
resource "libvirt_volume" "data" {
name = "thecloud-data.qcow2"
pool = "data"
size = 1024 * 1024 * 1024 * 150
}
output "data_disk_id" {
value = libvirt_volume.data.id
}

View file

@ -0,0 +1,40 @@
terraform {
backend "pg" {
schema_name = "thecloud"
conn_str = "postgresql://terraform@jefke.hyp/terraformstates"
}
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "0.7.1" # https://github.com/dmacvicar/terraform-provider-libvirt/issues/1040
}
}
}
# https://libvirt.org/uri.html#libssh-and-libssh2-transport
provider "libvirt" {
alias = "lewis"
uri = "qemu+ssh://root@lewis.hyp/system?known_hosts=/etc/ssh/ssh_known_hosts"
}
module "setup_lewis" {
source = "../../../terraform_modules/setup"
providers = {
libvirt = libvirt.lewis
}
}
module "thecloud" {
source = "../../../terraform_modules/debian"
name = "thecloud"
ram = 1024
storage = 25
mac = "CA:FE:C0:FF:EE:0A"
data_disk = "/mnt/data/volumes/thecloud-data.qcow2"
providers = {
libvirt = libvirt.lewis
}
depends_on = [ module.setup_lewis ]
}

View file

@ -4,4 +4,4 @@ Terraform modules we use for the virtual machines in our home network.
These are all personalized and probably of little use outside our network. These are all personalized and probably of little use outside our network.
The modules are currently: The modules are currently:
- `debian`: Personalized Debian VM using Terraform's `libvirt` provider - `debian`: Personalized Debian VM using Terraform's `libvirt` provider
- `invariants`: Invariants for our home network we use in multiple places. - `setup`: Prepares the physical machine with required libvirt pools and other prerequisites.

View file

@ -13,7 +13,21 @@ ssh_authorized_keys:
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOodpLr+FDRyKyHjucHizNLVFHZ5AQmE9GmxMnOsSoaw pimkunis@thinkpadpim" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOodpLr+FDRyKyHjucHizNLVFHZ5AQmE9GmxMnOsSoaw pimkunis@thinkpadpim"
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINUZp4BCxf7uLa1QWonx/Crf8tYZ5MKIZ+EuaBa82LrV user@user-laptop" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINUZp4BCxf7uLa1QWonx/Crf8tYZ5MKIZ+EuaBa82LrV user@user-laptop"
ssh_pwauth: false %{ if data_disk != null }
device_aliases:
data: /dev/vdb
disk_setup:
data:
table_type: 'gpt'
layout: true
overwrite: false
fs_type:
- label: 'data'
filesystem: 'ext4'
overwrite: false
mounts:
- ["data.1", "/mnt/data"]
%{ endif }
# TODO: Do we need this? # TODO: Do we need this?
runcmd: runcmd:

View file

@ -25,6 +25,7 @@ resource "libvirt_cloudinit_disk" "main" {
pool = "cloudinit" pool = "cloudinit"
user_data = templatefile("${path.module}/files/cloud_init.cfg.tftpl", { user_data = templatefile("${path.module}/files/cloud_init.cfg.tftpl", {
hostname = var.name hostname = var.name
data_disk = var.data_disk
}) })
network_config = templatefile("${path.module}/files/network_config.cfg.tftpl", { network_config = templatefile("${path.module}/files/network_config.cfg.tftpl", {
static_ip = var.static_ip static_ip = var.static_ip
@ -41,6 +42,14 @@ resource "libvirt_domain" "main" {
volume_id = libvirt_volume.os.id volume_id = libvirt_volume.os.id
} }
dynamic "disk" {
for_each = var.data_disk != null ? [1] : []
content {
volume_id = var.data_disk
}
}
network_interface { network_interface {
bridge = "bridgedmz" bridge = "bridgedmz"
hostname = var.name hostname = var.name

View file

@ -22,3 +22,8 @@ variable "static_ip" {
type = string type = string
default = null default = null
} }
variable "data_disk" {
type = string
default = null
}

View file

@ -0,0 +1,13 @@
terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
}
}
}
resource "libvirt_pool" "data" {
name = "data"
type = "dir"
path = "/mnt/data/volumes"
}

View file

@ -32,23 +32,23 @@
}; };
}; };
# lewis = { lewis = {
# name = "lewis"; name = "lewis";
# hostName = "lewis.hyp"; hostName = "lewis.hyp";
# nixosModule.custom = { nixosModule.custom = {
# disko.osDiskDevice = "/dev/sda"; disko.osDiskDevice = "/dev/sda";
# dataDisk = { dataDisk = {
# enable = true; enable = true;
# devicePath = "/dev/nvme0n1p1"; devicePath = "/dev/nvme0n1p1";
# }; };
# ssh = { ssh = {
# useCertificates = true; useCertificates = true;
# hostCert = builtins.readFile ./atlas_host_ed25519-cert.pub; hostCert = builtins.readFile ./lewis_host_ed25519-cert.pub;
# userCert = builtins.readFile ./atlas_user_ed25519-cert.pub; userCert = builtins.readFile ./lewis_user_ed25519-cert.pub;
# }; };
# }; };
# }; };
} }

Binary file not shown.

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> ssh-ed25519 aqswPA eCaomN/OCSet7JteG5GE1xArpi+zLdVjD415HRSa1hE
lgBm3ZCSzQlSOxLtLR6NW33TQcWgmU0nIMzZXpaTdsQ
--- z8b1blOyH3Ne62B4YO0UcwgGorWXHtguz453oHYUEV0
ð
"<22>ß]ËÞókéõR•»qÑ—+Œñf*Œ/Ö# Ü¢,ÁU,5ÔЈūQ9ÍKÃÆ>‰<²%/P@%ä#;0¼+Te §Šñ?ó”´º "½ÎSÊð
)bYZ†úÌ}-ëÜ™7UÞ%®:Ûë¼i
2ßú;íTÙ1ªû¤¿‰j…¸œNóëý´Ë#sY¸ÒòÔˆ¼dêR檸þ«Š}†W?ÆØ ÈZ¯0ô*ïäU¯Q”drÆɪ£Ûuù¦Õ]Û<> $÷E#¹=çhA𳬠kô³ã”+Z"¡…sÉ_Ÿ¥5%( ˪W€1GÆ€dÒ°Îwø!W¹pÒÈ¿J2 ù_.
x%ÒŸGï«/í»'ÇPd¬åW¦I<ÛS—î/ ³ÕϽÃ{ëòÞ3óïâÜ ¼˜5/E6`(袆( çðÇqpJ,÷ ÜÉ?&†÷½‘.8è×m¢žúiçÓA¦{-`¢=§GÏ<47>3濾Jzòuºa'Õk+•™“r×™RJHÃðOìâ”ò¯a½9§ÿf•ÓÌjüäT

View file

@ -21,9 +21,20 @@ let
"atlas_user_ed25519.age" "atlas_user_ed25519.age"
]; ];
}; };
lewis = {
publicKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL5lZjsqS6C50WO8p08TY7Fg8rqQH04EkpDTxCRGtR7a pim@x260"
];
encryptedFiles = [
"lewis_host_ed25519.age"
"lewis_user_ed25519.age"
];
};
}; };
in lib.attrsets.mergeAttrsList (builtins.map ({ publicKeys, encryptedFiles }: in
lib.attrsets.mergeAttrsList (builtins.map
({ publicKeys, encryptedFiles }:
lib.attrsets.mergeAttrsList (builtins.map lib.attrsets.mergeAttrsList (builtins.map
(encryptedFile: { "${encryptedFile}" = { inherit publicKeys; }; }) (encryptedFile: { "${encryptedFile}" = { inherit publicKeys; }; })
encryptedFiles)) (lib.attrsets.attrValues secrets)) encryptedFiles))
(lib.attrsets.attrValues secrets))