Compare commits
121 Commits
ba2891b18c
...
master
Author | SHA1 | Date | |
---|---|---|---|
1d5ce38922
|
|||
0b9e66f067
|
|||
379a113b86
|
|||
8538c00175
|
|||
645276018b
|
|||
ce5d682842
|
|||
de5b0f66bd
|
|||
64602b1db3
|
|||
caecb9b57e
|
|||
e8be04d5e1
|
|||
a7f90da43f
|
|||
0f80206c62
|
|||
1daff82cc5
|
|||
9b4293c624
|
|||
0d93e8094c
|
|||
b92ab556e5
|
|||
8086799c7b
|
|||
6ec5df4b66
|
|||
fb91e45806
|
|||
44f82434e7
|
|||
31ca27750e
|
|||
4be8d297ba
|
|||
bcd8e62691
|
|||
160f4219c5
|
|||
c518125bbd
|
|||
e16e23d18c
|
|||
ede37e7fa3
|
|||
b4cddb337a
|
|||
35f1abd718
|
|||
21b52a1887
|
|||
af39ca9de8
|
|||
e10b37a9f6
|
|||
85627f8931
|
|||
38e2294a65
|
|||
dfcb781a90
|
|||
2c8bd2bb8d
|
|||
592c3a062b
|
|||
04d789f0a4
|
|||
16719977c8
|
|||
6cd8d3b14b
|
|||
791caab704
|
|||
df60296f9d
|
|||
232d06a123
|
|||
a3886c8675
|
|||
db55fcd180
|
|||
53dd462cac
|
|||
28faff3c99
|
|||
2619b8f9e6
|
|||
8a9b3db287
|
|||
a72c67f070
|
|||
47745b7bc9
|
|||
c568f00db1
|
|||
99b6959c84
|
|||
fa65726096
|
|||
f9eaf7a41e
|
|||
d825b1f391
|
|||
b296a3f2fe
|
|||
8ff89c9ee1
|
|||
62a4e598bd
|
|||
b65aaa5072
|
|||
98b7aff274
|
|||
6eaf7f7390
|
|||
32e80282ef
|
|||
c8bd9f4ec3
|
|||
d3d189e284
|
|||
71fe688ef8
|
|||
c5d0f96bdf
|
|||
eea8db6499
|
|||
7893349da4
|
|||
a4c61f94e6
|
|||
da0a261ddd
|
|||
b9954d1bba
|
|||
3a23c08f37
|
|||
d1500ea373
|
|||
a77fefcded
|
|||
41fac2c4f9
|
|||
280ea24dea
|
|||
855bafee5b
|
|||
adde4e32c1
|
|||
527067146f
|
|||
93326907d2
|
|||
bcad87c6e0
|
|||
5d127d27ef
|
|||
2d6cb3ffe0
|
|||
e68920c0e2
|
|||
c5c15341b8
|
|||
cd4a7177d7
|
|||
daeef1bc4b
|
|||
ddae18f8b3
|
|||
8c8657fdd8
|
|||
c4b0200dc6
|
|||
38bafd7186
|
|||
c6db39b55a
|
|||
528512e665
|
|||
0e05d3e066
|
|||
4221fb0009
|
|||
255ac33e04
|
|||
0bdd2c2543
|
|||
155d065dd0
|
|||
9a3e646d8a
|
|||
f4b5fcb0f1
|
|||
3054836085
|
|||
838f959fd9
|
|||
5b60af6061
|
|||
d1eae9b5b5
|
|||
76328bf6c6
|
|||
a31cbbe18e
|
|||
132da79fab
|
|||
676f6626f2
|
|||
dda5cb4449
|
|||
4ae238a09a
|
|||
fcedbdbe3d
|
|||
e5ad8fda80
|
|||
c5a7db6a55
|
|||
30f7a913ab
|
|||
5a3c32ba73
|
|||
9f075fac11
|
|||
5e427e688d
|
|||
32437de3f1
|
|||
88b47fb32d
|
|||
cad1c9bd89
|
@@ -1,3 +1,5 @@
|
||||
---
|
||||
exclude_paths:
|
||||
- 'galaxy.roles/'
|
||||
- ".ansible/"
|
||||
- "galaxy.roles/"
|
||||
- "Taskfile.yml"
|
||||
|
@@ -9,6 +9,9 @@ indent_size = 4
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.yml.j2]
|
||||
indent_size = 2
|
||||
|
||||
[Vagrantfile]
|
||||
indent_size = 2
|
||||
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,9 +1,9 @@
|
||||
/.ansible
|
||||
/.idea
|
||||
/.vagrant
|
||||
/.vscode
|
||||
|
||||
/galaxy.roles/
|
||||
/ansible-vault-password-file
|
||||
/temp
|
||||
*.retry
|
||||
|
||||
test_smtp.py
|
||||
|
29
README.md
29
README.md
@@ -3,12 +3,11 @@
|
||||
Настройки виртуального сервера для домашних проектов.
|
||||
|
||||
> В этом проекте не самые оптимальные решения.
|
||||
> Но они помогают мне поддерживать сервер для моих личных проектов уже семь лет.
|
||||
> Но они помогают мне поддерживать сервер для моих личных проектов уже много лет.
|
||||
|
||||
## Требования
|
||||
|
||||
- [ansible](https://docs.ansible.com/ansible/latest/getting_started/index.html)
|
||||
- [invoke](https://www.pyinvoke.org/)
|
||||
- [task](https://taskfile.dev/)
|
||||
- [yq](https://github.com/mikefarah/yq)
|
||||
|
||||
@@ -21,7 +20,7 @@ $ ansible-galaxy install --role-file requirements.yml
|
||||
|
||||
## Структура
|
||||
|
||||
- Для каждого приложения создается свой пользователь.
|
||||
- Для каждого приложения создается свой пользователь (опционально).
|
||||
- Для доступа используется ssh-ключ.
|
||||
- Докер используется для запуска и изоляции приложений. Для загрузки образов настраивается Yandex Docker Registry.
|
||||
- Выход во внешнюю сеть через proxy server [Caddy](https://caddyserver.com/).
|
||||
@@ -32,30 +31,10 @@ $ ansible-galaxy install --role-file requirements.yml
|
||||
|
||||
В организации Яндекс: https://admin.yandex.ru/domains/vakhrushev.me?action=set_dns&uid=46045840
|
||||
|
||||
## Частые команды
|
||||
|
||||
Конфигурация приложений (если нужно добавить новое приложение):
|
||||
|
||||
```bash
|
||||
$ task configure-apps
|
||||
```
|
||||
|
||||
Конфигурация мониторинга (если нужно обновить netdata):
|
||||
|
||||
```bash
|
||||
$ task configure-monitoring
|
||||
```
|
||||
|
||||
## Деплой приложений
|
||||
|
||||
Доступные для деплоя приложения:
|
||||
Деплой всех приложений через ansible:
|
||||
|
||||
```bash
|
||||
invoke --list
|
||||
```
|
||||
|
||||
Выполнить команду деплоя, например:
|
||||
|
||||
```bash
|
||||
invoke deploy:gitea
|
||||
ansible-playbook -i production.yml --diff playbook-gitea.yml
|
||||
```
|
||||
|
46
Taskfile.yml
46
Taskfile.yml
@@ -12,8 +12,13 @@ vars:
|
||||
sh: 'yq .ungrouped.hosts.server.ansible_user {{.HOSTS_FILE}}'
|
||||
REMOTE_HOST:
|
||||
sh: 'yq .ungrouped.hosts.server.ansible_host {{.HOSTS_FILE}}'
|
||||
AUTHELIA_DOCKER: 'docker run --rm -v $PWD:/data authelia/authelia:4.39.4 authelia'
|
||||
|
||||
tasks:
|
||||
install-roles:
|
||||
cmds:
|
||||
- ansible-galaxy role install --role-file requirements.yml --force
|
||||
|
||||
ssh:
|
||||
cmds:
|
||||
- ssh {{.REMOTE_USER}}@{{.REMOTE_HOST}}
|
||||
@@ -22,11 +27,44 @@ tasks:
|
||||
cmds:
|
||||
- ssh {{.REMOTE_USER}}@{{.REMOTE_HOST}} -t btop
|
||||
|
||||
edit-vars:
|
||||
vars-decrypt:
|
||||
cmds:
|
||||
- ansible-vault edit vars/vars.yml
|
||||
env:
|
||||
EDITOR: micro
|
||||
- ansible-vault decrypt vars/vars.yml
|
||||
|
||||
vars-encrypt:
|
||||
cmds:
|
||||
- ansible-vault encrypt vars/vars.yml
|
||||
|
||||
authelia-cli:
|
||||
cmds:
|
||||
- "{{.AUTHELIA_DOCKER}} {{.CLI_ARGS}}"
|
||||
|
||||
authelia-validate-config:
|
||||
vars:
|
||||
DEST_FILE: "temp/configuration.yml"
|
||||
cmds:
|
||||
- >
|
||||
ansible localhost
|
||||
--module-name template
|
||||
--args "src=files/authelia/configuration.template.yml dest={{.DEST_FILE}}"
|
||||
--extra-vars "@vars/secrets.yml"
|
||||
--extra-vars "@files/authelia/secrets.yml"
|
||||
- defer: rm -f {{.DEST_FILE}}
|
||||
- >
|
||||
{{.AUTHELIA_DOCKER}}
|
||||
validate-config --config /data/{{.DEST_FILE}}
|
||||
|
||||
authelia-gen-random-string:
|
||||
cmds:
|
||||
- >
|
||||
{{.AUTHELIA_DOCKER}}
|
||||
crypto rand --length 32 --charset alphanumeric
|
||||
|
||||
authelia-gen-secret-and-hash:
|
||||
cmds:
|
||||
- >
|
||||
{{.AUTHELIA_DOCKER}}
|
||||
crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
|
||||
|
||||
format-py-files:
|
||||
cmds:
|
||||
|
28
Vagrantfile
vendored
28
Vagrantfile
vendored
@@ -1,28 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Этот файл предназначен для запуска тестовой виртуальной машины,
|
||||
# на которой можно обкатать роли для настройки сервера.
|
||||
|
||||
|
||||
ENV["LC_ALL"] = "en_US.UTF-8"
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
|
||||
config.vm.box = "ubuntu/bionic64"
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.memory = 2048
|
||||
v.cpus = 2
|
||||
end
|
||||
|
||||
config.vm.network "private_network", ip: "192.168.50.10"
|
||||
|
||||
# Приватный ключ для доступа к машине
|
||||
config.vm.provision "shell" do |s|
|
||||
ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip
|
||||
s.inline = <<-SHELL
|
||||
echo #{ssh_pub_key} >> /home/vagrant/.ssh/authorized_keys
|
||||
echo #{ssh_pub_key} >> /root/.ssh/authorized_keys
|
||||
SHELL
|
||||
end
|
||||
end
|
@@ -1,3 +0,0 @@
|
||||
WEB_SERVER_PORT=9494
|
||||
USER_UID=1000
|
||||
USER_GID=1000
|
1
app/gitea/.gitignore
vendored
1
app/gitea/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
data/
|
@@ -1,16 +0,0 @@
|
||||
services:
|
||||
|
||||
server:
|
||||
image: gitea/gitea:1.22.6
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- "USER_UID=${USER_UID}"
|
||||
- "USER_GID=${USER_GID}"
|
||||
- "GITEA__server__SSH_PORT=2222"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- "${WEB_SERVER_PORT}:3000"
|
||||
- "2222:22"
|
@@ -1,5 +0,0 @@
|
||||
WEB_SERVER_PORT=9595
|
||||
KEYCLOAK_ADMIN=admin
|
||||
KEYCLOAK_ADMIN_PASSWORD=password
|
||||
USER_UID=1000
|
||||
USER_GID=1000
|
1
app/keycloak/.gitignore
vendored
1
app/keycloak/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
data/
|
@@ -1,22 +0,0 @@
|
||||
# Images: https://quay.io/repository/keycloak/keycloak?tab=tags&tag=latest
|
||||
# Configuration: https://www.keycloak.org/server/all-config
|
||||
|
||||
# NB
|
||||
# - На проде были проблемы с правами к директории data, пришлось выдать 777
|
||||
# - Переменную KC_HOSTNAME_ADMIN_URL нужно указать вместе с KC_HOSTNAME_URL, иначе будут ошибки 403
|
||||
|
||||
services:
|
||||
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:24.0.4
|
||||
command: ["start-dev"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
KEYCLOAK_ADMIN: "${KEYCLOAK_ADMIN}"
|
||||
KEYCLOAK_ADMIN_PASSWORD: "${KEYCLOAK_ADMIN_PASSWORD}"
|
||||
KC_HOSTNAME_URL: "https://kk.vakhrushev.me"
|
||||
KC_HOSTNAME_ADMIN_URL: "https://kk.vakhrushev.me"
|
||||
ports:
|
||||
- "${WEB_SERVER_PORT}:8080"
|
||||
volumes:
|
||||
- "./data:/opt/keycloak/data"
|
@@ -1,16 +0,0 @@
|
||||
# Images: https://quay.io/repository/keycloak/keycloak?tab=tags&tag=latest
|
||||
# Configuration: https://www.keycloak.org/server/all-config
|
||||
|
||||
services:
|
||||
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:24.0.4
|
||||
command: ["start-dev"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
KEYCLOAK_ADMIN: "${KEYCLOAK_ADMIN}"
|
||||
KEYCLOAK_ADMIN_PASSWORD: "${KEYCLOAK_ADMIN_PASSWORD}"
|
||||
ports:
|
||||
- "${WEB_SERVER_PORT}:8080"
|
||||
volumes:
|
||||
- "./data:/opt/keycloak/data"
|
@@ -1,60 +0,0 @@
|
||||
services:
|
||||
|
||||
outline-app:
|
||||
image: outlinewiki/outline:0.81.1
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${WEB_SERVER_PORT}:3000"
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
environment:
|
||||
NODE_ENV: '${NODE_ENV}'
|
||||
SECRET_KEY: '${SECRET_KEY}'
|
||||
UTILS_SECRET: '${UTILS_SECRET}'
|
||||
DATABASE_URL: '${DATABASE_URL}'
|
||||
PGSSLMODE: '${PGSSLMODE}'
|
||||
REDIS_URL: '${REDIS_URL}'
|
||||
URL: '${URL}'
|
||||
FILE_STORAGE: '${FILE_STORAGE}'
|
||||
FILE_STORAGE_UPLOAD_MAX_SIZE: '262144000'
|
||||
AWS_ACCESS_KEY_ID: '${AWS_ACCESS_KEY_ID}'
|
||||
AWS_SECRET_ACCESS_KEY: '${AWS_SECRET_ACCESS_KEY}'
|
||||
AWS_REGION: '${AWS_REGION}'
|
||||
AWS_S3_ACCELERATE_URL: '${AWS_S3_ACCELERATE_URL}'
|
||||
AWS_S3_UPLOAD_BUCKET_URL: '${AWS_S3_UPLOAD_BUCKET_URL}'
|
||||
AWS_S3_UPLOAD_BUCKET_NAME: '${AWS_S3_UPLOAD_BUCKET_NAME}'
|
||||
AWS_S3_FORCE_PATH_STYLE: '${AWS_S3_FORCE_PATH_STYLE}'
|
||||
AWS_S3_ACL: '${AWS_S3_ACL}'
|
||||
OIDC_CLIENT_ID: '${OIDC_CLIENT_ID}'
|
||||
OIDC_CLIENT_SECRET: '${OIDC_CLIENT_SECRET}'
|
||||
OIDC_AUTH_URI: '${OIDC_AUTH_URI}'
|
||||
OIDC_TOKEN_URI: '${OIDC_TOKEN_URI}'
|
||||
OIDC_USERINFO_URI: '${OIDC_USERINFO_URI}'
|
||||
OIDC_LOGOUT_URI: '${OIDC_LOGOUT_URI}'
|
||||
OIDC_USERNAME_CLAIM: '${OIDC_USERNAME_CLAIM}'
|
||||
OIDC_DISPLAY_NAME: '${OIDC_DISPLAY_NAME}'
|
||||
|
||||
redis:
|
||||
image: redis:7.2-bookworm
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- ./redis.conf:/redis.conf
|
||||
command: ["redis-server", "/redis.conf"]
|
||||
|
||||
postgres:
|
||||
image: postgres:16.3-bookworm
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- ./data/postgres:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: '${POSTGRES_USER}'
|
||||
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD}'
|
||||
POSTGRES_DB: '${POSTGRES_DB}'
|
||||
|
||||
volumes:
|
||||
database-data:
|
@@ -1,55 +0,0 @@
|
||||
# See versions: https://github.com/gramps-project/gramps-web/pkgs/container/grampsweb
|
||||
|
||||
services:
|
||||
|
||||
grampsweb: &grampsweb
|
||||
image: ghcr.io/gramps-project/grampsweb:v24.12.2
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:${WEB_SERVER_PORT}:5000" # host:docker
|
||||
environment:
|
||||
GRAMPSWEB_TREE: "Gramps" # will create a new tree if not exists
|
||||
GRAMPSWEB_SECRET_KEY: "${SECRET_KEY}"
|
||||
GRAMPSWEB_BASE_URL: "https://gramps.vakhrushev.me"
|
||||
GRAMPSWEB_REGISTRATION_DISABLED: "true"
|
||||
GRAMPSWEB_CELERY_CONFIG__broker_url: "redis://grampsweb_redis:6379/0"
|
||||
GRAMPSWEB_CELERY_CONFIG__result_backend: "redis://grampsweb_redis:6379/0"
|
||||
GRAMPSWEB_RATELIMIT_STORAGE_URI: redis://grampsweb_redis:6379/1
|
||||
GRAMPSWEB_EMAIL_HOST: "${POSTBOX_HOST}"
|
||||
GRAMPSWEB_EMAIL_PORT: "${POSTBOX_PORT}"
|
||||
GRAMPSWEB_EMAIL_HOST_USER: "${POSTBOX_USER}"
|
||||
GRAMPSWEB_EMAIL_HOST_PASSWORD: "${POSTBOX_PASS}"
|
||||
GRAMPSWEB_EMAIL_USE_TLS: "false"
|
||||
GRAMPSWEB_DEFAULT_FROM_EMAIL: "gramps@vakhrushev.me"
|
||||
GUNICORN_NUM_WORKERS: 4
|
||||
# media storage at s3
|
||||
GRAMPSWEB_MEDIA_BASE_DIR: "s3://av-gramps-media-storage"
|
||||
AWS_ENDPOINT_URL: "https://storage.yandexcloud.net"
|
||||
AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}"
|
||||
AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}"
|
||||
AWS_DEFAULT_REGION: "ru-central1"
|
||||
depends_on:
|
||||
- grampsweb_redis
|
||||
volumes:
|
||||
- ./data/gramps_users:/app/users # persist user database
|
||||
- ./data/gramps_index:/app/indexdir # persist search index
|
||||
- ./data/gramps_thumb_cache:/app/thumbnail_cache # persist thumbnails
|
||||
- ./data/gramps_cache:/app/cache # persist export and report caches
|
||||
- ./data/gramps_secret:/app/secret # persist flask secret
|
||||
- ./data/gramps_db:/root/.gramps/grampsdb # persist Gramps database
|
||||
- ./data/gramps_media:/app/media # persist media files
|
||||
- ./data/gramps_tmp:/tmp
|
||||
|
||||
grampsweb_celery:
|
||||
<<: *grampsweb # YAML merge key copying the entire grampsweb service config
|
||||
ports: []
|
||||
container_name: grampsweb_celery
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- grampsweb_redis
|
||||
command: celery -A gramps_webapi.celery worker --loglevel=INFO --concurrency=2
|
||||
|
||||
grampsweb_redis:
|
||||
image: docker.io/library/redis:7.2.4-alpine
|
||||
container_name: grampsweb_redis
|
||||
restart: unless-stopped
|
@@ -1,21 +0,0 @@
|
||||
services:
|
||||
navidrome:
|
||||
image: deluan/navidrome:0.54.2
|
||||
ports:
|
||||
- "127.0.0.1:${WEB_SERVER_PORT}:4533"
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ND_BASEURL: "https://music.vakhrushev.me"
|
||||
ND_LOGLEVEL: "info"
|
||||
ND_SCANSCHEDULE: "0 4 * * *"
|
||||
ND_SESSIONTIMEOUT: "24h"
|
||||
ND_AUTOIMPORTPLAYLISTS: "false"
|
||||
volumes:
|
||||
- "./data:/data"
|
||||
- "yandex-storage:/music:ro"
|
||||
|
||||
volumes:
|
||||
yandex-storage:
|
||||
driver: "rclone"
|
||||
driver_opts:
|
||||
remote: "yandex-s3-music-encrypted:"
|
1683
files/authelia/configuration.template.yml
Normal file
1683
files/authelia/configuration.template.yml
Normal file
File diff suppressed because it is too large
Load Diff
15
files/authelia/docker-compose.yml.j2
Normal file
15
files/authelia/docker-compose.yml.j2
Normal file
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
|
||||
authelia_app:
|
||||
container_name: 'authelia_app'
|
||||
image: 'docker.io/authelia/authelia:4.39.8'
|
||||
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}'
|
||||
restart: 'unless-stopped'
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
volumes:
|
||||
- "{{ config_dir }}:/config"
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
136
files/authelia/secrets.yml
Normal file
136
files/authelia/secrets.yml
Normal file
@@ -0,0 +1,136 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
37373465363866623436393966626530656465653837363463323664383666663164363233623738
|
||||
3233383234343332623065386134643161346132653431350a303935373631656366633339663333
|
||||
32353263346437626633346263323533313238613462613334353334643236343438306630333037
|
||||
6435313930313262310a386662336637623461303636633337303531353261343861313966383764
|
||||
32353439333364353434653164666434326232383562363063313433373137383138396266383134
|
||||
36613538653531346232353236313262313138656234626638623034363436303337313961333536
|
||||
66366666383363333439333439623931626662383764393463663733333034636633353538656137
|
||||
62386263613533343963396166666532313862366433636536613266353064633932323765336362
|
||||
34643634643962333563346633306665313765393663306364363362333536646635343832333634
|
||||
30383361653063396333616433323235663338346439303465323135626639646166303164643339
|
||||
31616534633034393339373934346531633433323433646436333863306566356462613531663136
|
||||
61343561616434306163616130626338663737633866646537323263316636626137366361363963
|
||||
31303361366365616335363230343239663038623830303232376236393639663232333764643064
|
||||
34666439316430356664313531333363626562633636326463313765343263393636333465386339
|
||||
33623037343134633535303863626564373630656463336330396336303462373735346331616663
|
||||
63666161356565643539343431386231396162323030383836366161303634626266663934356362
|
||||
61623833613734333661613338373663663230363331373236323166636534613962613763343663
|
||||
31666534303965333466653335646263343764346465373461326166666266303138363933653566
|
||||
37636530306632346636626336616536346236663664383864623863653835366133633635613861
|
||||
63303634333962343039646564353534313063383434386462366333386331303433366665623734
|
||||
61353039313762383664626330663230656237373061616132376564323763393632356665306633
|
||||
34643865333165616664376162306634366532386437383461396163376366363832363834356164
|
||||
64376637373135383539353636346461353761366561303530326363366238393932333039313264
|
||||
34316539626365306461323336396631633532306637306331373863613531656565366236656338
|
||||
30306237626561613561353265643137353965313033313939643161613163643566663632663964
|
||||
61623134656238363134626530363933623930346532336366393163363562386265626233393139
|
||||
63633333313531666335376538613765663933626533636137306564616333373766613665613332
|
||||
65643331626361626136623432346233633364343963653932306632646436626433653337326665
|
||||
39343033353030616630663865613630613032333831626538323461383264636633623439393765
|
||||
65343866323237386336613764386439313830646239613135636161333138646664666339626137
|
||||
36616433393339346139323333363030613731313236636464393864616135346234643664343533
|
||||
31396661306664343031393865306533373762663962623730313261353231363661306134623934
|
||||
64613931356164386431663536363361386566353361333861666365636564643536306638376238
|
||||
37633865303063643962346664346366346362313463386432376637663934363165343537323532
|
||||
63363431623663656163316662343435636165306134373839613731326139636337343862326338
|
||||
61326433343631343065303735663434316330303139303834316137663330363762666664393062
|
||||
30653236636538396234313735613365386635323062666236656164633136313362643834336339
|
||||
32623834343334613839313138313462376237666238636663343333643533643537376261666433
|
||||
64323933323134393461623034623563316135333566326135326434613237363830623063626535
|
||||
66323533356366346130626530633337393263663664353430646330353339313534396434653137
|
||||
36663737396261653162663337663338373433653233616363626130333833323533303363643730
|
||||
66613135633761346433626164643130353963313762643361306537653639373934333565373439
|
||||
31636133383866373032373562653933316163353936386339326266363233663633623437346665
|
||||
66393630346434356563393039626537336537313930393437663562303031626338616266363361
|
||||
65653033363539633364326531653563383634303830386362303665306438663035373831646562
|
||||
30356564623733623939663332393463663730643533666134636361316263633166626566333831
|
||||
66376461383139626230343136373437393464646331633139633435303236623132343035373037
|
||||
39363131623330376262326235646633303232623139626239326361313236316665316464616265
|
||||
35303166333561626130323864363430663332306338353731383139386131346132653632633132
|
||||
37643865393462623831623435633838323664666264623232326561653866626437373864666232
|
||||
64393466326162323236353539326364336238643031313434346566316434383733663663356334
|
||||
62343337386532393236353432653239643735323531306337373739343839306264356666636635
|
||||
35626665646634653766323939633434303238633564613962643364356631623539623032363039
|
||||
31336535333763323236633531616661313834636231363362376661313931623131343364356364
|
||||
35653539393265323636303930633639316139643631386632646139643266303531653865623664
|
||||
31303930613561323330356337396138633033616265356137353336613638656161633063663964
|
||||
33373965383532656634333863643131333461376135646635323035316230393439386130663036
|
||||
37373331356364343433316435666130373031303038643063313131653835333365366138656238
|
||||
38643437376234316332306434633039346564643863656461353364346335353839303734366565
|
||||
32613364356532623231623632363637373664393764636262346264383134366439373238623032
|
||||
62326163306532356262393565373937316530623963313266373736356632313831313465666663
|
||||
61303962333836373832383236663532376130316465393039326366636133323233316134646430
|
||||
63313437663662353962633561643535396332303533343962643038393165373239336431336664
|
||||
65356663393565383263613530663762643731356463653538316439303863643363303261363838
|
||||
33353739306337326665393164366232393665363465343537373866396136346164333663393738
|
||||
31316335366238316537386236393461656266323566636364343139393665616138663432333564
|
||||
37653837646666376530373530636164343633653162343131373034633432333138613138346339
|
||||
32303332626338653561323835343266353633613434346465616162326162393733643837663230
|
||||
62333630333464633362656661336139393639623863343036636534323637336561333734373262
|
||||
36373365306531383830383361303566626239343062316166303636323539373966626336613638
|
||||
31623863643632653036323834346362663834666431643637666137376139386666643834323465
|
||||
63626264643337376663633335666631653637626364653866353131393336303937633430366430
|
||||
38383066313831346461643862333838386566613661623130313038386137323331373434363033
|
||||
34613537303134343532643430383532353934623066383530653435626566333239333162316435
|
||||
30386361346336666665656336646633353663376337326131663435636533646162616332306530
|
||||
30373263376437646639623039343234393537303931346461643966383732336366363331643135
|
||||
65383462353034643464366334636136373035313437366639336338366133663765313735353366
|
||||
62386239326134343761313464383239316465633932363862303536353365376338643863643834
|
||||
32633233336262626336383061316137646431353766303930336562386136383530613538383837
|
||||
66336235643437636138646663393565383466636232366133343232306563363635316561346565
|
||||
36633637303163303963396132653731663134666238363939666663303033316564356364633162
|
||||
31376233306138666131333634336639643163666562653934336162323964613863646564616361
|
||||
33666264303163326664633839303562333664383130356134383836313635376239353137313363
|
||||
66613132663137383737373530326131663861303935663635373464306334363962333566663261
|
||||
64616361366137626163626139333630653331383763353632396130306231376662666363353962
|
||||
66343064313932386631303663373432646135353438313632316634336235316139313237666362
|
||||
31383638393362663038363765303634363366326265643332393165643635643339343137373930
|
||||
37633031626365353033333938623466663963376366353561303166373164386132376365643630
|
||||
32333134316464643564373537643734353534366563666435663663616331383039393862613838
|
||||
63633962316533386337366263636265646334636235323430383832363964343939633264343338
|
||||
64366539383831373636343330356537323662666533323935646634363466663239663362326531
|
||||
66363863383762303539373636336330353834303239376330623964393439356130646166663332
|
||||
65326162336366363466646230656362653531386162356235326235333866373966636434326537
|
||||
38653139393563373337346636333337373039343439343139313366316264613763663664633037
|
||||
32313237653239643635393363316465346561613331623033323137653865396239633639323534
|
||||
61326262643365363737393031383461626530636266393836663937666135363662353665376362
|
||||
63643039373931316439303731663762393237623065643236303737643966393836646335626132
|
||||
36376665656662373437653933356330636638626162666564393636633630663562303839326662
|
||||
35626261616534386361373539633636356136616137323737393466633364653730356138343638
|
||||
36663537663361393566616365383161366236646630653737643765666638346531376136643163
|
||||
37643530373330353238323431343761653633306464643835343333623837313135303031666535
|
||||
30326538636432303363666131376334393361333232313834623230306630373834633265333237
|
||||
34323731333835363863643031346166636464653731636636313161643265613861336638313338
|
||||
32383438353763343933383537636464666466386131363566306562333136356538326239656232
|
||||
33343631613134616265393232613063643561633335323665383133313536313364343066366665
|
||||
63636439376436393162306638303062616435333039343566613961626434303766616535623364
|
||||
32643866393430303137366264306262643365663034613965666332313430366630653736303537
|
||||
39663832326132393066373166613161613130363033363633366563353461663435393565326362
|
||||
37616462393933303937643664343663373234663066633834626164303866323835363333366266
|
||||
36356133336165613032646436306162663534306239313330353935336332643637653534626233
|
||||
38303965646361316434343131653461353234396163613736333235656639326231353734636266
|
||||
61613566336437666265366637336363326266383666383165343661333766303830633633393664
|
||||
34613061396564616337643032666561633038663062336233666263306132663139396565323035
|
||||
33353438633338363263313630393239376162366461383265386633613939663461616233396334
|
||||
35616433663862616530636362396333343464393339396538333861303763393066626439396361
|
||||
32303732393062383662633937653531653933333463366638613035333832636235346233653866
|
||||
35656664636636326163353439626538343463613465613634656530366566323165623162303565
|
||||
31303139343138616132383731323061346431336133643735356532373838663761313139663361
|
||||
33656365636261303532333131346633373732643232303139353431663132346532616334613034
|
||||
30363137613133396335343162643936623330393834356365663932626262313366616534663033
|
||||
37376132656233633361623733356334636266383361656437613331306636656333623139303661
|
||||
38353639346266333833663533366661633136313262396465633738373438623262306637643336
|
||||
34656136343139663461336264346666333537633065343766316630626566363761396537643334
|
||||
30323766633664666639363965363138396334343365346333663035323839613030626533303830
|
||||
31333734386565383831373939306265636432386332313531623638333663643162623339613366
|
||||
34363935636266313736366639373833636230633661323935646331376336623937353039343561
|
||||
39623865663462663431643738653663663733663765383663623437383163613232336332653531
|
||||
64663133353934313436336633666435343162316135303663636130353936363936363032313263
|
||||
65376436316237663434323736663263376164346139616465663737323963316361373438633339
|
||||
31323261343635633338613636643232616537653331326331353161396331633461643861323466
|
||||
64633033623537386263376263346666633939336133616234363964363339616331636464326163
|
||||
63633862373030323132613439343431333938343864383637613435323732356234613965666364
|
||||
37343765353735633737393664306533633262353562323565306537646534663833343430643662
|
||||
39326134353335653938396532363136376332306162613836663464636233383436333735663731
|
||||
313461396466396230323561646662653063
|
37
files/authelia/users.secrets.yml
Normal file
37
files/authelia/users.secrets.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
33323463653739626134366261626263396338333966376262313263613131343962326432613263
|
||||
6430616564313432666436376432383539626231616438330a646161313364353566373833353337
|
||||
64633361306564646564663736663937303435356332316432666135353863393439663235646462
|
||||
3136303031383835390a396531366636386133656366653835633833633733326561383066656464
|
||||
31613933333731643065316130303561383563626636346633396266346332653234373732326535
|
||||
39663765353938333835646563663633393835633163323435303164663261303661666435306239
|
||||
34353264633736383565306336633565376436646536623835613330393466363935303031346664
|
||||
63626465656435383162633761333131393934666632336539386435613362353135383538643836
|
||||
66373261306139353134393839333539366531393163393266386531613732366431663865343134
|
||||
64363933616338663966353431396133316561653366396130653232636561343739336265386339
|
||||
38646238653436663531633465616164303633356233363433623038666465326339656238653233
|
||||
36323162303233633935646132353835336364303833636563346535316166346533636536656665
|
||||
64323030616665316133363739393364306462316135636630613262646436643062373138656431
|
||||
35663334616239623534383564643738616264373762663034376332323637626337306639653830
|
||||
65386339666465343931303933663561643664313364386662656663643336636264636333666435
|
||||
66366531613538363233346137383462326334306534333564636232393931393433386664363036
|
||||
39623134636331646536323531653063326231613363366562643561353939633062663132303035
|
||||
38303265326136303633666566613966636133666336396133333033643434303138303065666463
|
||||
36643765316134636133333937396332613233383932663265386264623133633364646237346465
|
||||
32623965653662336335366639643765393636623236323036396538353666646132393636663536
|
||||
65646638643236313762373135336430643731643961386264303134366633353934366431333430
|
||||
34313362633836613166336437323835626537653237666139383230663835626630623933383834
|
||||
32636136663830643661363663303136393733646133626538333836666135653936323832336433
|
||||
64396234396430326334656561393264366263313730306631383037643135613765373861356561
|
||||
37363933383238316232336564363364376637626630373963666262376165343838303530653764
|
||||
64343937666365646666363939383662313334656236326566373565643637313434616261616635
|
||||
35646131396432623534396133666239613036386332663038353531313935636139363136666562
|
||||
62616234663935383262626235313337623332333733383035666633393965336535316234323561
|
||||
37353563623138343339616565653465633633383563636631356333303435376536393634343031
|
||||
63653062303432366230643333353634383061313135616533643935316263393366653335353964
|
||||
36363135356365373064613338393261326265396330323930613538326330663532616163666564
|
||||
39313631633434353938626637626462376139383536306531633733646331303030333238373161
|
||||
36336364383939663132366461383264346631366566363638333738386235623264623331343738
|
||||
34316436393363323165396430343163653837623035626236313663643038336666633535666462
|
||||
33323566353062653964643362363233346264396365336637376661323730336437333031363830
|
||||
38303962646561346262
|
326
files/backups/backup-all.template.py
Normal file
326
files/backups/backup-all.template.py
Normal file
@@ -0,0 +1,326 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Backup script for all applications
|
||||
Automatically discovers and runs backup scripts for all users,
|
||||
then creates restic backups and sends notifications.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import logging
|
||||
import pwd
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple, Optional
|
||||
import requests
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout),
|
||||
logging.FileHandler("/var/log/backup-all.log"),
|
||||
],
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Configuration from Ansible template variables
|
||||
RESTIC_REPOSITORY = "{{ restic_repository }}"
|
||||
RESTIC_PASSWORD = "{{ restic_password }}"
|
||||
AWS_ACCESS_KEY_ID = "{{ restic_s3_access_key }}"
|
||||
AWS_SECRET_ACCESS_KEY = "{{ restic_s3_access_secret }}"
|
||||
AWS_DEFAULT_REGION = "{{ restic_s3_region }}"
|
||||
TELEGRAM_BOT_TOKEN = "{{ notifications_tg_bot_token }}"
|
||||
TELEGRAM_CHAT_ID = "{{ notifications_tg_chat_id }}"
|
||||
NOTIFICATIONS_NAME = "{{ notifications_name }}"
|
||||
|
||||
|
||||
class BackupManager:
|
||||
def __init__(self):
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
self.successful_backups = []
|
||||
|
||||
def get_home_directories(self) -> List[Tuple[str, str]]:
|
||||
"""Get all home directories and their owners"""
|
||||
home_dirs = []
|
||||
home_path = Path("/home")
|
||||
|
||||
if not home_path.exists():
|
||||
logger.error("/home directory does not exist")
|
||||
return home_dirs
|
||||
|
||||
for user_dir in home_path.iterdir():
|
||||
if user_dir.is_dir():
|
||||
try:
|
||||
# Get the owner of the directory
|
||||
stat_info = user_dir.stat()
|
||||
owner = pwd.getpwuid(stat_info.st_uid).pw_name
|
||||
home_dirs.append((str(user_dir), owner))
|
||||
except (KeyError, OSError) as e:
|
||||
logger.warning(f"Could not get owner for {user_dir}: {e}")
|
||||
|
||||
return home_dirs
|
||||
|
||||
def find_backup_script(self, home_dir: str) -> Optional[str]:
|
||||
"""Find backup script in user's home directory"""
|
||||
possible_scripts = [
|
||||
os.path.join(home_dir, "backup.sh"),
|
||||
os.path.join(home_dir, "backup"),
|
||||
]
|
||||
|
||||
for script_path in possible_scripts:
|
||||
if os.path.exists(script_path):
|
||||
# Check if file is executable
|
||||
if os.access(script_path, os.X_OK):
|
||||
return script_path
|
||||
else:
|
||||
logger.warning(
|
||||
f"Backup script {script_path} exists but is not executable"
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def run_user_backup(self, script_path: str, username: str) -> bool:
|
||||
"""Run backup script as the specified user"""
|
||||
try:
|
||||
logger.info(f"Running backup script {script_path} as user {username}")
|
||||
|
||||
# Use su to run the script as the user
|
||||
cmd = ["su", "--login", username, "--command", script_path]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd, capture_output=True, text=True, timeout=3600 # 1 hour timeout
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.info(f"Backup script for {username} completed successfully")
|
||||
self.successful_backups.append(username)
|
||||
return True
|
||||
else:
|
||||
error_msg = f"Backup script for {username} failed with return code {result.returncode}"
|
||||
if result.stderr:
|
||||
error_msg += f": {result.stderr}"
|
||||
logger.error(error_msg)
|
||||
self.errors.append(f"User {username}: {error_msg}")
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
error_msg = f"Backup script for {username} timed out"
|
||||
logger.error(error_msg)
|
||||
self.errors.append(f"User {username}: {error_msg}")
|
||||
return False
|
||||
except Exception as e:
|
||||
error_msg = f"Failed to run backup script for {username}: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
self.errors.append(f"User {username}: {error_msg}")
|
||||
return False
|
||||
|
||||
def get_backup_directories(self) -> List[str]:
|
||||
"""Get all backup directories that exist"""
|
||||
backup_dirs = []
|
||||
home_dirs = self.get_home_directories()
|
||||
|
||||
for home_dir, _ in home_dirs:
|
||||
backup_path = os.path.join(home_dir, "backups")
|
||||
if os.path.exists(backup_path) and os.path.isdir(backup_path):
|
||||
backup_dirs.append(backup_path)
|
||||
|
||||
return backup_dirs
|
||||
|
||||
def run_restic_backup(self, backup_dirs: List[str]) -> bool:
|
||||
"""Run restic backup for all backup directories"""
|
||||
if not backup_dirs:
|
||||
logger.warning("No backup directories found")
|
||||
return True
|
||||
|
||||
try:
|
||||
logger.info("Starting restic backup")
|
||||
|
||||
# Set environment variables for restic
|
||||
env = os.environ.copy()
|
||||
env.update(
|
||||
{
|
||||
"RESTIC_REPOSITORY": RESTIC_REPOSITORY,
|
||||
"RESTIC_PASSWORD": RESTIC_PASSWORD,
|
||||
"AWS_ACCESS_KEY_ID": AWS_ACCESS_KEY_ID,
|
||||
"AWS_SECRET_ACCESS_KEY": AWS_SECRET_ACCESS_KEY,
|
||||
"AWS_DEFAULT_REGION": AWS_DEFAULT_REGION,
|
||||
}
|
||||
)
|
||||
|
||||
# Run backup
|
||||
backup_cmd = ["restic", "backup", "--verbose"] + backup_dirs
|
||||
result = subprocess.run(backup_cmd, env=env, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
error_msg = f"Restic backup failed: {result.stderr}"
|
||||
logger.error(error_msg)
|
||||
self.errors.append(f"Restic backup: {error_msg}")
|
||||
return False
|
||||
|
||||
logger.info("Restic backup completed successfully")
|
||||
|
||||
# Run check
|
||||
check_cmd = ["restic", "check"]
|
||||
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
error_msg = f"Restic check failed: {result.stderr}"
|
||||
logger.error(error_msg)
|
||||
self.errors.append(f"Restic check: {error_msg}")
|
||||
return False
|
||||
|
||||
logger.info("Restic check completed successfully")
|
||||
|
||||
# Run forget and prune
|
||||
forget_cmd = [
|
||||
"restic",
|
||||
"forget",
|
||||
"--compact",
|
||||
"--prune",
|
||||
"--keep-daily",
|
||||
"90",
|
||||
"--keep-monthly",
|
||||
"36",
|
||||
]
|
||||
result = subprocess.run(forget_cmd, env=env, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
error_msg = f"Restic forget/prune failed: {result.stderr}"
|
||||
logger.error(error_msg)
|
||||
self.errors.append(f"Restic forget/prune: {error_msg}")
|
||||
return False
|
||||
|
||||
logger.info("Restic forget/prune completed successfully")
|
||||
|
||||
# Final check
|
||||
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
error_msg = f"Final restic check failed: {result.stderr}"
|
||||
logger.error(error_msg)
|
||||
self.errors.append(f"Final restic check: {error_msg}")
|
||||
return False
|
||||
|
||||
logger.info("Final restic check completed successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Restic backup process failed: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
self.errors.append(f"Restic: {error_msg}")
|
||||
return False
|
||||
|
||||
def send_telegram_notification(self, success: bool) -> None:
|
||||
"""Send notification to Telegram"""
|
||||
try:
|
||||
if success and not self.errors:
|
||||
message = f"<b>{NOTIFICATIONS_NAME}</b>: бекап успешно завершен!"
|
||||
if self.successful_backups:
|
||||
message += (
|
||||
f"\n\nУспешные бекапы: {', '.join(self.successful_backups)}"
|
||||
)
|
||||
else:
|
||||
message = f"<b>{NOTIFICATIONS_NAME}</b>: бекап завершен с ошибками!"
|
||||
|
||||
if self.successful_backups:
|
||||
message += (
|
||||
f"\n\n✅ Успешные бекапы: {', '.join(self.successful_backups)}"
|
||||
)
|
||||
|
||||
if self.warnings:
|
||||
message += f"\n\n⚠️ Предупреждения:\n" + "\n".join(self.warnings)
|
||||
|
||||
if self.errors:
|
||||
message += f"\n\n❌ Ошибки:\n" + "\n".join(self.errors)
|
||||
|
||||
url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
|
||||
data = {"chat_id": TELEGRAM_CHAT_ID, "parse_mode": "HTML", "text": message}
|
||||
|
||||
response = requests.post(url, data=data, timeout=30)
|
||||
|
||||
if response.status_code == 200:
|
||||
logger.info("Telegram notification sent successfully")
|
||||
else:
|
||||
logger.error(
|
||||
f"Failed to send Telegram notification: {response.status_code} - {response.text}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send Telegram notification: {str(e)}")
|
||||
|
||||
def run_backup_process(self) -> bool:
|
||||
"""Main backup process"""
|
||||
logger.info("Starting backup process")
|
||||
|
||||
# Get all home directories
|
||||
home_dirs = self.get_home_directories()
|
||||
logger.info(f"Found {len(home_dirs)} home directories")
|
||||
|
||||
# Process each user's backup
|
||||
for home_dir, username in home_dirs:
|
||||
logger.info(f"Processing backup for user: {username} ({home_dir})")
|
||||
|
||||
# Find backup script
|
||||
backup_script = self.find_backup_script(home_dir)
|
||||
|
||||
if backup_script is None:
|
||||
warning_msg = (
|
||||
f"No backup script found for user {username} in {home_dir}"
|
||||
)
|
||||
logger.warning(warning_msg)
|
||||
self.warnings.append(warning_msg)
|
||||
continue
|
||||
|
||||
# Run backup script
|
||||
self.run_user_backup(backup_script, username)
|
||||
|
||||
# Get backup directories
|
||||
backup_dirs = self.get_backup_directories()
|
||||
logger.info(f"Found backup directories: {backup_dirs}")
|
||||
|
||||
# Run restic backup
|
||||
restic_success = self.run_restic_backup(backup_dirs)
|
||||
|
||||
# Determine overall success
|
||||
overall_success = restic_success and len(self.errors) == 0
|
||||
|
||||
# Send notification
|
||||
self.send_telegram_notification(overall_success)
|
||||
|
||||
logger.info("Backup process completed")
|
||||
|
||||
if self.errors:
|
||||
logger.error(f"Backup completed with {len(self.errors)} errors")
|
||||
return False
|
||||
elif self.warnings:
|
||||
logger.warning(f"Backup completed with {len(self.warnings)} warnings")
|
||||
return True
|
||||
else:
|
||||
logger.info("Backup completed successfully")
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point"""
|
||||
try:
|
||||
backup_manager = BackupManager()
|
||||
success = backup_manager.run_backup_process()
|
||||
|
||||
if success:
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Backup process interrupted by user")
|
||||
sys.exit(130)
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error in backup process: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
12
files/backups/restic-shell.sh.j2
Normal file
12
files/backups/restic-shell.sh.j2
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
export RESTIC_REPOSITORY={{ restic_repository }}
|
||||
export RESTIC_PASSWORD={{ restic_password }}
|
||||
export AWS_ACCESS_KEY_ID={{ restic_s3_access_key }}
|
||||
export AWS_SECRET_ACCESS_KEY={{ restic_s3_access_secret }}
|
||||
export AWS_DEFAULT_REGION={{ restic_s3_region }}
|
||||
|
||||
restic "$@"
|
104
files/caddyproxy/Caddyfile.j2
Normal file
104
files/caddyproxy/Caddyfile.j2
Normal file
@@ -0,0 +1,104 @@
|
||||
# -------------------------------------------------------------------
|
||||
# Global options
|
||||
# -------------------------------------------------------------------
|
||||
{
|
||||
grace_period 15s
|
||||
|
||||
admin :2019
|
||||
|
||||
# Enable metrics in Prometheus format
|
||||
# https://caddyserver.com/docs/metrics
|
||||
metrics
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Applications
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to homepage_app:80
|
||||
}
|
||||
}
|
||||
|
||||
auth.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy authelia_app:9091
|
||||
}
|
||||
|
||||
status.vakhrushev.me, :29999 {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
forward_auth authelia_app:9091 {
|
||||
uri /api/authz/forward-auth
|
||||
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||
}
|
||||
|
||||
reverse_proxy netdata:19999
|
||||
}
|
||||
|
||||
git.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to gitea_app:3000
|
||||
}
|
||||
}
|
||||
|
||||
outline.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to outline_app:3000
|
||||
}
|
||||
}
|
||||
|
||||
gramps.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to gramps_app:5000
|
||||
}
|
||||
}
|
||||
|
||||
miniflux.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to miniflux_app:8080
|
||||
}
|
||||
}
|
||||
|
||||
wakapi.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to wakapi_app:3000
|
||||
}
|
||||
}
|
||||
|
||||
rssbridge.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
forward_auth authelia_app:9091 {
|
||||
uri /api/authz/forward-auth
|
||||
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||
}
|
||||
|
||||
reverse_proxy rssbridge_app:80
|
||||
}
|
||||
|
||||
dozzle.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
forward_auth authelia_app:9091 {
|
||||
uri /api/authz/forward-auth
|
||||
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name Remote-Filter
|
||||
}
|
||||
|
||||
reverse_proxy dozzle_app:8080
|
||||
}
|
||||
}
|
22
files/caddyproxy/docker-compose.yml.j2
Normal file
22
files/caddyproxy/docker-compose.yml.j2
Normal file
@@ -0,0 +1,22 @@
|
||||
services:
|
||||
|
||||
{{ service_name }}:
|
||||
image: caddy:2.10.2
|
||||
restart: unless-stopped
|
||||
container_name: {{ service_name }}
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "443:443/udp"
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
volumes:
|
||||
- {{ caddy_file_dir }}:/etc/caddy
|
||||
- {{ data_dir }}:/data
|
||||
- {{ config_dir }}:/config
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
@@ -1,25 +0,0 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
36373937313831396330393762313931643536363765353936333166376465343033376564613538
|
||||
3235356131646564393664376535646561323435363330660a353632613334633461383562306662
|
||||
37373439373636383834383464316337656531626663393830323332613136323438313762656435
|
||||
6338353136306338640a636539363766663030356432663361636438386538323238373235663766
|
||||
37393035356137653763373364623836346439663062313061346537353634306138376231633635
|
||||
30363465663836373830366231636265663837646137313764316364623637623333346636363934
|
||||
33666164343832653536303262663635616632663561633739636561333964653862313131613232
|
||||
39316239376566633964633064393532613935306161666666323337343130393861306532623666
|
||||
39653463323532333932646262663862313961393430306663643866623865346666313731366331
|
||||
32663262636132663238313630373937663936326532643730613161376565653263633935393363
|
||||
63373163346566363639396432653132646334643031323532613238666531363630353266303139
|
||||
31613138303131343364343438663762343936393165356235646239343039396637643666653065
|
||||
31363163623863613533663366303664623134396134393765636435633464373731653563646537
|
||||
39373766626338646564356463623531373337303861383862613966323132656639326533356533
|
||||
38346263326361656563386333663531663232623436653866383865393964353363353563653532
|
||||
65343130383262386262393634636338313732623565666531303636303433333638323230346565
|
||||
61633837373531343530383238396162373632623135333263323234623833383731336463333063
|
||||
62656533636237303962653238653934346430366533636436646264306461323639666665623839
|
||||
32643637623630613863323335666138303538313236343932386461346433656432626433663365
|
||||
38376666623839393630343637386336623334623064383131316331333564363934636662633630
|
||||
31363337393339643738306363306538373133626564613765643138666237303330613036666537
|
||||
61363838353736613531613436313730313936363564303464346661376137303133633062613932
|
||||
36383631303739306264386663333338666235346339623338333663386663303439363362376239
|
||||
35626136646634363430
|
23
files/dozzle/docker-compose.yml.j2
Normal file
23
files/dozzle/docker-compose.yml.j2
Normal file
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
|
||||
dozzle_app:
|
||||
image: amir20/dozzle:v8.13.12
|
||||
container_name: dozzle_app
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
environment:
|
||||
DOZZLE_HOSTNAME: vakhrushev.me
|
||||
DOZZLE_AUTH_PROVIDER: forward-proxy
|
||||
healthcheck:
|
||||
test: ["CMD", "/dozzle", "healthcheck"]
|
||||
interval: 3s
|
||||
timeout: 30s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
21
files/gitea/backup.sh.j2
Normal file
21
files/gitea/backup.sh.j2
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
echo "Gitea: backup data with gitea dump"
|
||||
|
||||
(cd "{{ base_dir }}" && \
|
||||
docker compose exec \
|
||||
-u "{{ user_create_result.uid }}:{{ user_create_result.group }}" \
|
||||
-w /backups gitea_app \
|
||||
gitea dump -c /data/gitea/conf/app.ini \
|
||||
)
|
||||
|
||||
|
||||
echo "Gitea: remove old backups"
|
||||
|
||||
keep-files.py "{{ backups_dir }}" --keep 3
|
||||
|
||||
|
||||
echo "Gitea: done."
|
33
files/gitea/docker-compose.yml.j2
Normal file
33
files/gitea/docker-compose.yml.j2
Normal file
@@ -0,0 +1,33 @@
|
||||
services:
|
||||
|
||||
gitea_app:
|
||||
image: gitea/gitea:1.24.5
|
||||
restart: unless-stopped
|
||||
container_name: gitea_app
|
||||
ports:
|
||||
- "127.0.0.1:{{ gitea_port }}:3000"
|
||||
- "2222:22"
|
||||
volumes:
|
||||
- {{ data_dir }}:/data
|
||||
- {{ backups_dir }}:/backups
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
environment:
|
||||
- "USER_UID={{ user_create_result.uid }}"
|
||||
- "USER_GID={{ user_create_result.group }}"
|
||||
- "GITEA__server__SSH_PORT=2222"
|
||||
|
||||
# Mailer
|
||||
- "GITEA__mailer__ENABLED=true"
|
||||
- "GITEA__mailer__PROTOCOL=smtp+starttls"
|
||||
- "GITEA__mailer__SMTP_ADDR={{ postbox_host }}"
|
||||
- "GITEA__mailer__SMTP_PORT={{ postbox_port }}"
|
||||
- "GITEA__mailer__USER={{ postbox_user }}"
|
||||
- "GITEA__mailer__PASSWD={{ postbox_pass }}"
|
||||
- "GITEA__mailer__FROM=gitea@vakhrushev.me"
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
10
files/gramps/backup.sh.j2
Normal file
10
files/gramps/backup.sh.j2
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
echo "Gramps: backup data with gobackups"
|
||||
|
||||
(cd "{{ base_dir }}" && gobackup perform --config "{{ gobackup_config }}")
|
||||
|
||||
echo "Gramps: done."
|
72
files/gramps/docker-compose.yml.j2
Normal file
72
files/gramps/docker-compose.yml.j2
Normal file
@@ -0,0 +1,72 @@
|
||||
# See versions: https://github.com/gramps-project/gramps-web/pkgs/container/grampsweb
|
||||
|
||||
services:
|
||||
|
||||
gramps_app: &gramps_app
|
||||
image: ghcr.io/gramps-project/grampsweb:25.8.0
|
||||
container_name: gramps_app
|
||||
depends_on:
|
||||
- gramps_redis
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- "gramps_network"
|
||||
- "web_proxy_network"
|
||||
volumes:
|
||||
- "{{ (data_dir, 'gramps_db') | path_join }}:/root/.gramps/grampsdb" # persist Gramps database
|
||||
- "{{ (data_dir, 'gramps_users') | path_join }}:/app/users" # persist user database
|
||||
- "{{ (data_dir, 'gramps_index') | path_join }}:/app/indexdir" # persist search index
|
||||
- "{{ (data_dir, 'gramps_thumb_cache') | path_join }}:/app/thumbnail_cache" # persist thumbnails
|
||||
- "{{ (data_dir, 'gramps_cache') | path_join }}:/app/cache" # persist export and report caches
|
||||
- "{{ (data_dir, 'gramps_secret') | path_join }}:/app/secret" # persist flask secret
|
||||
- "{{ (data_dir, 'gramps_media') | path_join }}:/app/media" # persist media files
|
||||
environment:
|
||||
GRAMPSWEB_TREE: "Gramps" # will create a new tree if not exists
|
||||
GRAMPSWEB_SECRET_KEY: "{{ gramps_secret_key }}"
|
||||
GRAMPSWEB_BASE_URL: "https://gramps.vakhrushev.me"
|
||||
GRAMPSWEB_REGISTRATION_DISABLED: "true"
|
||||
GRAMPSWEB_CELERY_CONFIG__broker_url: "redis://gramps_redis:6379/0"
|
||||
GRAMPSWEB_CELERY_CONFIG__result_backend: "redis://gramps_redis:6379/0"
|
||||
GRAMPSWEB_RATELIMIT_STORAGE_URI: "redis://gramps_redis:6379/1"
|
||||
GUNICORN_NUM_WORKERS: 2
|
||||
|
||||
# Email options
|
||||
GRAMPSWEB_EMAIL_HOST: "{{ postbox_host }}"
|
||||
GRAMPSWEB_EMAIL_PORT: "{{ postbox_port }}"
|
||||
GRAMPSWEB_EMAIL_HOST_USER: "{{ postbox_user }}"
|
||||
GRAMPSWEB_EMAIL_HOST_PASSWORD: "{{ postbox_pass }}"
|
||||
GRAMPSWEB_EMAIL_USE_TLS: "false"
|
||||
GRAMPSWEB_DEFAULT_FROM_EMAIL: "gramps@vakhrushev.me"
|
||||
|
||||
# media storage at s3
|
||||
GRAMPSWEB_MEDIA_BASE_DIR: "s3://av-gramps-media-storage"
|
||||
AWS_ENDPOINT_URL: "{{ gramps_s3_endpoint }}"
|
||||
AWS_ACCESS_KEY_ID: "{{ gramps_s3_access_key_id }}"
|
||||
AWS_SECRET_ACCESS_KEY: "{{ gramps_s3_secret_access_key }}"
|
||||
AWS_DEFAULT_REGION: "{{ gramps_s3_region }}"
|
||||
|
||||
gramps_celery:
|
||||
<<: *gramps_app # YAML merge key copying the entire grampsweb service config
|
||||
container_name: gramps_celery
|
||||
depends_on:
|
||||
- gramps_redis
|
||||
restart: unless-stopped
|
||||
ports: []
|
||||
networks:
|
||||
- "gramps_network"
|
||||
command: celery -A gramps_webapi.celery worker --loglevel=INFO --concurrency=2
|
||||
|
||||
gramps_redis:
|
||||
image: valkey/valkey:8.1.1-alpine
|
||||
container_name: gramps_redis
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- "gramps_network"
|
||||
- "monitoring_network"
|
||||
|
||||
networks:
|
||||
gramps_network:
|
||||
driver: bridge
|
||||
web_proxy_network:
|
||||
external: true
|
||||
monitoring_network:
|
||||
external: true
|
32
files/gramps/gobackup.yml.j2
Normal file
32
files/gramps/gobackup.yml.j2
Normal file
@@ -0,0 +1,32 @@
|
||||
# https://gobackup.github.io/configuration
|
||||
|
||||
models:
|
||||
|
||||
gramps:
|
||||
compress_with:
|
||||
type: 'tgz'
|
||||
storages:
|
||||
local:
|
||||
type: 'local'
|
||||
path: '{{ backups_dir }}'
|
||||
keep: 3
|
||||
databases:
|
||||
users:
|
||||
type: sqlite
|
||||
path: "{{ (data_dir, 'gramps_users/users.sqlite') | path_join }}"
|
||||
search_index:
|
||||
type: sqlite
|
||||
path: "{{ (data_dir, 'gramps_index/search_index.db') | path_join }}"
|
||||
sqlite:
|
||||
type: sqlite
|
||||
path: "{{ (data_dir, 'gramps_db/59a0f3d6-1c3d-4410-8c1d-1c9c6689659f/sqlite.db') | path_join }}"
|
||||
undo:
|
||||
type: sqlite
|
||||
path: "{{ (data_dir, 'gramps_db/59a0f3d6-1c3d-4410-8c1d-1c9c6689659f/undo.db') | path_join }}"
|
||||
archive:
|
||||
includes:
|
||||
- "{{ data_dir }}"
|
||||
excludes:
|
||||
- "{{ (data_dir, 'gramps_cache') | path_join }}"
|
||||
- "{{ (data_dir, 'gramps_thumb_cache') | path_join }}"
|
||||
- "{{ (data_dir, 'gramps_tmp') | path_join }}"
|
14
files/homepage/docker-compose.yml.j2
Normal file
14
files/homepage/docker-compose.yml.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
homepage_app:
|
||||
image: "{{ registry_homepage_web_image }}"
|
||||
container_name: homepage_app
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:{{ homepage_port }}:80"
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
||||
|
48
files/keep-files.py
Normal file
48
files/keep-files.py
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import argparse
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Retain specified number of files in a directory sorted by name, delete others."
|
||||
)
|
||||
parser.add_argument("directory", type=str, help="Path to target directory")
|
||||
parser.add_argument(
|
||||
"--keep", type=int, default=2, help="Number of files to retain (default: 2)"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validate arguments
|
||||
if args.keep < 0:
|
||||
parser.error("--keep value cannot be negative")
|
||||
|
||||
if not os.path.isdir(args.directory):
|
||||
parser.error(f"Directory not found: {args.directory}")
|
||||
|
||||
# Get list of files (exclude subdirectories)
|
||||
files = []
|
||||
with os.scandir(args.directory) as entries:
|
||||
for entry in entries:
|
||||
if entry.is_file():
|
||||
files.append(entry.name)
|
||||
|
||||
# Sort files alphabetically
|
||||
sorted_files = sorted(files)
|
||||
|
||||
# Identify files to delete
|
||||
to_delete = sorted_files[:-args.keep] if args.keep > 0 else sorted_files.copy()
|
||||
|
||||
# Delete files and print results
|
||||
for filename in to_delete:
|
||||
filepath = os.path.join(args.directory, filename)
|
||||
try:
|
||||
os.remove(filepath)
|
||||
print(f"Deleted: {filename}")
|
||||
except Exception as e:
|
||||
print(f"Error deleting {filename}: {str(e)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
25
files/miniflux/backup.sh.j2
Normal file
25
files/miniflux/backup.sh.j2
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="miniflux_postgres_${TIMESTAMP}.sql.gz"
|
||||
|
||||
echo "miniflux: backing up postgresql database"
|
||||
|
||||
docker compose --file "{{ base_dir }}/docker-compose.yml" exec \
|
||||
miniflux_postgres \
|
||||
pg_dump \
|
||||
-U "{{ miniflux_postgres_user }}" \
|
||||
"{{ miniflux_postgres_database }}" \
|
||||
| gzip > "{{ postgres_backups_dir }}/${BACKUP_FILE}"
|
||||
|
||||
echo "miniflux: PostgreSQL backup saved to {{ postgres_backups_dir }}/${BACKUP_FILE}"
|
||||
|
||||
echo "miniflux: removing old backups"
|
||||
|
||||
# Keep only the 3 most recent backups
|
||||
keep-files.py "{{ postgres_backups_dir }}" --keep 3
|
||||
|
||||
echo "miniflux: backup completed successfully."
|
63
files/miniflux/docker-compose.yml.j2
Normal file
63
files/miniflux/docker-compose.yml.j2
Normal file
@@ -0,0 +1,63 @@
|
||||
# See sample https://miniflux.app/docs/docker.html#docker-compose
|
||||
# See env https://miniflux.app/docs/configuration.html
|
||||
|
||||
services:
|
||||
miniflux_app:
|
||||
image: miniflux/miniflux:2.2.10
|
||||
container_name: miniflux_app
|
||||
user: "{{ user_create_result.uid }}:{{ user_create_result.group }}"
|
||||
depends_on:
|
||||
miniflux_postgres:
|
||||
condition: service_healthy
|
||||
restart: 'unless-stopped'
|
||||
networks:
|
||||
- "miniflux_network"
|
||||
- "web_proxy_network"
|
||||
volumes:
|
||||
- "{{ secrets_dir }}:/secrets:ro"
|
||||
environment:
|
||||
- DATABASE_URL_FILE=/secrets/miniflux_database_url
|
||||
- RUN_MIGRATIONS=1
|
||||
- CREATE_ADMIN=1
|
||||
- ADMIN_USERNAME_FILE=/secrets/miniflux_admin_user
|
||||
- ADMIN_PASSWORD_FILE=/secrets/miniflux_admin_password
|
||||
- BASE_URL=https://miniflux.vakhrushev.me
|
||||
- DISABLE_LOCAL_AUTH=1
|
||||
- OAUTH2_OIDC_DISCOVERY_ENDPOINT=https://auth.vakhrushev.me
|
||||
- OAUTH2_CLIENT_ID_FILE=/secrets/miniflux_oidc_client_id
|
||||
- OAUTH2_CLIENT_SECRET_FILE=/secrets/miniflux_oidc_client_secret
|
||||
- OAUTH2_OIDC_PROVIDER_NAME=Authelia
|
||||
- OAUTH2_PROVIDER=oidc
|
||||
- OAUTH2_REDIRECT_URL=https://miniflux.vakhrushev.me/oauth2/oidc/callback
|
||||
- OAUTH2_USER_CREATION=1
|
||||
- METRICS_COLLECTOR=1
|
||||
- METRICS_ALLOWED_NETWORKS=0.0.0.0/0
|
||||
|
||||
miniflux_postgres:
|
||||
image: postgres:16.3-bookworm
|
||||
container_name: miniflux_postgres
|
||||
user: "{{ user_create_result.uid }}:{{ user_create_result.group }}"
|
||||
restart: 'unless-stopped'
|
||||
environment:
|
||||
- POSTGRES_USER={{ miniflux_postgres_user }}
|
||||
- POSTGRES_PASSWORD_FILE=/secrets/miniflux_postgres_password
|
||||
- POSTGRES_DB={{ miniflux_postgres_database }}
|
||||
networks:
|
||||
- "miniflux_network"
|
||||
- "monitoring_network"
|
||||
volumes:
|
||||
- "/etc/passwd:/etc/passwd:ro"
|
||||
- "{{ secrets_dir }}:/secrets:ro"
|
||||
- "{{ postgres_data_dir }}:/var/lib/postgresql/data"
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "--username={{ miniflux_postgres_user }}", "--dbname={{ miniflux_postgres_database }}"]
|
||||
interval: 10s
|
||||
start_period: 30s
|
||||
|
||||
networks:
|
||||
miniflux_network:
|
||||
driver: bridge
|
||||
web_proxy_network:
|
||||
external: true
|
||||
monitoring_network:
|
||||
external: true
|
40
files/netdata/docker-compose.yml.j2
Normal file
40
files/netdata/docker-compose.yml.j2
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
services:
|
||||
|
||||
netdata:
|
||||
image: netdata/netdata:v2.6.3
|
||||
container_name: netdata
|
||||
restart: unless-stopped
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
- SYS_ADMIN
|
||||
security_opt:
|
||||
- apparmor:unconfined
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
- "monitoring_network"
|
||||
volumes:
|
||||
- "{{ config_dir }}:/etc/netdata"
|
||||
- "{{ (data_dir, 'lib') | path_join }}:/var/lib/netdata"
|
||||
- "{{ (data_dir, 'cache') | path_join }}:/var/cache/netdata"
|
||||
# Netdata system volumes
|
||||
- "/:/host/root:ro,rslave"
|
||||
- "/etc/group:/host/etc/group:ro"
|
||||
- "/etc/localtime:/etc/localtime:ro"
|
||||
- "/etc/os-release:/host/etc/os-release:ro"
|
||||
- "/etc/passwd:/host/etc/passwd:ro"
|
||||
- "/proc:/host/proc:ro"
|
||||
- "/run/dbus:/run/dbus:ro"
|
||||
- "/sys:/host/sys:ro"
|
||||
- "/var/log:/host/var/log:ro"
|
||||
- "/var/run:/host/var/run:ro"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
environment:
|
||||
PGID: "{{ netdata_docker_group_output.stdout | default(999) }}"
|
||||
NETDATA_EXTRA_DEB_PACKAGES: "fail2ban"
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
||||
monitoring_network:
|
||||
external: true
|
3
files/netdata/go.d/fail2ban.conf
Normal file
3
files/netdata/go.d/fail2ban.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
jobs:
|
||||
- name: fail2ban
|
||||
update_every: 15 # Collect Fail2Ban jails statistics every 15 seconds
|
9
files/netdata/go.d/postgres.conf
Normal file
9
files/netdata/go.d/postgres.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
update_every: 15
|
||||
|
||||
jobs:
|
||||
|
||||
- name: outline_db
|
||||
dsn: 'postgresql://netdata:{{ netdata_postgres_password }}@outline_postgres:5432/outline'
|
||||
|
||||
- name: miniflux_db
|
||||
dsn: 'postgresql://netdata:{{ netdata_postgres_password }}@miniflux_postgres:5432/miniflux'
|
24
files/netdata/go.d/prometheus.conf
Normal file
24
files/netdata/go.d/prometheus.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
update_every: 15
|
||||
|
||||
jobs:
|
||||
|
||||
- name: caddyproxy
|
||||
url: http://caddyproxy:2019/metrics
|
||||
selector:
|
||||
allow:
|
||||
- "caddy_http_*"
|
||||
|
||||
- name: authelia
|
||||
url: http://authelia_app:9959/metrics
|
||||
selector:
|
||||
allow:
|
||||
- "authelia_*"
|
||||
|
||||
- name: miniflux
|
||||
url: http://miniflux_app:8080/metrics
|
||||
selector:
|
||||
allow:
|
||||
- "miniflux_*"
|
||||
|
||||
- name: transcriber
|
||||
url: http://transcriber_app:8080/metrics
|
687
files/netdata/netdata.conf.j2
Normal file
687
files/netdata/netdata.conf.j2
Normal file
@@ -0,0 +1,687 @@
|
||||
# netdata configuration
|
||||
#
|
||||
# You can download the latest version of this file, using:
|
||||
#
|
||||
# wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf
|
||||
# or
|
||||
# curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf
|
||||
#
|
||||
# You can uncomment and change any of the options below.
|
||||
# The value shown in the commented settings, is the default value.
|
||||
#
|
||||
|
||||
# global netdata configuration
|
||||
|
||||
[global]
|
||||
# run as user = netdata
|
||||
# host access prefix = /host
|
||||
# pthread stack size = 8MiB
|
||||
# cpu cores = 2
|
||||
# libuv worker threads = 16
|
||||
# profile = standalone
|
||||
hostname = {{ host_name }}
|
||||
# glibc malloc arena max for plugins = 1
|
||||
# glibc malloc arena max for netdata = 1
|
||||
# crash reports = all
|
||||
# timezone = Etc/UTC
|
||||
# OOM score = 0
|
||||
# process scheduling policy = keep
|
||||
# is ephemeral node = no
|
||||
# has unstable connection = no
|
||||
|
||||
[db]
|
||||
# enable replication = yes
|
||||
# replication period = 1d
|
||||
# replication step = 1h
|
||||
# replication threads = 1
|
||||
# replication prefetch = 10
|
||||
# update every = 1s
|
||||
# db = dbengine
|
||||
# memory deduplication (ksm) = auto
|
||||
# cleanup orphan hosts after = 1h
|
||||
# cleanup ephemeral hosts after = off
|
||||
# cleanup obsolete charts after = 1h
|
||||
# gap when lost iterations above = 1
|
||||
# dbengine page type = gorilla
|
||||
# dbengine page cache size = 32MiB
|
||||
# dbengine extent cache size = off
|
||||
# dbengine enable journal integrity check = no
|
||||
# dbengine use all ram for caches = no
|
||||
# dbengine out of memory protection = 391.99MiB
|
||||
# dbengine use direct io = yes
|
||||
# dbengine journal v2 unmount time = 2m
|
||||
# dbengine pages per extent = 109
|
||||
# storage tiers = 3
|
||||
# dbengine tier backfill = new
|
||||
# dbengine tier 1 update every iterations = 60
|
||||
# dbengine tier 2 update every iterations = 60
|
||||
# dbengine tier 0 retention size = 1024MiB
|
||||
# dbengine tier 0 retention time = 14d
|
||||
# dbengine tier 1 retention size = 1024MiB
|
||||
# dbengine tier 1 retention time = 3mo
|
||||
# dbengine tier 2 retention size = 1024MiB
|
||||
# dbengine tier 2 retention time = 2y
|
||||
# extreme cardinality protection = yes
|
||||
# extreme cardinality keep instances = 1000
|
||||
# extreme cardinality min ephemerality = 50
|
||||
|
||||
[directories]
|
||||
# config = /etc/netdata
|
||||
# stock config = /usr/lib/netdata/conf.d
|
||||
# log = /var/log/netdata
|
||||
# web = /usr/share/netdata/web
|
||||
# cache = /var/cache/netdata
|
||||
# lib = /var/lib/netdata
|
||||
# cloud.d = /var/lib/netdata/cloud.d
|
||||
# plugins = "/usr/libexec/netdata/plugins.d" "/etc/netdata/custom-plugins.d"
|
||||
# registry = /var/lib/netdata/registry
|
||||
# home = /etc/netdata
|
||||
# stock health config = /usr/lib/netdata/conf.d/health.d
|
||||
# health config = /etc/netdata/health.d
|
||||
|
||||
[logs]
|
||||
# facility = daemon
|
||||
# logs flood protection period = 1m
|
||||
# logs to trigger flood protection = 1000
|
||||
# level = info
|
||||
# debug = /var/log/netdata/debug.log
|
||||
# daemon = /var/log/netdata/daemon.log
|
||||
# collector = /var/log/netdata/collector.log
|
||||
# access = /var/log/netdata/access.log
|
||||
# health = /var/log/netdata/health.log
|
||||
# debug flags = 0x0000000000000000
|
||||
|
||||
[environment variables]
|
||||
# PATH = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
|
||||
# PYTHONPATH =
|
||||
# TZ = :/etc/localtime
|
||||
|
||||
[host labels]
|
||||
# name = value
|
||||
|
||||
[cloud]
|
||||
# conversation log = no
|
||||
# scope = full
|
||||
# query threads = 6
|
||||
# proxy = env
|
||||
|
||||
[ml]
|
||||
# enabled = auto
|
||||
# maximum num samples to train = 21600
|
||||
# minimum num samples to train = 900
|
||||
# train every = 3h
|
||||
# number of models per dimension = 18
|
||||
# delete models older than = 7d
|
||||
# num samples to diff = 1
|
||||
# num samples to smooth = 3
|
||||
# num samples to lag = 5
|
||||
# random sampling ratio = 0.20000
|
||||
# maximum number of k-means iterations = 1000
|
||||
# dimension anomaly score threshold = 0.99000
|
||||
# host anomaly rate threshold = 1.00000
|
||||
# anomaly detection grouping method = average
|
||||
# anomaly detection grouping duration = 5m
|
||||
# num training threads = 1
|
||||
# flush models batch size = 256
|
||||
# dimension anomaly rate suppression window = 15m
|
||||
# dimension anomaly rate suppression threshold = 450
|
||||
# enable statistics charts = yes
|
||||
# hosts to skip from training = !*
|
||||
# charts to skip from training = netdata.*
|
||||
# stream anomaly detection charts = yes
|
||||
|
||||
[health]
|
||||
# silencers file = /var/lib/netdata/health.silencers.json
|
||||
# enabled = yes
|
||||
# enable stock health configuration = yes
|
||||
# use summary for notifications = yes
|
||||
# default repeat warning = off
|
||||
# default repeat critical = off
|
||||
# in memory max health log entries = 1000
|
||||
# health log retention = 5d
|
||||
# script to execute on alarm = /usr/libexec/netdata/plugins.d/alarm-notify.sh
|
||||
# enabled alarms = *
|
||||
# run at least every = 10s
|
||||
# postpone alarms during hibernation for = 1m
|
||||
|
||||
[web]
|
||||
#| >>> [web].default port <<<
|
||||
#| migrated from: [global].default port
|
||||
# default port = 19999
|
||||
|
||||
# ssl key = /etc/netdata/ssl/key.pem
|
||||
# ssl certificate = /etc/netdata/ssl/cert.pem
|
||||
# tls version = 1.3
|
||||
# tls ciphers = none
|
||||
# ses max tg_des_window = 15
|
||||
# des max tg_des_window = 15
|
||||
# mode = static-threaded
|
||||
# listen backlog = 4096
|
||||
# bind to = *
|
||||
# bearer token protection = no
|
||||
# disconnect idle clients after = 1m
|
||||
# timeout for first request = 1m
|
||||
# accept a streaming request every = off
|
||||
# respect do not track policy = no
|
||||
# x-frame-options response header =
|
||||
# allow connections from = localhost *
|
||||
# allow connections by dns = heuristic
|
||||
# allow dashboard from = localhost *
|
||||
# allow dashboard by dns = heuristic
|
||||
# allow badges from = *
|
||||
# allow badges by dns = heuristic
|
||||
# allow streaming from = *
|
||||
# allow streaming by dns = heuristic
|
||||
# allow netdata.conf from = localhost fd* 10.* 192.168.* 172.16.* 172.17.* 172.18.* 172.19.* 172.20.* 172.21.* 172.22.* 172.23.* 172.24.* 172.25.* 172.26.* 172.27.* 172.28.* 172.29.* 172.30.* 172.31.* UNKNOWN
|
||||
# allow netdata.conf by dns = no
|
||||
# allow management from = localhost
|
||||
# allow management by dns = heuristic
|
||||
# enable gzip compression = yes
|
||||
# gzip compression strategy = default
|
||||
# gzip compression level = 3
|
||||
# ssl skip certificate verification = no
|
||||
# web server threads = 6
|
||||
# web server max sockets = 262144
|
||||
|
||||
[registry]
|
||||
# enabled = no
|
||||
# registry db file = /var/lib/netdata/registry/registry.db
|
||||
# registry log file = /var/lib/netdata/registry/registry-log.db
|
||||
# registry save db every new entries = 1000000
|
||||
# registry expire idle persons = 1y
|
||||
# registry domain =
|
||||
# registry to announce = https://registry.my-netdata.io
|
||||
# registry hostname = 7171b7f9fc69
|
||||
# verify browser cookies support = yes
|
||||
# enable cookies SameSite and Secure = yes
|
||||
# max URL length = 1024
|
||||
# max URL name length = 50
|
||||
# netdata management api key file = /var/lib/netdata/netdata.api.key
|
||||
# allow from = *
|
||||
# allow by dns = heuristic
|
||||
|
||||
[pulse]
|
||||
# extended = no
|
||||
# update every = 1s
|
||||
|
||||
[plugins]
|
||||
# idlejitter = yes
|
||||
# netdata pulse = yes
|
||||
# profile = no
|
||||
# tc = yes
|
||||
# diskspace = yes
|
||||
# proc = yes
|
||||
# cgroups = yes
|
||||
# timex = yes
|
||||
# statsd = yes
|
||||
# enable running new plugins = yes
|
||||
# check for new plugins every = 1m
|
||||
# slabinfo = no
|
||||
# freeipmi = no
|
||||
# python.d = yes
|
||||
# go.d = yes
|
||||
# apps = yes
|
||||
# systemd-journal = yes
|
||||
# network-viewer = yes
|
||||
# charts.d = yes
|
||||
# debugfs = yes
|
||||
# perf = yes
|
||||
# ioping = yes
|
||||
|
||||
[statsd]
|
||||
# update every (flushInterval) = 1s
|
||||
# udp messages to process at once = 10
|
||||
# create private charts for metrics matching = *
|
||||
# max private charts hard limit = 1000
|
||||
# set charts as obsolete after = off
|
||||
# decimal detail = 1000
|
||||
# disconnect idle tcp clients after = 10m
|
||||
# private charts hidden = no
|
||||
# histograms and timers percentile (percentThreshold) = 95.00000
|
||||
# dictionaries max unique dimensions = 200
|
||||
# add dimension for number of events received = no
|
||||
# gaps on gauges (deleteGauges) = no
|
||||
# gaps on counters (deleteCounters) = no
|
||||
# gaps on meters (deleteMeters) = no
|
||||
# gaps on sets (deleteSets) = no
|
||||
# gaps on histograms (deleteHistograms) = no
|
||||
# gaps on timers (deleteTimers) = no
|
||||
# gaps on dictionaries (deleteDictionaries) = no
|
||||
# statsd server max TCP sockets = 262144
|
||||
# listen backlog = 4096
|
||||
# default port = 8125
|
||||
# bind to = udp:localhost tcp:localhost
|
||||
|
||||
[plugin:idlejitter]
|
||||
# loop time = 20ms
|
||||
|
||||
[plugin:timex]
|
||||
# update every = 10s
|
||||
# clock synchronization state = yes
|
||||
# time offset = yes
|
||||
|
||||
[plugin:proc]
|
||||
# /proc/net/dev = yes
|
||||
# /proc/pagetypeinfo = no
|
||||
# /proc/stat = yes
|
||||
# /proc/uptime = yes
|
||||
# /proc/loadavg = yes
|
||||
# /proc/sys/fs/file-nr = yes
|
||||
# /proc/sys/kernel/random/entropy_avail = yes
|
||||
# /run/reboot_required = yes
|
||||
# /proc/pressure = yes
|
||||
# /proc/interrupts = yes
|
||||
# /proc/softirqs = yes
|
||||
# /proc/vmstat = yes
|
||||
# /proc/meminfo = yes
|
||||
# /sys/kernel/mm/ksm = yes
|
||||
# /sys/block/zram = yes
|
||||
# /sys/devices/system/edac/mc = yes
|
||||
# /sys/devices/pci/aer = yes
|
||||
# /sys/devices/system/node = yes
|
||||
# /proc/net/wireless = yes
|
||||
# /proc/net/sockstat = yes
|
||||
# /proc/net/sockstat6 = yes
|
||||
# /proc/net/netstat = yes
|
||||
# /proc/net/sctp/snmp = yes
|
||||
# /proc/net/softnet_stat = yes
|
||||
# /proc/net/ip_vs/stats = yes
|
||||
# /sys/class/infiniband = yes
|
||||
# /proc/net/stat/conntrack = yes
|
||||
# /proc/net/stat/synproxy = yes
|
||||
# /proc/diskstats = yes
|
||||
# /proc/mdstat = yes
|
||||
# /proc/net/rpc/nfsd = yes
|
||||
# /proc/net/rpc/nfs = yes
|
||||
# /proc/spl/kstat/zfs/arcstats = yes
|
||||
# /sys/fs/btrfs = yes
|
||||
# ipc = yes
|
||||
# /sys/class/power_supply = yes
|
||||
# /sys/class/drm = yes
|
||||
|
||||
[plugin:cgroups]
|
||||
# update every = 1s
|
||||
# check for new cgroups every = 10s
|
||||
# use unified cgroups = auto
|
||||
# max cgroups to allow = 1000
|
||||
# max cgroups depth to monitor = 0
|
||||
# enable by default cgroups matching = !*/init.scope !/system.slice/run-*.scope *user.slice/docker-* !*user.slice* *.scope !/machine.slice/*/.control !/machine.slice/*/payload* !/machine.slice/*/supervisor /machine.slice/*.service */kubepods/pod*/* */kubepods/*/pod*/* */*-kubepods-pod*/* */*-kubepods-*-pod*/* !*kubepods* !*kubelet* !*/vcpu* !*/emulator !*.mount !*.partition !*.service !*.service/udev !*.socket !*.slice !*.swap !*.user !/ !/docker !*/libvirt !/lxc !/lxc/*/* !/lxc.monitor* !/lxc.pivot !/lxc.payload !*lxcfs.service/.control !/machine !/qemu !/system !/systemd !/user *
|
||||
# enable by default cgroups names matching = *
|
||||
# search for cgroups in subpaths matching = !*/init.scope !*-qemu !*.libvirt-qemu !/init.scope !/system !/systemd !/user !/lxc/*/* !/lxc.monitor !/lxc.payload/*/* !/lxc.payload.* *
|
||||
# script to get cgroup names = /usr/libexec/netdata/plugins.d/cgroup-name.sh
|
||||
# script to get cgroup network interfaces = /usr/libexec/netdata/plugins.d/cgroup-network
|
||||
# run script to rename cgroups matching = !/ !*.mount !*.socket !*.partition /machine.slice/*.service !*.service !*.slice !*.swap !*.user !init.scope !*.scope/vcpu* !*.scope/emulator *.scope *docker* *lxc* *qemu* */kubepods/pod*/* */kubepods/*/pod*/* */*-kubepods-pod*/* */*-kubepods-*-pod*/* !*kubepods* !*kubelet* *.libvirt-qemu *
|
||||
# cgroups to match as systemd services = !/system.slice/*/*.service /system.slice/*.service
|
||||
|
||||
[plugin:proc:diskspace]
|
||||
# remove charts of unmounted disks = yes
|
||||
# update every = 1s
|
||||
# check for new mount points every = 15s
|
||||
# exclude space metrics on paths = /dev /dev/shm /proc/* /sys/* /var/run/user/* /run/lock /run/user/* /snap/* /var/lib/docker/* /var/lib/containers/storage/* /run/credentials/* /run/containerd/* /rpool /rpool/*
|
||||
# exclude space metrics on filesystems = *gvfs *gluster* *s3fs *ipfs *davfs2 *httpfs *sshfs *gdfs *moosefs fusectl autofs cgroup cgroup2 hugetlbfs devtmpfs fuse.lxcfs
|
||||
# exclude inode metrics on filesystems = msdosfs msdos vfat overlayfs aufs* *unionfs
|
||||
# space usage for all disks = auto
|
||||
# inodes usage for all disks = auto
|
||||
|
||||
[plugin:tc]
|
||||
# script to run to get tc values = /usr/libexec/netdata/plugins.d/tc-qos-helper.sh
|
||||
|
||||
[plugin:python.d]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:go.d]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:apps]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:systemd-journal]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:network-viewer]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:charts.d]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:debugfs]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:perf]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:ioping]
|
||||
# update every = 1s
|
||||
# command options =
|
||||
|
||||
[plugin:proc:/proc/net/dev]
|
||||
# compressed packets for all interfaces = no
|
||||
# disable by default interfaces matching = lo fireqos* *-ifb fwpr* fwbr* fwln* ifb4*
|
||||
|
||||
[plugin:proc:/proc/stat]
|
||||
# cpu utilization = yes
|
||||
# per cpu core utilization = no
|
||||
# cpu interrupts = yes
|
||||
# context switches = yes
|
||||
# processes started = yes
|
||||
# processes running = yes
|
||||
# keep per core files open = yes
|
||||
# keep cpuidle files open = yes
|
||||
# core_throttle_count = auto
|
||||
# package_throttle_count = no
|
||||
# cpu frequency = yes
|
||||
# cpu idle states = no
|
||||
# core_throttle_count filename to monitor = /host/sys/devices/system/cpu/%s/thermal_throttle/core_throttle_count
|
||||
# package_throttle_count filename to monitor = /host/sys/devices/system/cpu/%s/thermal_throttle/package_throttle_count
|
||||
# scaling_cur_freq filename to monitor = /host/sys/devices/system/cpu/%s/cpufreq/scaling_cur_freq
|
||||
# time_in_state filename to monitor = /host/sys/devices/system/cpu/%s/cpufreq/stats/time_in_state
|
||||
# schedstat filename to monitor = /host/proc/schedstat
|
||||
# cpuidle name filename to monitor = /host/sys/devices/system/cpu/cpu%zu/cpuidle/state%zu/name
|
||||
# cpuidle time filename to monitor = /host/sys/devices/system/cpu/cpu%zu/cpuidle/state%zu/time
|
||||
# filename to monitor = /host/proc/stat
|
||||
|
||||
[plugin:proc:/proc/uptime]
|
||||
# filename to monitor = /host/proc/uptime
|
||||
|
||||
[plugin:proc:/proc/loadavg]
|
||||
# filename to monitor = /host/proc/loadavg
|
||||
# enable load average = yes
|
||||
# enable total processes = yes
|
||||
|
||||
[plugin:proc:/proc/sys/fs/file-nr]
|
||||
# filename to monitor = /host/proc/sys/fs/file-nr
|
||||
|
||||
[plugin:proc:/proc/sys/kernel/random/entropy_avail]
|
||||
# filename to monitor = /host/proc/sys/kernel/random/entropy_avail
|
||||
|
||||
[plugin:proc:/proc/pressure]
|
||||
# base path of pressure metrics = /proc/pressure
|
||||
# enable cpu some pressure = yes
|
||||
# enable cpu full pressure = no
|
||||
# enable memory some pressure = yes
|
||||
# enable memory full pressure = yes
|
||||
# enable io some pressure = yes
|
||||
# enable io full pressure = yes
|
||||
# enable irq some pressure = no
|
||||
# enable irq full pressure = yes
|
||||
|
||||
[plugin:proc:/proc/interrupts]
|
||||
# interrupts per core = no
|
||||
# filename to monitor = /host/proc/interrupts
|
||||
|
||||
[plugin:proc:/proc/softirqs]
|
||||
# interrupts per core = no
|
||||
# filename to monitor = /host/proc/softirqs
|
||||
|
||||
[plugin:proc:/proc/vmstat]
|
||||
# filename to monitor = /host/proc/vmstat
|
||||
# swap i/o = auto
|
||||
# disk i/o = yes
|
||||
# memory page faults = yes
|
||||
# out of memory kills = yes
|
||||
# system-wide numa metric summary = auto
|
||||
# transparent huge pages = auto
|
||||
# zswap i/o = auto
|
||||
# memory ballooning = auto
|
||||
# kernel same memory = auto
|
||||
|
||||
[plugin:proc:/sys/devices/system/node]
|
||||
# directory to monitor = /host/sys/devices/system/node
|
||||
# enable per-node numa metrics = auto
|
||||
|
||||
[plugin:proc:/proc/meminfo]
|
||||
# system ram = yes
|
||||
# system swap = auto
|
||||
# hardware corrupted ECC = auto
|
||||
# committed memory = yes
|
||||
# writeback memory = yes
|
||||
# kernel memory = yes
|
||||
# slab memory = yes
|
||||
# hugepages = auto
|
||||
# transparent hugepages = auto
|
||||
# memory reclaiming = yes
|
||||
# high low memory = yes
|
||||
# cma memory = auto
|
||||
# direct maps = yes
|
||||
# filename to monitor = /host/proc/meminfo
|
||||
|
||||
[plugin:proc:/sys/kernel/mm/ksm]
|
||||
# /sys/kernel/mm/ksm/pages_shared = /host/sys/kernel/mm/ksm/pages_shared
|
||||
# /sys/kernel/mm/ksm/pages_sharing = /host/sys/kernel/mm/ksm/pages_sharing
|
||||
# /sys/kernel/mm/ksm/pages_unshared = /host/sys/kernel/mm/ksm/pages_unshared
|
||||
# /sys/kernel/mm/ksm/pages_volatile = /host/sys/kernel/mm/ksm/pages_volatile
|
||||
|
||||
[plugin:proc:/sys/devices/system/edac/mc]
|
||||
# directory to monitor = /host/sys/devices/system/edac/mc
|
||||
|
||||
[plugin:proc:/sys/class/pci/aer]
|
||||
# enable root ports = no
|
||||
# enable pci slots = no
|
||||
|
||||
[plugin:proc:/proc/net/wireless]
|
||||
# filename to monitor = /host/proc/net/wireless
|
||||
# status for all interfaces = auto
|
||||
# quality for all interfaces = auto
|
||||
# discarded packets for all interfaces = auto
|
||||
# missed beacon for all interface = auto
|
||||
|
||||
[plugin:proc:/proc/net/sockstat]
|
||||
# ipv4 sockets = auto
|
||||
# ipv4 TCP sockets = auto
|
||||
# ipv4 TCP memory = auto
|
||||
# ipv4 UDP sockets = auto
|
||||
# ipv4 UDP memory = auto
|
||||
# ipv4 UDPLITE sockets = auto
|
||||
# ipv4 RAW sockets = auto
|
||||
# ipv4 FRAG sockets = auto
|
||||
# ipv4 FRAG memory = auto
|
||||
# update constants every = 1m
|
||||
# filename to monitor = /host/proc/net/sockstat
|
||||
|
||||
[plugin:proc:/proc/net/sockstat6]
|
||||
# ipv6 TCP sockets = auto
|
||||
# ipv6 UDP sockets = auto
|
||||
# ipv6 UDPLITE sockets = auto
|
||||
# ipv6 RAW sockets = auto
|
||||
# ipv6 FRAG sockets = auto
|
||||
# filename to monitor = /host/proc/net/sockstat6
|
||||
|
||||
[plugin:proc:/proc/net/netstat]
|
||||
# bandwidth = auto
|
||||
# input errors = auto
|
||||
# multicast bandwidth = auto
|
||||
# broadcast bandwidth = auto
|
||||
# multicast packets = auto
|
||||
# broadcast packets = auto
|
||||
# ECN packets = auto
|
||||
# TCP reorders = auto
|
||||
# TCP SYN cookies = auto
|
||||
# TCP out-of-order queue = auto
|
||||
# TCP connection aborts = auto
|
||||
# TCP memory pressures = auto
|
||||
# TCP SYN queue = auto
|
||||
# TCP accept queue = auto
|
||||
# filename to monitor = /host/proc/net/netstat
|
||||
|
||||
[plugin:proc:/proc/net/snmp]
|
||||
# ipv4 packets = auto
|
||||
# ipv4 fragments sent = auto
|
||||
# ipv4 fragments assembly = auto
|
||||
# ipv4 errors = auto
|
||||
# ipv4 TCP connections = auto
|
||||
# ipv4 TCP packets = auto
|
||||
# ipv4 TCP errors = auto
|
||||
# ipv4 TCP opens = auto
|
||||
# ipv4 TCP handshake issues = auto
|
||||
# ipv4 UDP packets = auto
|
||||
# ipv4 UDP errors = auto
|
||||
# ipv4 ICMP packets = auto
|
||||
# ipv4 ICMP messages = auto
|
||||
# ipv4 UDPLite packets = auto
|
||||
# filename to monitor = /host/proc/net/snmp
|
||||
|
||||
[plugin:proc:/proc/net/snmp6]
|
||||
# ipv6 packets = auto
|
||||
# ipv6 fragments sent = auto
|
||||
# ipv6 fragments assembly = auto
|
||||
# ipv6 errors = auto
|
||||
# ipv6 UDP packets = auto
|
||||
# ipv6 UDP errors = auto
|
||||
# ipv6 UDPlite packets = auto
|
||||
# ipv6 UDPlite errors = auto
|
||||
# bandwidth = auto
|
||||
# multicast bandwidth = auto
|
||||
# broadcast bandwidth = auto
|
||||
# multicast packets = auto
|
||||
# icmp = auto
|
||||
# icmp redirects = auto
|
||||
# icmp errors = auto
|
||||
# icmp echos = auto
|
||||
# icmp group membership = auto
|
||||
# icmp router = auto
|
||||
# icmp neighbor = auto
|
||||
# icmp mldv2 = auto
|
||||
# icmp types = auto
|
||||
# ect = auto
|
||||
# filename to monitor = /host/proc/net/snmp6
|
||||
|
||||
[plugin:proc:/proc/net/sctp/snmp]
|
||||
# established associations = auto
|
||||
# association transitions = auto
|
||||
# fragmentation = auto
|
||||
# packets = auto
|
||||
# packet errors = auto
|
||||
# chunk types = auto
|
||||
# filename to monitor = /host/proc/net/sctp/snmp
|
||||
|
||||
[plugin:proc:/proc/net/softnet_stat]
|
||||
# softnet_stat per core = no
|
||||
# filename to monitor = /host/proc/net/softnet_stat
|
||||
|
||||
[plugin:proc:/proc/net/ip_vs_stats]
|
||||
# IPVS bandwidth = yes
|
||||
# IPVS connections = yes
|
||||
# IPVS packets = yes
|
||||
# filename to monitor = /host/proc/net/ip_vs_stats
|
||||
|
||||
[plugin:proc:/sys/class/infiniband]
|
||||
# dirname to monitor = /host/sys/class/infiniband
|
||||
# bandwidth counters = yes
|
||||
# packets counters = yes
|
||||
# errors counters = yes
|
||||
# hardware packets counters = auto
|
||||
# hardware errors counters = auto
|
||||
# monitor only active ports = auto
|
||||
# disable by default interfaces matching =
|
||||
# refresh ports state every = 30s
|
||||
|
||||
[plugin:proc:/proc/net/stat/nf_conntrack]
|
||||
# filename to monitor = /host/proc/net/stat/nf_conntrack
|
||||
# netfilter new connections = no
|
||||
# netfilter connection changes = no
|
||||
# netfilter connection expectations = no
|
||||
# netfilter connection searches = no
|
||||
# netfilter errors = no
|
||||
# netfilter connections = yes
|
||||
|
||||
[plugin:proc:/proc/sys/net/netfilter/nf_conntrack_max]
|
||||
# filename to monitor = /host/proc/sys/net/netfilter/nf_conntrack_max
|
||||
# read every seconds = 10
|
||||
|
||||
[plugin:proc:/proc/sys/net/netfilter/nf_conntrack_count]
|
||||
# filename to monitor = /host/proc/sys/net/netfilter/nf_conntrack_count
|
||||
|
||||
[plugin:proc:/proc/net/stat/synproxy]
|
||||
# SYNPROXY cookies = auto
|
||||
# SYNPROXY SYN received = auto
|
||||
# SYNPROXY connections reopened = auto
|
||||
# filename to monitor = /host/proc/net/stat/synproxy
|
||||
|
||||
[plugin:proc:/proc/diskstats]
|
||||
# enable new disks detected at runtime = yes
|
||||
# performance metrics for physical disks = auto
|
||||
# performance metrics for virtual disks = auto
|
||||
# performance metrics for partitions = no
|
||||
# bandwidth for all disks = auto
|
||||
# operations for all disks = auto
|
||||
# merged operations for all disks = auto
|
||||
# i/o time for all disks = auto
|
||||
# queued operations for all disks = auto
|
||||
# utilization percentage for all disks = auto
|
||||
# extended operations for all disks = auto
|
||||
# backlog for all disks = auto
|
||||
# bcache for all disks = auto
|
||||
# bcache priority stats update every = off
|
||||
# remove charts of removed disks = yes
|
||||
# path to get block device = /host/sys/block/%s
|
||||
# path to get block device bcache = /host/sys/block/%s/bcache
|
||||
# path to get virtual block device = /host/sys/devices/virtual/block/%s
|
||||
# path to get block device infos = /host/sys/dev/block/%lu:%lu/%s
|
||||
# path to device mapper = /host/dev/mapper
|
||||
# path to /dev/disk = /host/dev/disk
|
||||
# path to /sys/block = /host/sys/block
|
||||
# path to /dev/disk/by-label = /host/dev/disk/by-label
|
||||
# path to /dev/disk/by-id = /host/dev/disk/by-id
|
||||
# path to /dev/vx/dsk = /host/dev/vx/dsk
|
||||
# name disks by id = no
|
||||
# preferred disk ids = *
|
||||
# exclude disks = loop* ram*
|
||||
# filename to monitor = /host/proc/diskstats
|
||||
# performance metrics for disks with major 252 = yes
|
||||
|
||||
[plugin:proc:/proc/mdstat]
|
||||
# faulty devices = yes
|
||||
# nonredundant arrays availability = yes
|
||||
# mismatch count = auto
|
||||
# disk stats = yes
|
||||
# operation status = yes
|
||||
# make charts obsolete = yes
|
||||
# filename to monitor = /host/proc/mdstat
|
||||
# mismatch_cnt filename to monitor = /host/sys/block/%s/md/mismatch_cnt
|
||||
|
||||
[plugin:proc:/proc/net/rpc/nfsd]
|
||||
# filename to monitor = /host/proc/net/rpc/nfsd
|
||||
|
||||
[plugin:proc:/proc/net/rpc/nfs]
|
||||
# filename to monitor = /host/proc/net/rpc/nfs
|
||||
|
||||
[plugin:proc:/proc/spl/kstat/zfs/arcstats]
|
||||
# filename to monitor = /host/proc/spl/kstat/zfs/arcstats
|
||||
|
||||
[plugin:proc:/sys/fs/btrfs]
|
||||
# path to monitor = /host/sys/fs/btrfs
|
||||
# check for btrfs changes every = 1m
|
||||
# physical disks allocation = auto
|
||||
# data allocation = auto
|
||||
# metadata allocation = auto
|
||||
# system allocation = auto
|
||||
# commit stats = auto
|
||||
# error stats = auto
|
||||
|
||||
[plugin:proc:ipc]
|
||||
# message queues = yes
|
||||
# semaphore totals = yes
|
||||
# shared memory totals = yes
|
||||
# msg filename to monitor = /host/proc/sysvipc/msg
|
||||
# shm filename to monitor = /host/proc/sysvipc/shm
|
||||
# max dimensions in memory allowed = 50
|
||||
|
||||
[plugin:proc:/sys/class/power_supply]
|
||||
# battery capacity = yes
|
||||
# battery power = yes
|
||||
# battery charge = no
|
||||
# battery energy = no
|
||||
# power supply voltage = no
|
||||
# keep files open = auto
|
||||
# directory to monitor = /host/sys/class/power_supply
|
||||
|
||||
[plugin:proc:/sys/class/drm]
|
||||
# directory to monitor = /host/sys/class/drm
|
25
files/outline/backup.sh.j2
Normal file
25
files/outline/backup.sh.j2
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="outline_postgres_${TIMESTAMP}.sql.gz"
|
||||
|
||||
echo "Outline: backing up PostgreSQL database"
|
||||
|
||||
docker compose --file "{{ base_dir }}/docker-compose.yml" exec \
|
||||
outline_postgres \
|
||||
pg_dump \
|
||||
-U "{{ outline_postgres_user }}" \
|
||||
"{{ outline_postgres_database }}" \
|
||||
| gzip > "{{ postgres_backups_dir }}/${BACKUP_FILE}"
|
||||
|
||||
echo "Outline: PostgreSQL backup saved to {{ postgres_backups_dir }}/${BACKUP_FILE}"
|
||||
|
||||
echo "Outline: removing old backups"
|
||||
|
||||
# Keep only the 3 most recent backups
|
||||
keep-files.py "{{ postgres_backups_dir }}" --keep 3
|
||||
|
||||
echo "Outline: backup completed successfully."
|
84
files/outline/docker-compose.yml.j2
Normal file
84
files/outline/docker-compose.yml.j2
Normal file
@@ -0,0 +1,84 @@
|
||||
services:
|
||||
|
||||
# See sample https://github.com/outline/outline/blob/main/.env.sample
|
||||
|
||||
outline_app:
|
||||
image: outlinewiki/outline:0.87.3
|
||||
container_name: outline_app
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- outline_postgres
|
||||
- outline_redis
|
||||
ports:
|
||||
- "127.0.0.1:{{ outline_port }}:3000"
|
||||
networks:
|
||||
- "outline_network"
|
||||
- "web_proxy_network"
|
||||
environment:
|
||||
NODE_ENV: 'production'
|
||||
URL: 'https://outline.vakhrushev.me'
|
||||
FORCE_HTTPS: 'true'
|
||||
SECRET_KEY: '{{ outline_secret_key }}'
|
||||
UTILS_SECRET: '{{ outline_utils_secret }}'
|
||||
DATABASE_URL: 'postgres://{{ outline_postgres_user }}:{{ outline_postgres_password }}@outline_postgres:5432/{{ outline_postgres_database }}'
|
||||
PGSSLMODE: 'disable'
|
||||
REDIS_URL: 'redis://outline_redis:6379'
|
||||
|
||||
FILE_STORAGE: 's3'
|
||||
FILE_STORAGE_UPLOAD_MAX_SIZE: '262144000'
|
||||
AWS_ACCESS_KEY_ID: '{{ outline_s3_access_key }}'
|
||||
AWS_SECRET_ACCESS_KEY: '{{ outline_s3_secret_key }}'
|
||||
AWS_REGION: '{{ outline_s3_region }}'
|
||||
AWS_S3_ACCELERATE_URL: ''
|
||||
AWS_S3_UPLOAD_BUCKET_URL: '{{ outline_s3_url }}'
|
||||
AWS_S3_UPLOAD_BUCKET_NAME: '{{ outline_s3_bucket }}'
|
||||
AWS_S3_FORCE_PATH_STYLE: 'true'
|
||||
AWS_S3_ACL: 'private'
|
||||
|
||||
OIDC_CLIENT_ID: '{{ outline_oidc_client_id | replace("$", "$$") }}'
|
||||
OIDC_CLIENT_SECRET: '{{ outline_oidc_client_secret | replace("$", "$$") }}'
|
||||
OIDC_AUTH_URI: 'https://auth.vakhrushev.me/api/oidc/authorization'
|
||||
OIDC_TOKEN_URI: 'https://auth.vakhrushev.me/api/oidc/token'
|
||||
OIDC_USERINFO_URI: 'https://auth.vakhrushev.me/api/oidc/userinfo'
|
||||
OIDC_LOGOUT_URI: 'https://auth.vakhrushev.me/logout'
|
||||
OIDC_USERNAME_CLAIM: 'email'
|
||||
OIDC_SCOPES: 'openid profile email'
|
||||
OIDC_DISPLAY_NAME: 'Authelia'
|
||||
|
||||
SMTP_HOST: '{{ postbox_host }}'
|
||||
SMTP_PORT: '{{ postbox_port }}'
|
||||
SMTP_USERNAME: '{{ postbox_user }}'
|
||||
SMTP_PASSWORD: '{{ postbox_pass }}'
|
||||
SMTP_FROM_EMAIL: 'outline@vakhrushev.me'
|
||||
SMTP_TLS_CIPHERS: 'TLSv1.2'
|
||||
SMTP_SECURE: 'false'
|
||||
|
||||
outline_redis:
|
||||
image: valkey/valkey:8.1.1-alpine
|
||||
container_name: outline_redis
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- "outline_network"
|
||||
- "monitoring_network"
|
||||
|
||||
outline_postgres:
|
||||
image: postgres:16.3-bookworm
|
||||
container_name: outline_postgres
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- {{ postgres_data_dir }}:/var/lib/postgresql/data
|
||||
networks:
|
||||
- "outline_network"
|
||||
- "monitoring_network"
|
||||
environment:
|
||||
POSTGRES_USER: '{{ outline_postgres_user }}'
|
||||
POSTGRES_PASSWORD: '{{ outline_postgres_password }}'
|
||||
POSTGRES_DB: '{{ outline_postgres_database }}'
|
||||
|
||||
networks:
|
||||
outline_network:
|
||||
driver: bridge
|
||||
web_proxy_network:
|
||||
external: true
|
||||
monitoring_network:
|
||||
external: true
|
@@ -1,26 +0,0 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
66626231663733396232343163306138366434663364373937396137313134373033626539356166
|
||||
3038316664383731623635336233393566636234636532630a393234336561613133373662383161
|
||||
33653330663364363832346331653037663363643238326334326431336331373936666162363561
|
||||
3064656630666431330a626430353063313866663730663236343437356661333164653636376538
|
||||
62303164393766363933336163386663333030336132623661346565333861313537333566346563
|
||||
32666436383335353866396539663936376134653762613137343035376639376135616334326161
|
||||
62343366313032306664303030323433666230333665386630383635633863303366313639616462
|
||||
38643466356666653337383833366565633932613539666563653634643063663166623337303865
|
||||
64303365373932346233653237626363363964366431663966393937343966633735356563373735
|
||||
66366464346436303036383161316466323639396162346537653134626663303662326462656563
|
||||
63343065323636643266396532333331333137303131373633653233333837656665346635373564
|
||||
62613733613634356335636663336634323463376266373665306232626330363132313362373032
|
||||
30613366626563383236636262656135613431343639633339336135353362373665326264633438
|
||||
65306539663166623533336531356639306235346566313764343835643437663963613639326430
|
||||
36303031346339366561366166386532373838623635663837663466643032653930613635666237
|
||||
38313235343662623733613637616164366134613635343135646439623464623233303330333361
|
||||
62623166376337343838636564383633646432653436646236363262316438613333616236656532
|
||||
37336539343130343133626262616634303561326631363564353064336130613666353531646237
|
||||
66373036363764653435326638313036653135396362666439623431313930633539613965333263
|
||||
39383937616165333962366134343936323930386233356662303864643236396562313339313739
|
||||
64303934336164333563623263323236663531613265383833336239306435333735396666633666
|
||||
30663566653361343238306133613839333962373838623633363138353331616264363064316433
|
||||
36663233643134353333623264643238396438366633376530336134313365323832346663316535
|
||||
66653436323338636565303133316637353338346366633564306230386632373235653836626338
|
||||
3935
|
12
files/rssbridge/docker-compose.yml.j2
Normal file
12
files/rssbridge/docker-compose.yml.j2
Normal file
@@ -0,0 +1,12 @@
|
||||
services:
|
||||
|
||||
rssbridge_app:
|
||||
image: rssbridge/rss-bridge:2025-08-05
|
||||
container_name: rssbridge_app
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
44
files/transcriber/config.secrets.toml
Normal file
44
files/transcriber/config.secrets.toml
Normal file
@@ -0,0 +1,44 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
33396537353265633634336630353330653337623861373731613734663938633837613437366537
|
||||
3439383366633266623463366530626662346338393165630a663539313066663061353635666366
|
||||
61393437393131333166626165306563366661353338363138633239666566313330363331666537
|
||||
3763356535396334380a386362383436363732353234333033613133383264643934306432313335
|
||||
34646164323664636532663835306230386633316539373564383163346663376666633564326134
|
||||
30666135626637343963383766383836653135633739636261353666303666633566346562643962
|
||||
63376165636434343066306539653637343736323437653465656436323533636237643333326438
|
||||
35626239323530643066363533323039393237333338316135313838643464306161646635313062
|
||||
36386565626435373333393566393831366538363864313737306565343162316536353539333864
|
||||
63376264643566613266373665666363366662643262616634333132386535383731396462633430
|
||||
32343738343039616139343833366661303430383766376139636434616565356161396433643035
|
||||
37363165383935373937346464343738643430333764336264373931616332393964346566636638
|
||||
39303434343461326464623363323937396663376335316237373166306134636432376435663033
|
||||
34346436623435626363636237373965633139343661623135633764303862353465306235666563
|
||||
66653764666635636462636434663264646665383236343166643133613966366334653030653262
|
||||
38326437313939616332636638323033346139343732653933356239306132613665376163646164
|
||||
30316663643666633334653133613764396165646533636534613931663138666366316235396466
|
||||
61313964396264626339306135376635633133366433303033633363396132303938363638346333
|
||||
66326466326134313535393831343262363862663065323135643630316431336531373833316363
|
||||
64376338653366353031333836643137333736363534363164306331313337353663653961623665
|
||||
64626562366637336637353433303261303964633236356162363139396339396136393237643935
|
||||
34316266326561663834353762343766363933313463313263393063343562613933393361653861
|
||||
38363635323231666438366536626435373365323733663139666534636564623666356436346539
|
||||
63326436386436356636633637373738343032353664323736653939346234643165313461643833
|
||||
35666439613136396264313033336539313537613238393262306365656238396464373936616538
|
||||
64316365616464386638313331653030346330393665353539393834346135643434363736323135
|
||||
37663433326439356663633162616435313061353662373766633731636439636266666466613363
|
||||
39343930386534376330663230623832643933336235636166626534366664366562356165373764
|
||||
63343432323864366162376263656565646661633536666336643030363039616666343063386165
|
||||
37343238303034313832393538313632396261316232376635633732656663396631323261363433
|
||||
38373738363833323934353739643538376237316535623035383965613965636337646537326537
|
||||
64663837643632666334393634323264613139353332306263613165383733386662366333316139
|
||||
63373839346265366166333331353231663763306163323063613138323835313831303666306561
|
||||
39316666343761303464333535336361333462623363633333383363303134336139356436666165
|
||||
62616364373030613837353939363636653537373965613531636130383266643637333233316137
|
||||
39353866366239643265366162663031346439663234363935353138323739393337313835313062
|
||||
33373263326565383735366364316461323930336437623834356132346633636364313732383661
|
||||
66346634613762613037386238656334616430633037343066623463313035646339313638653137
|
||||
65643166316664626236633332326136303235623934306462643636373437373630346435633835
|
||||
66346364393236393563623032306631396561623263653236393939313333373635303365316638
|
||||
66373037333565323733656331636337336665363038353635383531386366633632363031623430
|
||||
31356461663438653736316464363231303938653932613561633139316361633461626361383132
|
||||
396436303634613135383839396566393135
|
23
files/transcriber/docker-compose.yml.j2
Normal file
23
files/transcriber/docker-compose.yml.j2
Normal file
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
|
||||
transcriber_app:
|
||||
image: "{{ registry_transcriber_image }}"
|
||||
container_name: transcriber_app
|
||||
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}'
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- "{{ config_file }}:/config/config.toml:ro"
|
||||
- "{{ data_dir }}:/data"
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
- "monitoring_network"
|
||||
environment:
|
||||
- "USER_UID={{ user_create_result.uid }}"
|
||||
- "USER_GID={{ user_create_result.group }}"
|
||||
command: ./transcriber --config=/config/config.toml
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
||||
monitoring_network:
|
||||
external: true
|
10
files/wakapi/backup.sh.j2
Normal file
10
files/wakapi/backup.sh.j2
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
echo "{{ app_name }}: backup data with gobackups"
|
||||
|
||||
(cd "{{ base_dir }}" && gobackup perform --config "{{ gobackup_config }}")
|
||||
|
||||
echo "{{ app_name }}: done."
|
32
files/wakapi/docker-compose.yml.j2
Normal file
32
files/wakapi/docker-compose.yml.j2
Normal file
@@ -0,0 +1,32 @@
|
||||
# See versions: https://github.com/gramps-project/gramps-web/pkgs/container/grampsweb
|
||||
|
||||
services:
|
||||
|
||||
wakapi_app:
|
||||
image: ghcr.io/muety/wakapi:2.15.0
|
||||
container_name: wakapi_app
|
||||
restart: unless-stopped
|
||||
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}'
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
volumes:
|
||||
- "{{ data_dir }}:/data"
|
||||
environment:
|
||||
WAKAPI_PUBLIC_URL: "https://wakapi.vakhrushev.me"
|
||||
WAKAPI_PASSWORD_SALT: "{{ wakapi_password_salt }}"
|
||||
WAKAPI_ALLOW_SIGNUP: "false"
|
||||
WAKAPI_DISABLE_FRONTPAGE: "true"
|
||||
WAKAPI_COOKIE_MAX_AGE: 31536000
|
||||
# Mail
|
||||
WAKAPI_MAIL_SENDER: "Wakapi <wakapi@vakhrushev.me>"
|
||||
WAKAPI_MAIL_PROVIDER: "smtp"
|
||||
WAKAPI_MAIL_SMTP_HOST: "{{ postbox_host }}"
|
||||
WAKAPI_MAIL_SMTP_PORT: "{{ postbox_port }}"
|
||||
WAKAPI_MAIL_SMTP_USER: "{{ postbox_user }}"
|
||||
WAKAPI_MAIL_SMTP_PASS: "{{ postbox_pass }}"
|
||||
WAKAPI_MAIL_SMTP_TLS: "false"
|
||||
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
16
files/wakapi/gobackup.yml.j2
Normal file
16
files/wakapi/gobackup.yml.j2
Normal file
@@ -0,0 +1,16 @@
|
||||
# https://gobackup.github.io/configuration
|
||||
|
||||
models:
|
||||
|
||||
gramps:
|
||||
compress_with:
|
||||
type: 'tgz'
|
||||
storages:
|
||||
local:
|
||||
type: 'local'
|
||||
path: '{{ backups_dir }}'
|
||||
keep: 3
|
||||
databases:
|
||||
wakapi:
|
||||
type: sqlite
|
||||
path: "{{ (data_dir, 'wakapi.db') | path_join }}"
|
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# Must be executed for every user
|
||||
# See https://cloud.yandex.ru/docs/container-registry/tutorials/run-docker-on-vm#run
|
||||
|
||||
set -eu
|
@@ -1 +0,0 @@
|
||||
192.168.50.10
|
14
lefthook.yml
Normal file
14
lefthook.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
# Refer for explanation to following link:
|
||||
# https://lefthook.dev/configuration/
|
||||
|
||||
templates:
|
||||
av-hooks-dir: "/home/av/projects/private/git-hooks"
|
||||
|
||||
pre-commit:
|
||||
jobs:
|
||||
|
||||
- name: "gitleaks"
|
||||
run: "gitleaks git --staged"
|
||||
|
||||
- name: "check secret files"
|
||||
run: "python3 {av-hooks-dir}/pre-commit/check-secrets-encrypted-with-ansible-vault.py"
|
@@ -1,51 +0,0 @@
|
||||
---
|
||||
- name: 'Configure gramps application'
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
|
||||
vars:
|
||||
app_name: 'gramps'
|
||||
base_dir: '/home/major/applications/{{ app_name }}/'
|
||||
|
||||
tasks:
|
||||
|
||||
- name: 'Create application directories'
|
||||
ansible.builtin.file:
|
||||
path: '{{ item }}'
|
||||
state: 'directory'
|
||||
mode: '0755'
|
||||
loop:
|
||||
- '{{ base_dir }}'
|
||||
- '{{ (base_dir, "data") | path_join }}'
|
||||
|
||||
- name: 'Copy application files'
|
||||
ansible.builtin.copy:
|
||||
src: '{{ item }}'
|
||||
dest: '{{ base_dir }}'
|
||||
mode: '0644'
|
||||
loop:
|
||||
- './files/apps/{{ app_name }}/docker-compose.yml'
|
||||
|
||||
- name: 'Set up environment variables for application'
|
||||
ansible.builtin.template:
|
||||
src: 'env.j2'
|
||||
dest: '{{ (base_dir, ".env") | path_join }}'
|
||||
mode: '0644'
|
||||
vars:
|
||||
env_dict:
|
||||
WEB_SERVER_PORT: '{{ gramps_port }}'
|
||||
SECRET_KEY: '{{ gramps.secret_key }}'
|
||||
AWS_ACCESS_KEY_ID: '{{ gramps.aws_access_key_id }}'
|
||||
AWS_SECRET_ACCESS_KEY: '{{ gramps.aws_secret_access_key }}'
|
||||
POSTBOX_HOST: '{{ postbox.host }}'
|
||||
POSTBOX_PORT: '{{ postbox.port }}'
|
||||
POSTBOX_USER: '{{ postbox.user }}'
|
||||
POSTBOX_PASS: '{{ postbox.pass }}'
|
||||
|
||||
- name: 'Run application with docker compose'
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: '{{ base_dir }}'
|
||||
state: 'present'
|
@@ -1,43 +0,0 @@
|
||||
---
|
||||
- name: 'Configure music application'
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
|
||||
vars:
|
||||
base_dir: '/home/major/applications/music/'
|
||||
|
||||
tasks:
|
||||
|
||||
- name: 'Create application directories'
|
||||
ansible.builtin.file:
|
||||
path: '{{ item }}'
|
||||
state: 'directory'
|
||||
mode: '0755'
|
||||
loop:
|
||||
- '{{ base_dir }}'
|
||||
- '{{ (base_dir, "data") | path_join }}'
|
||||
|
||||
- name: 'Copy application files'
|
||||
ansible.builtin.copy:
|
||||
src: '{{ item }}'
|
||||
dest: '{{ base_dir }}'
|
||||
mode: '0644'
|
||||
loop:
|
||||
- './files/apps/music/docker-compose.yml'
|
||||
|
||||
- name: 'Set up environment variables for application'
|
||||
ansible.builtin.template:
|
||||
src: 'env.j2'
|
||||
dest: '{{ (base_dir, ".env") | path_join }}'
|
||||
mode: '0644'
|
||||
vars:
|
||||
env_dict:
|
||||
WEB_SERVER_PORT: '{{ navidrome_port }}'
|
||||
|
||||
- name: 'Run application with docker compose'
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: '{{ base_dir }}'
|
||||
state: 'present'
|
67
playbook-authelia.yml
Normal file
67
playbook-authelia.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
- name: "Configure authelia application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
- files/authelia/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "authelia"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups: ["docker"]
|
||||
|
||||
- name: "Create internal application directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0700"
|
||||
loop:
|
||||
- "{{ config_dir }}"
|
||||
|
||||
- name: "Copy users file"
|
||||
ansible.builtin.copy:
|
||||
src: "files/{{ app_name }}/users.secrets.yml"
|
||||
dest: "{{ (config_dir, 'users.yml') | path_join }}"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0600"
|
||||
|
||||
- name: "Copy configuration files (templates)"
|
||||
ansible.builtin.template:
|
||||
src: "files/{{ app_name }}/configuration.template.yml"
|
||||
dest: "{{ (config_dir, 'configuration.yml') | path_join }}"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0600"
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
||||
|
||||
- name: "Restart application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "restarted"
|
53
playbook-backups.yml
Normal file
53
playbook-backups.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
- name: "Configure restic and backup schedule"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/secrets.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
restic_shell_script: "{{ (bin_prefix, 'restic-shell.sh') | path_join }}"
|
||||
backup_all_script: "{{ (bin_prefix, 'backup-all.py') | path_join }}"
|
||||
|
||||
tasks:
|
||||
- name: "Copy restic shell script"
|
||||
ansible.builtin.template:
|
||||
src: "files/backups/restic-shell.sh.j2"
|
||||
dest: "{{ restic_shell_script }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: "Copy backup all script"
|
||||
ansible.builtin.template:
|
||||
src: "files/backups/backup-all.template.py"
|
||||
dest: "{{ backup_all_script }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: "Setup paths for backup cron file"
|
||||
ansible.builtin.cron:
|
||||
cron_file: "ansible_restic_backup"
|
||||
user: "root"
|
||||
env: true
|
||||
name: "PATH"
|
||||
job: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
|
||||
|
||||
- name: "Setup mail for backup cron file"
|
||||
ansible.builtin.cron:
|
||||
cron_file: "ansible_restic_backup"
|
||||
user: "root"
|
||||
env: true
|
||||
name: "MAILTO"
|
||||
job: ""
|
||||
|
||||
- name: "Creates a cron file for backups under /etc/cron.d"
|
||||
ansible.builtin.cron:
|
||||
name: "restic backup"
|
||||
minute: "0"
|
||||
hour: "1"
|
||||
job: "{{ backup_all_script }} 2>&1 | logger -t backup"
|
||||
cron_file: "ansible_restic_backup"
|
||||
user: "root"
|
@@ -1,27 +0,0 @@
|
||||
---
|
||||
- name: 'Install and configure Caddy server'
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- name: 'Ensure networkd service is started (required by Caddy).'
|
||||
ansible.builtin.systemd:
|
||||
name: systemd-networkd
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: 'Install and configure Caddy server'
|
||||
ansible.builtin.import_role:
|
||||
name: caddy_ansible.caddy_ansible
|
||||
vars:
|
||||
caddy_github_token: '{{ caddy_vars.github_token }}'
|
||||
caddy_config: '{{ lookup("template", "templates/Caddyfile.j2") }}'
|
||||
caddy_setcap: true
|
||||
caddy_systemd_capabilities_enabled: true
|
||||
caddy_systemd_capabilities: "CAP_NET_BIND_SERVICE"
|
||||
# Поменяй на true, чтобы обновить Caddy
|
||||
caddy_update: false
|
72
playbook-caddyproxy.yml
Normal file
72
playbook-caddyproxy.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
- name: "Configure caddy reverse proxy service"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "caddyproxy"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
|
||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
||||
caddy_file_dir: "{{ (base_dir, 'caddy_file') | path_join }}"
|
||||
|
||||
service_name: "{{ app_name }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups:
|
||||
- "docker"
|
||||
|
||||
- name: "Create internal application directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0770"
|
||||
loop:
|
||||
- "{{ data_dir }}"
|
||||
- "{{ config_dir }}"
|
||||
- "{{ caddy_file_dir }}"
|
||||
|
||||
- name: "Copy caddy file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/Caddyfile.j2"
|
||||
dest: "{{ (caddy_file_dir, 'Caddyfile') | path_join }}"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
||||
|
||||
# - name: "Reload caddy"
|
||||
# community.docker.docker_compose_v2_exec:
|
||||
# project_src: '{{ base_dir }}'
|
||||
# service: "{{ service_name }}"
|
||||
# command: caddy reload --config /etc/caddy/Caddyfile
|
||||
|
||||
- name: "Restart application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "restarted"
|
@@ -1,171 +0,0 @@
|
||||
---
|
||||
- hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
|
||||
tasks:
|
||||
|
||||
# Applications
|
||||
|
||||
- import_role:
|
||||
name: docker-app
|
||||
vars:
|
||||
username: homepage
|
||||
extra_groups:
|
||||
- docker
|
||||
ssh_keys:
|
||||
- '{{ lookup("file", "files/av_id_rsa.pub") }}'
|
||||
env:
|
||||
DOCKER_PREFIX: homepage
|
||||
PROJECT_NAME: homepage
|
||||
IMAGE_PREFIX: homepage
|
||||
CONTAINER_PREFIX: homepage
|
||||
WEB_SERVER_PORT: '127.0.0.1:{{ homepage_port }}'
|
||||
tags:
|
||||
- apps
|
||||
|
||||
- import_role:
|
||||
name: docker-app
|
||||
vars:
|
||||
username: dayoff
|
||||
extra_groups:
|
||||
- docker
|
||||
ssh_keys:
|
||||
- '{{ lookup("file", "files/av_id_rsa.pub") }}'
|
||||
- '{{ lookup("file", "files/dayoff_id_rsa.pub") }}'
|
||||
env:
|
||||
DOCKER_PREFIX: dayoff
|
||||
PROJECT_NAME: dayoff
|
||||
IMAGE_PREFIX: dayoff
|
||||
CONTAINER_PREFIX: dayoff
|
||||
WEB_SERVER_PORT: '127.0.0.1:{{ dayoff_port }}'
|
||||
tags:
|
||||
- apps
|
||||
|
||||
- import_role:
|
||||
name: docker-app
|
||||
vars:
|
||||
username: wiki
|
||||
extra_groups:
|
||||
- docker
|
||||
ssh_keys:
|
||||
- '{{ lookup("file", "files/av_id_rsa.pub") }}'
|
||||
env:
|
||||
PROJECT_NAME: wiki
|
||||
DOCKER_PREFIX: wiki
|
||||
IMAGE_PREFIX: wiki
|
||||
CONTAINER_PREFIX: wiki
|
||||
WEB_SERVER_PORT: '127.0.0.1:{{ wiki_port }}'
|
||||
tags:
|
||||
- apps
|
||||
|
||||
- import_role:
|
||||
name: docker-app
|
||||
vars:
|
||||
username: nomie
|
||||
extra_groups:
|
||||
- docker
|
||||
ssh_keys:
|
||||
- '{{ lookup("file", "files/av_id_rsa.pub") }}'
|
||||
env:
|
||||
PROJECT_NAME: nomie
|
||||
DOCKER_PREFIX: nomie
|
||||
IMAGE_PREFIX: nomie
|
||||
CONTAINER_PREFIX: nomie
|
||||
WEB_SERVER_PORT: '127.0.0.1:{{ nomie_port }}'
|
||||
COUCH_DB_PORT: '127.0.0.1:{{ nomie_db_port }}'
|
||||
COUCH_DB_USER: 'couch-admin'
|
||||
COUCH_DB_PASSWORD: '{{ nomie.couch_db_password }}'
|
||||
tags:
|
||||
- apps
|
||||
|
||||
- import_role:
|
||||
name: docker-app
|
||||
vars:
|
||||
username: gitea
|
||||
extra_groups:
|
||||
- docker
|
||||
ssh_keys:
|
||||
- '{{ lookup("file", "files/av_id_rsa.pub") }}'
|
||||
env:
|
||||
PROJECT_NAME: gitea
|
||||
DOCKER_PREFIX: gitea
|
||||
IMAGE_PREFIX: gitea
|
||||
CONTAINER_PREFIX: gitea
|
||||
WEB_SERVER_PORT: '127.0.0.1:{{ gitea_port }}'
|
||||
USER_UID: '{{ uc_result.uid }}'
|
||||
USER_GID: '{{ uc_result.group }}'
|
||||
tags:
|
||||
- apps
|
||||
|
||||
- import_role:
|
||||
name: docker-app
|
||||
vars:
|
||||
username: keycloak
|
||||
extra_groups:
|
||||
- docker
|
||||
ssh_keys:
|
||||
- '{{ lookup("file", "files/av_id_rsa.pub") }}'
|
||||
env:
|
||||
PROJECT_NAME: keycloak
|
||||
DOCKER_PREFIX: keycloak
|
||||
IMAGE_PREFIX: keycloak
|
||||
CONTAINER_PREFIX: keycloak
|
||||
WEB_SERVER_PORT: '127.0.0.1:{{ keycloak_port }}'
|
||||
KEYCLOAK_ADMIN: '{{ keycloak.admin_login }}'
|
||||
KEYCLOAK_ADMIN_PASSWORD: '{{ keycloak.admin_password }}'
|
||||
USER_UID: '{{ uc_result.uid }}'
|
||||
USER_GID: '{{ uc_result.group }}'
|
||||
tags:
|
||||
- apps
|
||||
|
||||
- import_role:
|
||||
name: docker-app
|
||||
vars:
|
||||
username: outline
|
||||
extra_groups:
|
||||
- docker
|
||||
ssh_keys:
|
||||
- '{{ lookup("file", "files/av_id_rsa.pub") }}'
|
||||
env:
|
||||
PROJECT_NAME: outline
|
||||
DOCKER_PREFIX: outline
|
||||
IMAGE_PREFIX: outline
|
||||
CONTAINER_PREFIX: outline
|
||||
WEB_SERVER_PORT: '127.0.0.1:{{ outline_port }}'
|
||||
USER_UID: '{{ uc_result.uid }}'
|
||||
USER_GID: '{{ uc_result.group }}'
|
||||
# Postgres
|
||||
POSTGRES_USER: '{{ outline.postgres_user }}'
|
||||
POSTGRES_PASSWORD: '{{ outline.postgres_password }}'
|
||||
POSTGRES_DB: 'outline'
|
||||
# See sample https://github.com/outline/outline/blob/main/.env.sample
|
||||
NODE_ENV: 'production'
|
||||
SECRET_KEY: '{{ outline.secret_key }}'
|
||||
UTILS_SECRET: '{{ outline.utils_secret }}'
|
||||
DATABASE_URL: 'postgres://{{ outline.postgres_user }}:{{ outline.postgres_password }}@postgres:5432/outline'
|
||||
PGSSLMODE: 'disable'
|
||||
REDIS_URL: 'redis://redis:6379'
|
||||
URL: 'https://outline.vakhrushev.me'
|
||||
FILE_STORAGE: 's3'
|
||||
AWS_ACCESS_KEY_ID: '{{ outline.s3_access_key }}'
|
||||
AWS_SECRET_ACCESS_KEY: '{{ outline.s3_secret_key }}'
|
||||
AWS_REGION: 'ru-central1'
|
||||
AWS_S3_ACCELERATE_URL: ''
|
||||
AWS_S3_UPLOAD_BUCKET_URL: 'https://storage.yandexcloud.net'
|
||||
AWS_S3_UPLOAD_BUCKET_NAME: 'av-outline-wiki'
|
||||
AWS_S3_FORCE_PATH_STYLE: 'true'
|
||||
AWS_S3_ACL: 'private'
|
||||
OIDC_CLIENT_ID: '{{ outline.oidc_client_id }}'
|
||||
OIDC_CLIENT_SECRET: '{{ outline.oidc_client_secret }}'
|
||||
OIDC_AUTH_URI: 'https://kk.vakhrushev.me/realms/outline/protocol/openid-connect/auth'
|
||||
OIDC_TOKEN_URI: 'https://kk.vakhrushev.me/realms/outline/protocol/openid-connect/token'
|
||||
OIDC_USERINFO_URI: 'https://kk.vakhrushev.me/realms/outline/protocol/openid-connect/userinfo'
|
||||
OIDC_LOGOUT_URI: 'https://kk.vakhrushev.me/realms/outline/protocol/openid-connect/logout'
|
||||
OIDC_USERNAME_CLAIM: 'email'
|
||||
OIDC_DISPLAY_NAME: 'KK'
|
||||
|
||||
tags:
|
||||
- apps
|
@@ -1,29 +1,34 @@
|
||||
---
|
||||
- name: 'Configure docker parameters'
|
||||
- name: "Configure docker parameters"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- name: 'Install python docker lib from pip'
|
||||
- name: "Install python docker lib from pip"
|
||||
ansible.builtin.pip:
|
||||
name: docker
|
||||
|
||||
- name: 'Install docker'
|
||||
- name: "Install docker"
|
||||
ansible.builtin.import_role:
|
||||
name: geerlingguy.docker
|
||||
vars:
|
||||
docker_edition: 'ce'
|
||||
docker_edition: "ce"
|
||||
docker_packages:
|
||||
- "docker-{{ docker_edition }}"
|
||||
- "docker-{{ docker_edition }}-cli"
|
||||
- "docker-{{ docker_edition }}-rootless-extras"
|
||||
docker_users:
|
||||
- major
|
||||
- "{{ primary_user }}"
|
||||
|
||||
- name: 'Install rclone plugin'
|
||||
ansible.builtin.import_role:
|
||||
name: docker_rclone_plugin
|
||||
- name: Create a network for web proxy
|
||||
community.docker.docker_network:
|
||||
name: "web_proxy_network"
|
||||
driver: "bridge"
|
||||
|
||||
- name: Create a network for monitoring
|
||||
community.docker.docker_network:
|
||||
name: "monitoring_network"
|
||||
driver: "bridge"
|
||||
|
34
playbook-dozzle.yml
Normal file
34
playbook-dozzle.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
- name: "Configure dozzle application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "dozzle"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups: ["docker"]
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
@@ -1,26 +1,46 @@
|
||||
---
|
||||
- name: 'Install eget'
|
||||
- name: "Install eget"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
# See: https://github.com/zyedidia/eget/releases
|
||||
|
||||
vars:
|
||||
eget_install_dir: "{{ bin_prefix }}"
|
||||
eget_bin_path: '{{ (eget_install_dir, "eget") | path_join }}'
|
||||
|
||||
tasks:
|
||||
|
||||
- name: 'Install eget'
|
||||
- name: "Install eget"
|
||||
ansible.builtin.import_role:
|
||||
name: eget
|
||||
vars:
|
||||
eget_version: '1.3.4'
|
||||
eget_install_path: '/usr/bin/eget'
|
||||
eget_version: "1.3.4"
|
||||
eget_install_path: "{{ eget_bin_path }}"
|
||||
|
||||
- name: 'Install rclone with eget'
|
||||
- name: "Install rclone"
|
||||
ansible.builtin.command:
|
||||
cmd: '/usr/bin/eget rclone/rclone --quiet --upgrade-only --to /usr/bin --tag v1.68.2 --asset zip'
|
||||
cmd: "{{ eget_bin_path }} rclone/rclone --quiet --upgrade-only --to {{ eget_install_dir }} --asset zip --tag v1.69.2"
|
||||
changed_when: false
|
||||
|
||||
- name: 'Install btop with eget'
|
||||
- name: "Install btop"
|
||||
ansible.builtin.command:
|
||||
cmd: '/usr/bin/eget aristocratos/btop --quiet --upgrade-only --to /usr/bin --tag v1.4.0'
|
||||
cmd: "{{ eget_bin_path }} aristocratos/btop --quiet --upgrade-only --to {{ eget_install_dir }} --tag v1.4.2"
|
||||
changed_when: false
|
||||
|
||||
- name: "Install restic"
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ eget_bin_path }} restic/restic --quiet --upgrade-only --to {{ eget_install_dir }} --tag v0.18.0"
|
||||
changed_when: false
|
||||
|
||||
- name: "Install gobackup"
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ eget_bin_path }} gobackup/gobackup --quiet --upgrade-only --to {{ eget_install_dir }} --tag v2.14.0"
|
||||
changed_when: false
|
||||
|
||||
- name: "Install task"
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ eget_bin_path }} go-task/task --quiet --upgrade-only --to {{ eget_install_dir }} --asset tar.gz --tag v3.43.3"
|
||||
changed_when: false
|
||||
|
58
playbook-gitea.yml
Normal file
58
playbook-gitea.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
- name: "Configure gitea application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "gitea"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups:
|
||||
- "docker"
|
||||
owner_ssh_keys:
|
||||
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
|
||||
|
||||
- name: "Create internal application directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0770"
|
||||
loop:
|
||||
- "{{ data_dir }}"
|
||||
- "{{ backups_dir }}"
|
||||
|
||||
- name: "Copy backup script"
|
||||
ansible.builtin.template:
|
||||
src: "files/{{ app_name }}/backup.sh.j2"
|
||||
dest: "{{ base_dir }}/backup.sh"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0750"
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
67
playbook-gramps.yml
Normal file
67
playbook-gramps.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
- name: "Configure gramps application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "gramps"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups:
|
||||
- "docker"
|
||||
owner_ssh_keys:
|
||||
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
|
||||
|
||||
- name: "Create application internal directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0750"
|
||||
loop:
|
||||
- "{{ data_dir }}"
|
||||
- "{{ backups_dir }}"
|
||||
|
||||
- name: "Copy gobackup config"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/gobackup.yml.j2"
|
||||
dest: "{{ gobackup_config }}"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Copy backup script"
|
||||
ansible.builtin.template:
|
||||
src: "files/{{ app_name }}/backup.sh.j2"
|
||||
dest: "{{ base_dir }}/backup.sh"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0750"
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
67
playbook-homepage.yml
Normal file
67
playbook-homepage.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
# Play 1: Setup environment for the application
|
||||
- name: "Setup environment for homepage application"
|
||||
hosts: all
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
- vars/homepage.yml
|
||||
tags:
|
||||
- setup
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups:
|
||||
- "docker"
|
||||
owner_ssh_keys:
|
||||
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
|
||||
|
||||
- name: "Login to yandex docker registry."
|
||||
ansible.builtin.script:
|
||||
cmd: "files/yandex-docker-registry-auth.sh"
|
||||
|
||||
# Play 2: Deploy the application
|
||||
- name: "Deploy homepage application"
|
||||
hosts: all
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
- vars/homepage.yml
|
||||
tags:
|
||||
- deploy
|
||||
tasks:
|
||||
- name: "Check is web service image passed"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "homepage_web_image is defined"
|
||||
fail_msg: 'You must pass variable "homepage_web_image"'
|
||||
|
||||
- name: "Create full image name with container registry"
|
||||
ansible.builtin.set_fact:
|
||||
registry_homepage_web_image: "{{ (docker_registry_prefix, homepage_web_image) | path_join }}"
|
||||
|
||||
- name: "Push web service image to remote registry"
|
||||
community.docker.docker_image:
|
||||
state: present
|
||||
source: local
|
||||
name: "{{ homepage_web_image }}"
|
||||
repository: "{{ registry_homepage_web_image }}"
|
||||
push: true
|
||||
delegate_to: 127.0.0.1
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
72
playbook-miniflux.yml
Normal file
72
playbook-miniflux.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
- name: "Configure miniflux application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "miniflux"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||
secrets_dir: "{{ (base_dir, 'secrets') | path_join }}"
|
||||
postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}"
|
||||
postgres_backups_dir: "{{ (base_dir, 'backups', 'postgres') | path_join }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups: ["docker"]
|
||||
|
||||
- name: "Create internal directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0770"
|
||||
loop:
|
||||
- "{{ postgres_backups_dir }}"
|
||||
|
||||
- name: "Copy secrets"
|
||||
ansible.builtin.import_role:
|
||||
name: secrets
|
||||
vars:
|
||||
secrets_dest: "{{ secrets_dir }}"
|
||||
secrets_user: "{{ app_user }}"
|
||||
secrets_group: "{{ app_user }}"
|
||||
secrets_vars:
|
||||
- "miniflux_database_url"
|
||||
- "miniflux_admin_user"
|
||||
- "miniflux_admin_password"
|
||||
- "miniflux_oidc_client_id"
|
||||
- "miniflux_oidc_client_secret"
|
||||
- "miniflux_postgres_password"
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Copy backup script"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/backup.sh.j2"
|
||||
dest: "{{ base_dir }}/backup.sh"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0750"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
recreate: "always"
|
||||
remove_orphans: true
|
@@ -1,17 +1,100 @@
|
||||
---
|
||||
- name: 'Install Netdata monitoring service'
|
||||
- name: "Install Netdata monitoring service"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "netdata"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
||||
config_go_d_dir: "{{ (config_dir, 'go.d') | path_join }}"
|
||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||
|
||||
tasks:
|
||||
- name: 'Install Netdata from role'
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: netdata
|
||||
name: owner
|
||||
vars:
|
||||
netdata_version: 'v2.1.0'
|
||||
netdata_exposed_port: '{{ netdata_port }}'
|
||||
tags:
|
||||
- monitoring
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups: ["docker"]
|
||||
|
||||
- name: "Create internal application directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0770"
|
||||
loop:
|
||||
- "{{ config_dir }}"
|
||||
- "{{ config_go_d_dir }}"
|
||||
- "{{ data_dir }}"
|
||||
|
||||
- name: "Copy netdata config file"
|
||||
ansible.builtin.template:
|
||||
src: "files/{{ app_name }}/netdata.conf.j2"
|
||||
dest: "{{ config_dir }}/netdata.conf"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Find all go.d plugin config files"
|
||||
ansible.builtin.find:
|
||||
paths: "files/{{ app_name }}/go.d"
|
||||
file_type: file
|
||||
delegate_to: localhost
|
||||
register: go_d_source_files
|
||||
|
||||
- name: "Template all go.d plugin config files"
|
||||
ansible.builtin.template:
|
||||
src: "{{ item.path }}"
|
||||
dest: "{{ config_go_d_dir }}/{{ item.path | basename }}"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
loop: "{{ go_d_source_files.files }}"
|
||||
|
||||
- name: "Find existing go.d config files on server"
|
||||
ansible.builtin.find:
|
||||
paths: "{{ config_go_d_dir }}"
|
||||
file_type: file
|
||||
register: go_d_existing_files
|
||||
|
||||
- name: "Remove go.d config files that don't exist in source"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.path }}"
|
||||
state: absent
|
||||
loop: "{{ go_d_existing_files.files }}"
|
||||
when: (item.path | basename) not in (go_d_source_files.files | map(attribute='path') | map('basename') | list)
|
||||
|
||||
- name: "Grab docker group id."
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
grep docker /etc/group | cut -d ':' -f 3
|
||||
executable: /bin/bash
|
||||
register: netdata_docker_group_output
|
||||
changed_when: netdata_docker_group_output.rc != 0
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
||||
|
||||
- name: "Restart application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "restarted"
|
||||
|
58
playbook-outline.yml
Normal file
58
playbook-outline.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
- name: "Configure outline application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "outline"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||
postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}"
|
||||
postgres_backups_dir: "{{ (base_dir, 'backups', 'postgres') | path_join }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups:
|
||||
- "docker"
|
||||
owner_ssh_keys:
|
||||
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
|
||||
|
||||
- name: "Create internal directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0770"
|
||||
loop:
|
||||
- "{{ postgres_backups_dir }}"
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Copy backup script"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/backup.sh.j2"
|
||||
dest: "{{ base_dir }}/backup.sh"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0750"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
@@ -1,27 +1,32 @@
|
||||
---
|
||||
- name: 'Update and upgrade system packages'
|
||||
- name: "Update and upgrade system packages"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
user_name: '<put-name-here>'
|
||||
user_name: "<put-name-here>"
|
||||
|
||||
tasks:
|
||||
- name: 'Remove user "{{ user_name }}"'
|
||||
ansible.builtin.user:
|
||||
name: '{{ user_name }}'
|
||||
name: "{{ user_name }}"
|
||||
state: absent
|
||||
remove: true
|
||||
|
||||
- name: 'Remove group "{{ user_name }}"'
|
||||
ansible.builtin.group:
|
||||
name: '{{ user_name }}'
|
||||
name: "{{ user_name }}"
|
||||
state: absent
|
||||
|
||||
- name: 'Remove web dir'
|
||||
- name: "Remove web dir"
|
||||
ansible.builtin.file:
|
||||
path: '/var/www/{{ user_name }}'
|
||||
path: "/var/www/{{ user_name }}"
|
||||
state: absent
|
||||
|
||||
- name: "Remove home dir"
|
||||
ansible.builtin.file:
|
||||
path: "/home/{{ user_name }}"
|
||||
state: absent
|
||||
|
34
playbook-rssbridge.yml
Normal file
34
playbook-rssbridge.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
- name: "Configure rssbridge application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "rssbridge"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups: ["docker"]
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
@@ -1,10 +1,10 @@
|
||||
---
|
||||
- name: 'Configure base system parameters'
|
||||
- name: "Configure base system parameters"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
apt_packages:
|
||||
@@ -16,26 +16,27 @@
|
||||
- jq
|
||||
- make
|
||||
- python3-pip
|
||||
- sqlite3
|
||||
- tree
|
||||
|
||||
tasks:
|
||||
|
||||
- name: 'Install additional apt packages'
|
||||
- name: "Install additional apt packages"
|
||||
ansible.builtin.apt:
|
||||
name: '{{ apt_packages }}'
|
||||
name: "{{ apt_packages }}"
|
||||
update_cache: true
|
||||
|
||||
- name: 'Configure timezone'
|
||||
ansible.builtin.import_role:
|
||||
name: yatesr.timezone
|
||||
vars:
|
||||
timezone: UTC
|
||||
tags:
|
||||
- skip_ansible_lint
|
||||
|
||||
- name: 'Configure security settings'
|
||||
- name: "Configure security settings"
|
||||
ansible.builtin.import_role:
|
||||
name: geerlingguy.security
|
||||
vars:
|
||||
security_ssh_permit_root_login: "yes"
|
||||
security_autoupdate_enabled: "no"
|
||||
security_fail2ban_enabled: "yes"
|
||||
security_fail2ban_enabled: true
|
||||
|
||||
- name: "Copy keep files script"
|
||||
ansible.builtin.copy:
|
||||
src: "files/keep-files.py"
|
||||
dest: "{{ bin_prefix }}/keep-files.py"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
92
playbook-transcriber.yml
Normal file
92
playbook-transcriber.yml
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
- name: "Deploy transcriber application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
|
||||
vars:
|
||||
app_name: "transcriber"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
|
||||
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
||||
config_file: "{{ (config_dir, 'config.toml') | path_join }}"
|
||||
|
||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||
|
||||
docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade"
|
||||
|
||||
# transcriber_image: "{{ transcriber_image | default(omit) }}"
|
||||
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups: ["docker"]
|
||||
|
||||
- name: "Create application internal directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0750"
|
||||
loop:
|
||||
- "{{ config_dir }}"
|
||||
- "{{ data_dir }}"
|
||||
- "{{ backups_dir }}"
|
||||
|
||||
- name: "Copy configuration files (templates)"
|
||||
ansible.builtin.copy:
|
||||
src: "files/{{ app_name }}/config.secrets.toml"
|
||||
dest: "{{ config_file }}"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0600"
|
||||
|
||||
- name: "Login to yandex docker registry."
|
||||
ansible.builtin.script:
|
||||
cmd: "files/yandex-docker-registry-auth.sh"
|
||||
|
||||
- name: "Deploy service"
|
||||
when: transcriber_image is defined
|
||||
block:
|
||||
# - name: "Check is web service image passed"
|
||||
# ansible.builtin.assert:
|
||||
# that:
|
||||
# - "transcriber_image is defined"
|
||||
# fail_msg: 'You must pass variable "transcriber_image"'
|
||||
|
||||
- name: "Create full image name with container registry"
|
||||
ansible.builtin.set_fact:
|
||||
registry_transcriber_image: "{{ (docker_registry_prefix, transcriber_image) | path_join }}"
|
||||
|
||||
- name: "Push web service image to remote registry"
|
||||
community.docker.docker_image:
|
||||
state: present
|
||||
source: local
|
||||
name: "{{ transcriber_image }}"
|
||||
repository: "{{ registry_transcriber_image }}"
|
||||
push: true
|
||||
delegate_to: 127.0.0.1
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
@@ -1,15 +1,15 @@
|
||||
---
|
||||
- name: 'Update and upgrade system packages'
|
||||
- name: "Update and upgrade system packages"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/vars.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
tasks:
|
||||
- name: Perform an upgrade of packages
|
||||
ansible.builtin.apt:
|
||||
upgrade: 'yes'
|
||||
upgrade: "yes"
|
||||
update_cache: true
|
||||
|
||||
- name: Check if a reboot is required
|
||||
@@ -25,3 +25,18 @@
|
||||
- name: Remove dependencies that are no longer required
|
||||
ansible.builtin.apt:
|
||||
autoremove: true
|
||||
|
||||
- name: Check if Docker is available
|
||||
ansible.builtin.stat:
|
||||
path: /usr/bin/docker
|
||||
register: docker_exists
|
||||
|
||||
- name: Clean up unnecessary Docker data
|
||||
ansible.builtin.command:
|
||||
cmd: docker system prune --all --force
|
||||
register: docker_prune_result
|
||||
when: docker_exists.stat.exists
|
||||
failed_when:
|
||||
- docker_prune_result.rc is defined
|
||||
- docker_prune_result.rc != 0
|
||||
changed_when: "'Total reclaimed space' in docker_prune_result.stdout"
|
||||
|
64
playbook-wakapi.yml
Normal file
64
playbook-wakapi.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
- name: "Configure wakapi application"
|
||||
hosts: all
|
||||
|
||||
vars_files:
|
||||
- vars/ports.yml
|
||||
- vars/secrets.yml
|
||||
|
||||
vars:
|
||||
app_name: "wakapi"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
||||
|
||||
tasks:
|
||||
- name: "Create user and environment"
|
||||
ansible.builtin.import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: "{{ app_user }}"
|
||||
owner_extra_groups: ["docker"]
|
||||
|
||||
- name: "Create application internal directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0750"
|
||||
loop:
|
||||
- "{{ data_dir }}"
|
||||
- "{{ backups_dir }}"
|
||||
|
||||
- name: "Copy gobackup config"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/gobackup.yml.j2"
|
||||
dest: "{{ gobackup_config }}"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Copy backup script"
|
||||
ansible.builtin.template:
|
||||
src: "files/{{ app_name }}/backup.sh.j2"
|
||||
dest: "{{ base_dir }}/backup.sh"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0750"
|
||||
|
||||
- name: "Copy docker compose file"
|
||||
ansible.builtin.template:
|
||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||
dest: "{{ base_dir }}/docker-compose.yml"
|
||||
owner: "{{ app_user }}"
|
||||
group: "{{ app_user }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: "Run application with docker compose"
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ base_dir }}"
|
||||
state: "present"
|
||||
remove_orphans: true
|
@@ -2,6 +2,6 @@
|
||||
ungrouped:
|
||||
hosts:
|
||||
server:
|
||||
ansible_host: '158.160.46.255'
|
||||
ansible_user: 'major'
|
||||
ansible_host: "158.160.46.255"
|
||||
ansible_user: "major"
|
||||
ansible_become: true
|
||||
|
@@ -3,10 +3,7 @@
|
||||
version: 1.2.2
|
||||
|
||||
- src: geerlingguy.security
|
||||
version: 2.4.0
|
||||
version: 3.0.0
|
||||
|
||||
- src: geerlingguy.docker
|
||||
version: 7.4.3
|
||||
|
||||
- src: caddy_ansible.caddy_ansible
|
||||
version: v3.2.0
|
||||
version: 7.4.7
|
||||
|
@@ -1,24 +0,0 @@
|
||||
---
|
||||
- name: 'Create owner.'
|
||||
import_role:
|
||||
name: owner
|
||||
vars:
|
||||
owner_name: '{{ username }}'
|
||||
owner_group: '{{ username }}'
|
||||
owner_extra_groups: '{{ extra_groups | default([]) }}'
|
||||
owner_ssh_keys: '{{ ssh_keys | default([]) }}'
|
||||
owner_env: '{{ env | default({}) }}'
|
||||
|
||||
- name: 'Create web dir.'
|
||||
file:
|
||||
path: '/var/www/{{ username }}'
|
||||
state: directory
|
||||
owner: '{{ username }}'
|
||||
group: '{{ username }}'
|
||||
recurse: True
|
||||
|
||||
- name: 'Login to yandex docker registry.'
|
||||
ansible.builtin.script:
|
||||
cmd: 'files/yandex-docker-registry-auth.sh'
|
||||
become: yes
|
||||
become_user: '{{ username }}'
|
@@ -1,38 +0,0 @@
|
||||
Role Name
|
||||
=========
|
||||
|
||||
A brief description of the role goes here.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||
|
||||
- hosts: servers
|
||||
roles:
|
||||
- { role: username.rolename, x: 42 }
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
@@ -1,3 +0,0 @@
|
||||
---
|
||||
|
||||
docker_rclone_plugin_config: './files/rclone.conf'
|
@@ -1,2 +0,0 @@
|
||||
---
|
||||
# handlers file for docker_rclone_plugin
|
@@ -1,51 +0,0 @@
|
||||
galaxy_info:
|
||||
author: 'Anton Vakhrushev'
|
||||
description: 'Rclone docker plugin'
|
||||
|
||||
# If the issue tracker for your role is not on github, uncomment the
|
||||
# next line and provide a value
|
||||
# issue_tracker_url: http://example.com/issue/tracker
|
||||
|
||||
# Choose a valid license ID from https://spdx.org - some suggested licenses:
|
||||
# - BSD-3-Clause (default)
|
||||
# - MIT
|
||||
# - GPL-2.0-or-later
|
||||
# - GPL-3.0-only
|
||||
# - Apache-2.0
|
||||
# - CC-BY-4.0
|
||||
license: 'MIT'
|
||||
|
||||
min_ansible_version: '2.1'
|
||||
|
||||
# If this a Container Enabled role, provide the minimum Ansible Container version.
|
||||
# min_ansible_container_version:
|
||||
|
||||
#
|
||||
# Provide a list of supported platforms, and for each platform a list of versions.
|
||||
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
|
||||
# To view available platforms and versions (or releases), visit:
|
||||
# https://galaxy.ansible.com/api/v1/platforms/
|
||||
#
|
||||
# platforms:
|
||||
# - name: Fedora
|
||||
# versions:
|
||||
# - all
|
||||
# - 25
|
||||
# - name: SomePlatform
|
||||
# versions:
|
||||
# - all
|
||||
# - 1.0
|
||||
# - 7
|
||||
# - 99.99
|
||||
|
||||
galaxy_tags: []
|
||||
# List tags for your role here, one per line. A tag is a keyword that describes
|
||||
# and categorizes the role. Users find roles by searching for tags. Be sure to
|
||||
# remove the '[]' above, if you add tags to this list.
|
||||
#
|
||||
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
|
||||
# Maximum 20 tags per role.
|
||||
|
||||
dependencies: []
|
||||
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
|
||||
# if you add dependencies to this list.
|
@@ -1,23 +0,0 @@
|
||||
---
|
||||
- name: 'Create required plugin directories'
|
||||
ansible.builtin.file:
|
||||
path: '{{ item }}'
|
||||
state: 'directory'
|
||||
mode: '0755'
|
||||
loop:
|
||||
- '/var/lib/docker-plugins/rclone/config'
|
||||
- '/var/lib/docker-plugins/rclone/cache'
|
||||
|
||||
- name: 'Install docker plugin'
|
||||
community.docker.docker_plugin:
|
||||
plugin_name: 'rclone/docker-volume-rclone:amd64'
|
||||
alias: 'rclone'
|
||||
state: 'enable'
|
||||
plugin_options:
|
||||
args: '-v'
|
||||
|
||||
- name: 'Copy rclone config'
|
||||
ansible.builtin.copy:
|
||||
src: '{{ docker_rclone_plugin_config }}'
|
||||
dest: '/var/lib/docker-plugins/rclone/config/rclone.conf'
|
||||
mode: '0644'
|
@@ -1,8 +1,8 @@
|
||||
---
|
||||
# defaults file for eget
|
||||
eget_version: '1.3.4'
|
||||
eget_download_url: 'https://github.com/zyedidia/eget/releases/download/v{{ eget_version }}/eget-{{ eget_version }}-linux_amd64.tar.gz'
|
||||
eget_install_path: '/usr/bin/eget'
|
||||
eget_version: "1.3.4"
|
||||
eget_download_url: "https://github.com/zyedidia/eget/releases/download/v{{ eget_version }}/eget-{{ eget_version }}-linux_amd64.tar.gz"
|
||||
eget_install_path: "/usr/bin/eget"
|
||||
|
||||
eget_download_dest: '/tmp/{{ eget_download_url | split("/") | last }}'
|
||||
eget_unarchive_dest: '{{ eget_download_dest | regex_replace("(\.tar\.gz|\.zip)$", "") }}'
|
||||
|
@@ -1,6 +1,7 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: 'Anton Vakhrushev'
|
||||
description: 'Role for installation eget utility'
|
||||
author: "Anton Vakhrushev"
|
||||
description: "Role for installation eget utility"
|
||||
|
||||
# If the issue tracker for your role is not on github, uncomment the
|
||||
# next line and provide a value
|
||||
@@ -13,9 +14,9 @@ galaxy_info:
|
||||
# - GPL-3.0-only
|
||||
# - Apache-2.0
|
||||
# - CC-BY-4.0
|
||||
license: 'MIT'
|
||||
license: "MIT"
|
||||
|
||||
min_ansible_version: '2.1'
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
# If this a Container Enabled role, provide the minimum Ansible Container version.
|
||||
# min_ansible_container_version:
|
||||
|
@@ -1,30 +1,30 @@
|
||||
---
|
||||
- name: 'Download eget from url "{{ eget_download_url }}"'
|
||||
ansible.builtin.get_url:
|
||||
url: '{{ eget_download_url }}'
|
||||
dest: '{{ eget_download_dest }}'
|
||||
mode: '0600'
|
||||
url: "{{ eget_download_url }}"
|
||||
dest: "{{ eget_download_dest }}"
|
||||
mode: "0600"
|
||||
|
||||
- name: 'Unarchive eget'
|
||||
- name: "Unarchive eget"
|
||||
ansible.builtin.unarchive:
|
||||
src: '{{ eget_download_dest }}'
|
||||
dest: '/tmp'
|
||||
src: "{{ eget_download_dest }}"
|
||||
dest: "/tmp"
|
||||
list_files: true
|
||||
remote_src: true
|
||||
|
||||
- name: 'Install eget binary'
|
||||
- name: "Install eget binary"
|
||||
ansible.builtin.copy:
|
||||
src: '{{ (eget_unarchive_dest, "eget") | path_join }}'
|
||||
dest: '{{ eget_install_path }}'
|
||||
mode: '0755'
|
||||
dest: "{{ eget_install_path }}"
|
||||
mode: "0755"
|
||||
remote_src: true
|
||||
|
||||
- name: 'Remove temporary files'
|
||||
- name: "Remove temporary files"
|
||||
ansible.builtin.file:
|
||||
path: '{{ eget_download_dest }}'
|
||||
path: "{{ eget_download_dest }}"
|
||||
state: absent
|
||||
|
||||
- name: 'Remove temporary directories'
|
||||
- name: "Remove temporary directories"
|
||||
ansible.builtin.file:
|
||||
path: '{{ eget_unarchive_dest }}'
|
||||
path: "{{ eget_unarchive_dest }}"
|
||||
state: absent
|
||||
|
@@ -1,24 +1,24 @@
|
||||
---
|
||||
# tasks file for eget
|
||||
|
||||
- name: 'Check if eget installed'
|
||||
- name: "Check if eget installed"
|
||||
ansible.builtin.command:
|
||||
cmd: '{{ eget_install_path }} --version'
|
||||
cmd: "{{ eget_install_path }} --version"
|
||||
register: eget_installed_output
|
||||
ignore_errors: true
|
||||
changed_when: false
|
||||
|
||||
- name: 'Check eget installed version'
|
||||
- name: "Check eget installed version"
|
||||
ansible.builtin.set_fact:
|
||||
eget_need_install: '{{ not (eget_installed_output.rc == 0 and eget_version in eget_installed_output.stdout) }}'
|
||||
eget_need_install: "{{ not (eget_installed_output.rc == 0 and eget_version in eget_installed_output.stdout) }}"
|
||||
|
||||
- name: 'Assert that installation flag is defined'
|
||||
- name: "Assert that installation flag is defined"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- eget_need_install is defined
|
||||
- eget_need_install is boolean
|
||||
|
||||
- name: 'Download eget and install eget'
|
||||
- name: "Download eget and install eget"
|
||||
ansible.builtin.include_tasks:
|
||||
file: 'install.yml'
|
||||
file: "install.yml"
|
||||
when: eget_need_install
|
||||
|
@@ -1,4 +0,0 @@
|
||||
---
|
||||
netdata_version: 'v2.0.0'
|
||||
netdata_image: 'netdata/netdata:{{ netdata_version }}'
|
||||
netdata_exposed_port: '19999'
|
@@ -1,36 +0,0 @@
|
||||
---
|
||||
- name: 'Grab docker group id.'
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
grep docker /etc/group | cut -d ':' -f 3
|
||||
executable: /bin/bash
|
||||
register: netdata_docker_group_output
|
||||
changed_when: netdata_docker_group_output.rc != 0
|
||||
|
||||
- name: 'Create NetData container from {{ netdata_image }}'
|
||||
community.docker.docker_container:
|
||||
name: netdata
|
||||
image: '{{ netdata_image }}'
|
||||
image_name_mismatch: 'recreate'
|
||||
restart_policy: 'always'
|
||||
published_ports:
|
||||
- '127.0.0.1:{{ netdata_exposed_port }}:19999'
|
||||
volumes:
|
||||
- '/:/host/root:ro,rslave'
|
||||
- '/etc/group:/host/etc/group:ro'
|
||||
- '/etc/localtime:/etc/localtime:ro'
|
||||
- '/etc/os-release:/host/etc/os-release:ro'
|
||||
- '/etc/passwd:/host/etc/passwd:ro'
|
||||
- '/proc:/host/proc:ro'
|
||||
- '/run/dbus:/run/dbus:ro'
|
||||
- '/sys:/host/sys:ro'
|
||||
- '/var/log:/host/var/log:ro'
|
||||
- '/var/run/docker.sock:/var/run/docker.sock:ro'
|
||||
capabilities:
|
||||
- 'SYS_PTRACE'
|
||||
- 'SYS_ADMIN'
|
||||
security_opts:
|
||||
- 'apparmor:unconfined'
|
||||
env:
|
||||
PGID: '{{ netdata_docker_group_output.stdout | default(999) }}'
|
@@ -1,5 +1,6 @@
|
||||
---
|
||||
owner_name: ''
|
||||
owner_group: '{{ owner_name }}'
|
||||
owner_name: ""
|
||||
owner_group: "{{ owner_name }}"
|
||||
owner_extra_groups: []
|
||||
owner_ssh_keys: []
|
||||
owner_env: {}
|
||||
|
@@ -1,60 +1,51 @@
|
||||
---
|
||||
- name: 'Check app requirements for user "{{ owner_name }}".'
|
||||
fail:
|
||||
ansible.builtin.fail:
|
||||
msg: You must set owner name.
|
||||
when: not owner_name
|
||||
|
||||
- name: 'Create group "{{ owner_group }}".'
|
||||
group:
|
||||
name: '{{ owner_group }}'
|
||||
ansible.builtin.group:
|
||||
name: "{{ owner_group }}"
|
||||
state: present
|
||||
|
||||
- name: 'Create user "{{ owner_name }}".'
|
||||
user:
|
||||
name: '{{ owner_name }}'
|
||||
group: '{{ owner_group }}'
|
||||
groups: '{{ owner_extra_groups }}'
|
||||
ansible.builtin.user:
|
||||
name: "{{ owner_name }}"
|
||||
group: "{{ owner_group }}"
|
||||
groups: "{{ owner_extra_groups }}"
|
||||
shell: /bin/bash
|
||||
register: uc_result
|
||||
register: user_create_result
|
||||
|
||||
- name: 'Set up user ssh keys for user "{{ owner_name }}".'
|
||||
authorized_key:
|
||||
user: '{{ owner_name }}'
|
||||
key: '{{ item }}'
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ owner_name }}"
|
||||
key: "{{ item }}"
|
||||
state: present
|
||||
with_items: '{{ owner_ssh_keys }}'
|
||||
with_items: "{{ owner_ssh_keys }}"
|
||||
when: owner_ssh_keys | length > 0
|
||||
|
||||
- name: 'Prepare env variables.'
|
||||
set_fact:
|
||||
env_dict: '{{ owner_env | combine({
|
||||
"CURRENT_UID": uc_result.uid | default(owner_name),
|
||||
"CURRENT_GID": uc_result.group | default(owner_group) }) }}'
|
||||
tags:
|
||||
- env
|
||||
- name: "Prepare env variables."
|
||||
ansible.builtin.set_fact:
|
||||
env_dict: '{{ owner_env | combine({"USER_UID": user_create_result.uid, "USER_GID": user_create_result.group}) }}'
|
||||
|
||||
- name: 'Set up environment variables for user "{{ owner_name }}".'
|
||||
template:
|
||||
ansible.builtin.template:
|
||||
src: env.j2
|
||||
dest: '/home/{{ owner_name }}/.env'
|
||||
owner: '{{ owner_name }}'
|
||||
group: '{{ owner_group }}'
|
||||
tags:
|
||||
- env
|
||||
dest: "/home/{{ owner_name }}/.env"
|
||||
owner: "{{ owner_name }}"
|
||||
group: "{{ owner_group }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: 'Remove absent environment variables for user "{{ owner_name }}" from bashrc.'
|
||||
lineinfile:
|
||||
path: '/home/{{ owner_name }}/.bashrc'
|
||||
regexp: '^export {{ item.key }}='
|
||||
- name: 'Remove from bashrc absent environment variables for user "{{ owner_name }}".'
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/home/{{ owner_name }}/.bashrc"
|
||||
regexp: "^export {{ item.key }}="
|
||||
state: absent
|
||||
with_dict: '{{ env_dict }}'
|
||||
tags:
|
||||
- env
|
||||
with_dict: "{{ env_dict }}"
|
||||
|
||||
- name: 'Include environment variables for user "{{ owner_name }}" in bashrc.'
|
||||
lineinfile:
|
||||
path: '/home/{{ owner_name }}/.bashrc'
|
||||
regexp: '^export \$\(grep -v'
|
||||
- name: 'Include in bashrc environment variables for user "{{ owner_name }}".'
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/home/{{ owner_name }}/.bashrc"
|
||||
regexp: "^export \\$\\(grep -v"
|
||||
line: 'export $(grep -v "^#" "$HOME"/.env | xargs)'
|
||||
tags:
|
||||
- env
|
||||
|
55
roles/secrets/README.md
Normal file
55
roles/secrets/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Secrets Role
|
||||
|
||||
Ansible роль для сохранения секретов на удаленной машине.
|
||||
|
||||
## Описание
|
||||
|
||||
Роль позволяет безопасно сохранять секреты на удаленной машине в виде файлов с ограниченными правами доступа. Поддерживает сохранение переменных как файлов и копирование существующих файлов.
|
||||
|
||||
## Параметры
|
||||
|
||||
### Обязательные параметры
|
||||
|
||||
- `secrets_dest` - директория, куда будут сохранены секреты в виде файлов
|
||||
- `secrets_user` - имя пользователя, который будет владеть директорией и файлами
|
||||
- `secrets_group` - группа, которая будет владеть директорией и файлами (по умолчанию равна `secrets_user`)
|
||||
|
||||
### Опциональные параметры
|
||||
|
||||
- `secrets_vars` - список переменных, из которых будут извлечены секреты (по умолчанию: `[]`)
|
||||
- `secrets_files` - список файлов, которые будут скопированы "как есть" (по умолчанию: `[]`)
|
||||
- `secrets_dir_mode` - права доступа для директории (по умолчанию: `0750`)
|
||||
- `secrets_file_mode` - права доступа для файлов с секретами (по умолчанию: `0400`)
|
||||
|
||||
## Функциональность
|
||||
|
||||
1. Создает директорию `secrets_dest` если она не существует
|
||||
2. Сохраняет каждую переменную из `secrets_vars` как файл с таким же именем
|
||||
3. Копирует файлы из `secrets_files` в директорию назначения
|
||||
4. Устанавливает права доступа `0400` для всех файлов с секретами
|
||||
5. Назначает указанного пользователя и группу владельцами директории и файлов
|
||||
|
||||
## Пример использования
|
||||
|
||||
```yaml
|
||||
- name: Save application secrets
|
||||
include_role:
|
||||
name: secrets
|
||||
vars:
|
||||
secrets_dest: /opt/myapp/secrets
|
||||
secrets_user: myapp
|
||||
secrets_group: myapp
|
||||
secrets_vars:
|
||||
- database_password
|
||||
- api_key
|
||||
- jwt_secret
|
||||
secrets_files:
|
||||
- /path/to/ssl/certificate.pem
|
||||
- /path/to/ssl/private.key
|
||||
```
|
||||
|
||||
## Безопасность
|
||||
|
||||
- Все файлы с секретами создаются с правами `0400` (только чтение для владельца)
|
||||
- Директория создается с правами `0750` (полный доступ для владельца, чтение и выполнение для группы)
|
||||
- Используется `no_log: true` для предотвращения вывода секретов в логи Ansible
|
9
roles/secrets/defaults/main.yml
Normal file
9
roles/secrets/defaults/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
# Default variables for secrets role
|
||||
secrets_dest: ""
|
||||
secrets_user: ""
|
||||
secrets_group: "{{ secrets_user }}"
|
||||
secrets_vars: []
|
||||
secrets_files: []
|
||||
secrets_dir_mode: "0750"
|
||||
secrets_file_mode: "0400"
|
22
roles/secrets/meta/main.yml
Normal file
22
roles/secrets/meta/main.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: 'Anton Vakhrushev'
|
||||
description: Ansible role for saving secrets on remote machine
|
||||
license: MIT
|
||||
min_ansible_version: '2.9'
|
||||
platforms:
|
||||
- name: Ubuntu
|
||||
versions:
|
||||
- all
|
||||
- name: Debian
|
||||
versions:
|
||||
- all
|
||||
- name: EL
|
||||
versions:
|
||||
- all
|
||||
galaxy_tags:
|
||||
- secrets
|
||||
- security
|
||||
- files
|
||||
|
||||
dependencies: []
|
46
roles/secrets/tasks/main.yml
Normal file
46
roles/secrets/tasks/main.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
# tasks file for secrets role
|
||||
|
||||
- name: "Validate secrets_dest parameter"
|
||||
ansible.builtin.fail:
|
||||
msg: "secrets_dest is required but not defined"
|
||||
when: secrets_dest is not defined or secrets_dest == ""
|
||||
|
||||
- name: "Validate secrets_user parameter"
|
||||
ansible.builtin.fail:
|
||||
msg: "secrets_user is required but not defined"
|
||||
when: secrets_user is not defined or secrets_user == ""
|
||||
|
||||
- name: "Validate secrets_group parameter"
|
||||
ansible.builtin.fail:
|
||||
msg: "secrets_group is required but not defined"
|
||||
when: secrets_group is not defined or secrets_group == ""
|
||||
|
||||
- name: "Create secrets destination directory"
|
||||
ansible.builtin.file:
|
||||
path: "{{ secrets_dest }}"
|
||||
state: directory
|
||||
owner: "{{ secrets_user }}"
|
||||
group: "{{ secrets_group }}"
|
||||
mode: "{{ secrets_dir_mode }}"
|
||||
|
||||
- name: "Save variables as secret files"
|
||||
ansible.builtin.copy:
|
||||
content: "{{ lookup('vars', item) }}"
|
||||
dest: "{{ secrets_dest }}/{{ item }}"
|
||||
owner: "{{ secrets_user }}"
|
||||
group: "{{ secrets_group }}"
|
||||
mode: "{{ secrets_file_mode }}"
|
||||
loop: "{{ secrets_vars }}"
|
||||
when: secrets_vars | length > 0
|
||||
no_log: true
|
||||
|
||||
- name: "Copy secret files"
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ secrets_dest }}/{{ item | basename }}"
|
||||
owner: "{{ secrets_user }}"
|
||||
group: "{{ secrets_group }}"
|
||||
mode: "{{ secrets_file_mode }}"
|
||||
loop: "{{ secrets_files }}"
|
||||
when: secrets_files | length > 0
|
@@ -1,11 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
SMTP Password Generator for Yandex Cloud Postbox
|
||||
|
||||
Этот скрипт конвертирует Secret Access Key в SMTP пароль для использования
|
||||
с Yandex Cloud Postbox сервисом. Скрипт реализует алгоритм AWS4 подписи
|
||||
для генерации корректного SMTP пароля.
|
||||
|
||||
Yandex Cloud Postbox использует AWS-совместимый API, и для SMTP аутентификации
|
||||
требуется специальный пароль, который генерируется из Secret Access Key
|
||||
с использованием определенного алгоритма подписи.
|
||||
|
||||
Примеры использования:
|
||||
|
||||
python3 smtp-convert-secret-key-to-password.py "YourSecretAccessKey123"
|
||||
|
||||
Требования:
|
||||
- Python 3.x
|
||||
- Стандартные библиотеки: hmac, hashlib, base64, argparse, sys
|
||||
|
||||
Автор: Yandex.Cloud Team
|
||||
Ссылка: https://yandex.cloud/ru/docs/postbox/operations/send-email
|
||||
"""
|
||||
|
||||
import hmac
|
||||
import hashlib
|
||||
import base64
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
||||
# These values are required to calculate the signature. Do not change them.
|
||||
DATE = "20230926"
|
||||
SERVICE = "postbox"
|
70
scripts/smtp-send-test-email.py
Normal file
70
scripts/smtp-send-test-email.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
SMTP Test Email Sender for Yandex Cloud Postbox
|
||||
|
||||
Скрипт для отправки тестового email через Yandex Cloud Postbox SMTP сервер.
|
||||
Используется для проверки корректности настроек SMTP аутентификации.
|
||||
|
||||
Примеры использования:
|
||||
python3 smtp-send-test-email.py --login "your-login" --password "smtp-password" --to "recipient@example.com"
|
||||
"""
|
||||
|
||||
import smtplib
|
||||
import argparse
|
||||
import sys
|
||||
from email.message import EmailMessage
|
||||
|
||||
|
||||
def send_test_email(login, password, to_email):
|
||||
"""Отправляет тестовое email через Yandex Cloud Postbox SMTP"""
|
||||
|
||||
# initialize connection to our email server
|
||||
smtp = smtplib.SMTP("postbox.cloud.yandex.net", port="587")
|
||||
|
||||
smtp.set_debuglevel(2)
|
||||
|
||||
smtp.ehlo() # send the extended hello to our server
|
||||
smtp.starttls() # tell server we want to communicate with TLS encryption
|
||||
|
||||
smtp.login(login, password) # login to our email server
|
||||
|
||||
message = EmailMessage()
|
||||
message.set_content("Hello, world! This is a test email from Yandex Cloud Postbox.")
|
||||
message.add_header("From", "service@vakhrushev.me")
|
||||
message.add_header("To", to_email)
|
||||
message.add_header("Subject", "Test Email from Yandex Cloud Postbox")
|
||||
|
||||
# send our email message
|
||||
smtp.send_message(message)
|
||||
|
||||
smtp.quit() # finally, don't forget to close the connection
|
||||
|
||||
print(f"Test email successfully sent to {to_email}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Send a test email via Yandex Cloud Postbox SMTP server."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--login", required=True, help="SMTP login (usually your email address)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--password",
|
||||
required=True,
|
||||
help="SMTP password (generated using smtp-convert-secret-key-to-password.py)",
|
||||
)
|
||||
parser.add_argument("--to", required=True, help="Recipient email address")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
send_test_email(args.login, args.password, args.to)
|
||||
except Exception as e:
|
||||
print(f"Error sending email: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
57
tasks.py
57
tasks.py
@@ -1,57 +0,0 @@
|
||||
import os
|
||||
import shlex
|
||||
import fabric
|
||||
from invoke import task
|
||||
|
||||
SERVER_HOST_FILE = "hosts_prod"
|
||||
DOKER_REGISTRY = "cr.yandex/crplfk0168i4o8kd7ade"
|
||||
|
||||
|
||||
@task(name="deploy:gitea")
|
||||
def deploy_gitea(context):
|
||||
deploy("gitea", dirs=["data"])
|
||||
|
||||
|
||||
@task(name="deploy:keycloak")
|
||||
def deploy_keykloak(context):
|
||||
deploy("keycloak", compose_file="docker-compose.prod.yml", dirs=["data"])
|
||||
|
||||
|
||||
@task(name="deploy:outline")
|
||||
def deploy_outline(context):
|
||||
deploy("outline", compose_file="docker-compose.prod.yml", dirs=["data/postgres"])
|
||||
|
||||
|
||||
def read_host():
|
||||
with open(SERVER_HOST_FILE) as f:
|
||||
return f.read().strip()
|
||||
|
||||
|
||||
def ssh_host(app_name):
|
||||
return f"{app_name}@{read_host()}"
|
||||
|
||||
|
||||
def deploy(app_name: str, compose_file="docker-compose.yml", dirs=None):
|
||||
docker_compose = os.path.join("app", app_name, compose_file)
|
||||
assert os.path.exists(docker_compose)
|
||||
conn_str = ssh_host(app_name)
|
||||
dirs = dirs or []
|
||||
print("Deploy app from", docker_compose)
|
||||
print("Start setup remote host", conn_str)
|
||||
with fabric.Connection(conn_str) as c:
|
||||
print("Copy docker compose file to remote host")
|
||||
c.put(
|
||||
local=docker_compose,
|
||||
remote=f"/home/{app_name}/docker-compose.yml",
|
||||
)
|
||||
print("Copy environment file")
|
||||
c.run("cp .env .env.prod")
|
||||
for d in dirs:
|
||||
print("Create remote directory", d)
|
||||
c.run(f"mkdir -p {d}")
|
||||
print("Up services")
|
||||
c.run(
|
||||
f"docker compose --project-name {shlex.quote(app_name)} --env-file=.env.prod up --detach --remove-orphans"
|
||||
)
|
||||
c.run(f"docker system prune --all --volumes --force")
|
||||
print("Done.")
|
@@ -1,75 +0,0 @@
|
||||
# -------------------------------------------------------------------
|
||||
# Global options
|
||||
# -------------------------------------------------------------------
|
||||
{
|
||||
grace_period 15s
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Netdata service
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
status.vakhrushev.me, :29999 {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to 127.0.0.1:{{ netdata_port }}
|
||||
}
|
||||
|
||||
basicauth / {
|
||||
{{ netdata.login }} {{ netdata.password_hash }}
|
||||
}
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Applications
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to 127.0.0.1:{{ homepage_port }}
|
||||
}
|
||||
}
|
||||
|
||||
git.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to 127.0.0.1:{{ gitea_port }}
|
||||
}
|
||||
}
|
||||
|
||||
kk.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to 127.0.0.1:{{ keycloak_port }}
|
||||
}
|
||||
}
|
||||
|
||||
outline.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to 127.0.0.1:{{ outline_port }}
|
||||
}
|
||||
}
|
||||
|
||||
music.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to 127.0.0.1:{{ navidrome_port }}
|
||||
}
|
||||
}
|
||||
|
||||
gramps.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
|
||||
reverse_proxy {
|
||||
to 127.0.0.1:{{ gramps_port }}
|
||||
}
|
||||
}
|
||||
}
|
7
vars/homepage.yml
Normal file
7
vars/homepage.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
app_name: "homepage"
|
||||
app_user: "{{ app_name }}"
|
||||
base_dir: "/home/{{ app_user }}"
|
||||
docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade"
|
||||
|
||||
homepage_web_image: "{{ homepage_web_image | default(omit) }}"
|
@@ -1,14 +1,7 @@
|
||||
---
|
||||
base_port: 41080
|
||||
notes_port: "{{ base_port + 1 }}"
|
||||
dayoff_port: "{{ base_port + 2 }}"
|
||||
homepage_port: "{{ base_port + 3 }}"
|
||||
netdata_port: "{{ base_port + 4 }}"
|
||||
wiki_port: "{{ base_port + 5 }}"
|
||||
nomie_port: "{{ base_port + 6 }}"
|
||||
nomie_db_port: "{{ base_port + 7 }}"
|
||||
gitea_port: "{{ base_port + 8 }}"
|
||||
keycloak_port: "{{ base_port + 9 }}"
|
||||
outline_port: "{{ base_port + 10 }}"
|
||||
navidrome_port: "{{ base_port + 11 }}"
|
||||
gramps_port: "{{ base_port + 12 }}"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user