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" {
source = "../../terraform_modules/setup"
source = "../../../terraform_modules/setup"
providers = {
libvirt = libvirt.jefke
}
}
module "bancomart" {
source = "../../terraform_modules/debian"
source = "../../../terraform_modules/debian"
name = "bancomart"
ram = 4096
storage = 25
@ -41,14 +41,14 @@ module "bancomart" {
}
module "setup_atlas" {
source = "../../terraform_modules/setup"
source = "../../../terraform_modules/setup"
providers = {
libvirt = libvirt.atlas
}
}
module "maestro" {
source = "../../terraform_modules/debian"
source = "../../../terraform_modules/debian"
name = "maestro"
ram = 8192
storage = 35

View file

@ -19,7 +19,7 @@ provider "libvirt" {
}
module "hermes" {
source = "../../terraform_modules/debian"
source = "../../../terraform_modules/debian"
name = "hermes"
ram = 1024
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.
The modules are currently:
- `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 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?
runcmd:

View file

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

View file

@ -22,3 +22,8 @@ variable "static_ip" {
type = string
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 = {
# name = "lewis";
# hostName = "lewis.hyp";
lewis = {
name = "lewis";
hostName = "lewis.hyp";
# nixosModule.custom = {
# disko.osDiskDevice = "/dev/sda";
nixosModule.custom = {
disko.osDiskDevice = "/dev/sda";
# dataDisk = {
# enable = true;
# devicePath = "/dev/nvme0n1p1";
# };
dataDisk = {
enable = true;
devicePath = "/dev/nvme0n1p1";
};
# ssh = {
# useCertificates = true;
# hostCert = builtins.readFile ./atlas_host_ed25519-cert.pub;
# userCert = builtins.readFile ./atlas_user_ed25519-cert.pub;
# };
# };
# };
ssh = {
useCertificates = true;
hostCert = builtins.readFile ./lewis_host_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"
];
};
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
(encryptedFile: { "${encryptedFile}" = { inherit publicKeys; }; })
encryptedFiles)) (lib.attrsets.attrValues secrets))
encryptedFiles))
(lib.attrsets.attrValues secrets))