6 Commits

Author SHA1 Message Date
Tuan-Dat Tran
27a002d608 fix(ubuntu): Fixed pipefile sets (wip)
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-03-16 11:16:43 +01:00
Tuan-Dat Tran
bb8a4560e5 feat(ubuntu): Added jdk)
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-02-18 08:04:49 +01:00
Tuan-Dat Tran
39a2925bcd feat(ubuntu): Refactor package installation, streamline GitHub deb installs
- Added 'become_password_file' to 'ansible.cfg' for privilege escalation handling.
- Removed separate installation tasks for 'atuin', 'eurkey', 'ghostty', 'git-delta', 'ripgrep', 'starship', 'veracrypt', and 'pacstall', consolidating them into 'curl.yml' and 'git_deb.yml'.
- Introduced 'git_deb.yml' for handling GitHub '.deb' installations dynamically using 'github_deb' variable.
- Improved error handling and pipe safety in curl-based installations ('set -o pipefail').
- Set proper permissions ('mode: 0600') for downloaded files in 'remove_ubuntu_banner.yml' and 'fira_code_fonts.yml'.
- Refactored 'github_releases.yml' to allow optional 'v' prefix handling in 'tag_name'.
- Updated 'main.yml' to remove redundant installations and streamline execution.
- Defined 'github_deb' list in 'vars/main.yml' to manage '.deb' package downloads dynamically.

These changes enhance maintainability, reduce redundancy, and improve package installation flexibility.

Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-02-17 18:19:08 +01:00
Tuan-Dat Tran
f4a322ed5d feat(ubuntu): Install github releases dynamically
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-02-12 23:35:02 +01:00
Tuan-Dat Tran
6dc7e5ac27 Added ubuntu setup
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-02-10 21:33:40 +01:00
Tuan-Dat Tran
09bbc04959 feat(docker): Added elasticsearch and kibana, need ssl cert and logstash
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
2025-02-07 09:39:48 +01:00
160 changed files with 1661 additions and 2063 deletions

View File

@@ -1,31 +0,0 @@
---
# .ansible-lint
# Specify exclude paths to prevent linting vendor roles, etc.
exclude_paths:
- ./.git/
- ./.venv/
- ./galaxy_roles/
# A list of rules to skip. This is a more modern and readable alternative to 'skip_list'.
skip_list:
- experimental
- fqcn-builtins
- no-handler
- var-naming
# Enforce certain rules that are not enabled by default.
enable_list:
- no-free-form
- var-spacing
- no-log-password
- no-relative-path
- command-instead-of-module
- fqcn[deep]
- no-changed-when
# Offline mode disables any features that require internet access.
offline: true
# Set the desired verbosity level.
verbosity: 1

View File

@@ -1,17 +0,0 @@
root = true
[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{yml,yaml}]
indent_size = 2
[*.py]
indent_size = 4
[*.md]
trim_trailing_whitespace = false

View File

@@ -72,16 +72,3 @@ sudo vgextend k3s-vg /dev/sda3
# Use the newly available storage in the root volume
sudo lvresize -l +100%FREE -r /dev/k3s-vg/root
```
## Cloud Init VMs
```sh
# On Hypervisor Host
qm resize <vmid> scsi0 +32G
# On VM
sudo fdisk -l /dev/sda # To check
echo 1 | sudo tee /sys/class/block/sda/device/rescan
sudo fdisk -l /dev/sda # To check
# sudo apt-get install cloud-guest-utils
sudo growpart /dev/sda 1
```

31
Vagrantfile vendored Normal file
View File

@@ -0,0 +1,31 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "bento/ubuntu-24.04"
config.vm.box_version = "202404.26.0"
# Configure VM provider resources (optional)
config.vm.provider :virtualbox do |v|
v.memory = 4096
v.cpus = 2
end
config.vm.define "test" do |v|
v.vm.hostname = "test"
v.vm.network :private_network, ip: "192.168.56.123"
v.vm.provision "bootstrap", type: "shell" do |s|
s.inline = "sudo apt install ansible -y"
end
#
# Use Ansible for provisioning
v.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml" # Path to the Ansible playbook relative to the Vagrantfile
ansible.inventory_path = "inventory" # Path to the inventory file
# Extra vars can be defined if needed
# ansible.extra_vars = { some_var: "value" }
end
end
end

View File

@@ -1,17 +1,18 @@
[defaults]
# (string) Path to the Python interpreter to be used for module execution on remote targets, or an automatic discovery mode. Supported discovery modes are ``auto`` (the default), ``auto_silent``, ``auto_legacy``, and ``auto_legacy_silent``. All discovery modes employ a lookup table to use the included system Python (on distributions known to include one), falling back to a fixed ordered list of well-known Python interpreter locations if a platform-specific default is not available. The fallback behavior will issue a warning that the interpreter should be set explicitly (since interpreters installed later may change which one is used). This warning behavior can be disabled by setting ``auto_silent`` or ``auto_legacy_silent``. The value of ``auto_legacy`` provides all the same behavior, but for backwards-compatibility with older Ansible releases that always defaulted to ``/usr/bin/python``, will use that interpreter if present.
interpreter_python=python3
# (pathspec) Colon separated paths in which Ansible will search for Roles.
roles_path=./roles
# (pathlist) Comma separated list of Ansible inventory sources
inventory=./vars/
inventory=./inventory/production
# (path) The vault password file to use. Equivalent to --vault-password-file or --vault-id
# If executable, it will be run and the resulting stdout will be used as the password.
vault_password_file=/media/veracrypt1/scripts/ansible_vault.sh
# (path) The password file to use for the become plugin. --become-password-file.
# If executable, it will be run and the resulting stdout will be used as the password.
become_password_file=/media/veracrypt1/scripts/ansible_become.sh
# (list) Check all of these extensions when looking for 'variable' files which should be YAML or JSON or vaulted versions of these.
# This affects vars_files, include_vars, inventory and vars plugins among others.
yaml_valid_extensions=.yml
@@ -36,6 +37,3 @@ skip=dark gray
[tags]
# (list) default list of tags to skip in your plays, has precedence over Run Tags
;skip=
[inventory]
ignore_extensions={{(REJECT_EXTS + ('.orig', '.cfg', '.retry', '.bak'))}}

View File

@@ -0,0 +1,56 @@
$ANSIBLE_VAULT;1.1;AES256
34623331393561623539666362643966336661326136363431666465356535343663376236663066
3235363061633666626133313363373336656438633566630a383230393161323862303863656464
61633861323966343263363466343130306635343539326464363637383139343033656130336464
3163373535613961340a643335626165306663363063656339653862393533633534366331336231
63393432383731633463323164333831313535373261336166326237306230326465616239306536
37663863663161393130373835373062393866633864373465333937633838303130386334356566
64303663303862623038646235303934376230393538353466393232363764366339616633343433
65343730663864393766313134653335396562646135306637613031333461613965666465376532
32643261626665396338313836633337383932616265613662383132303539623239623965333966
66333638643635313262616434396164313833303065303662303736303232346535613834643435
32316434343231363662393163353832393166643739396165313631363539663439316133616361
61623830613035396333303363383332653736666231343763353666356539633433373066613330
65656631343764323234333161636632616130353139626362343361386535313336666566636464
35323434656439346262336335383366626565333765343562633236636132636532333761663535
31383565313436633438633336306430343733663539666631386532313836623166356332626664
39653762353265643861633237326662383466373539633732323833376238383963393837636466
66656631666131623166393731643537393161303636353932653062363137376334356238643064
34303666656638396263336639636135393536623037666137653132633264316431656438386432
34333632616265343435306365373039653036353337633563393739653632656163316636363336
32346638393364353634386231616639386164326531353134366639653837653236333030666139
64656334336231636337656233383834343763393738643362626665333362353335656131653165
35376330336433383262653039643131313437643265343663626363373439643932643063646439
37663630363839643263373630646430386536346132383564396463376361343661346661333636
39643961643031626462363537633263393838363262626439313838313039373035373634633462
38363938343932626131343966616638323632303636383034383536616164393539343635666166
39383434313863356434383961383139623436636230323866396366326665623863336438623335
33346634303639643131333933363838666336306438646335343931366437326462376438663837
34353938343837663930356464373332356530643231653166616331376335643832316365303164
32393062313638393936393863613731363233376537323834623164613231393133353635623866
35626337336562653265613730363961633662653331663966333430343462666535306133663835
64663539303765366331613666653632313233626231313264346332323266653230323332373836
33303564633464333064613431383230383535633362373839323334353162623433646230393838
33306162613739393338373361616634396636313765326465393332396537613263383339626666
63613162616363363138323965373966353366323463313934356530663931653565656164346363
37633862366436623030303233396639393434336438623433383530393836626164353064366432
35303532393437316162346366346636633135383938323631316563323935383561326335323438
30613266643232656138663431666162663330643133643263343237663565323231316239633037
39323732386236396136633539383335646634306139643533666636633131623566333137376236
39616134306463613864353135313636343365643437323465643862303137663937376233306261
31383862356535646563383438396363323838613237623034656561396163376433663262366137
63323562346633303162666530616534386539383238366139376263326265343138373139393432
35643335363139373139666230626363386232316536306431653964376333366235303763336135
65623231336638643034373932376263636336653561646664366138643031316438316465353363
38386539363631393433313664323135646562313537376236653635303263633230383866653039
66636534336234363438363139366531653237323137613961383831376665626365393462363834
36333965366463636233643433616431376436323535396238363933326363333661326462353161
66626435373938633832393662313161663336613862343332643766333633653866316464653735
31356135363662633961386264613836323435323836386635336338353663333137336666323531
36663731336664633763633634613136663866363530613264356431326539316530326161313362
62616539356537353261343464356334636134396664353463623163313765633432653932346136
32326239373333643461333733646264353238356134613037663836643131316664653539643839
30613235623933356565336630323939633266613164306262386666363137666661666131613962
61623930663536646462343264336535353634373833316537613839396566376466653736333830
33376663613063326230346439626237373232656665633832373364653931663361666432303166
663564323132383864336332363139393534

36
group_vars/all/vars.yml Normal file
View File

@@ -0,0 +1,36 @@
#
# Essential
#
root: root
user: tudattr
timezone: Europe/Berlin
puid: "1000"
pgid: "1000"
pk_path: "/media/veracrypt1/genesis"
pubkey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKqc9fnzfCz8fQDFzla+D8PBhvaMmFu2aF+TYkkZRxl9 tuan@genesis-2022-01-20"
public_domain: tudattr.dev
internal_domain: seyshiro.de
#
# Packages
#
common_packages:
- build-essential
- curl
- git
- iperf3
- neovim
- rsync
- smartmontools
- sudo
- systemd-timesyncd
- tree
- screen
- bat
- fd-find
- ripgrep
arch: "{{ 'arm64' if ansible_architecture == 'aarch64' else 'amd64' }}"

View File

@@ -2,37 +2,73 @@ docker:
url: "https://download.docker.com/linux"
apt_release_channel: "stable"
directories:
local: "/opt/local/"
config: "/opt/docker/config/"
compose: "/opt/docker/compose/"
media: "/media/docker/data/"
caddy:
admin_email: me+acme@tudattr.dev
domain: "seyshiro.de"
elk_version: 8.17.0
services:
- name: syncthing
vm:
- docker-host00
container_name: syncthing
image: syncthing/syncthing
restart: unless-stopped
volumes:
- name: "Data"
internal: /var/syncthing/
external: /media/docker/data/syncthing/
ports:
- name: "http"
internal: 8384
external: 8384
- name: ""
internal: 22000
external: 22000
- name: ""
internal: 22000
external: 22000
- name: ""
internal: 21027
external: 21027
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- name: status
vm:
- docker-host12
- docker-host00
container_name: kuma
image: louislam/uptime-kuma:1.23.16
image: louislam/uptime-kuma:1
restart: unless-stopped
volumes:
- name: "Data"
internal: /app/data
external: "{{ docker.directories.local }}/kuma/"
external: /opt/local/kuma/
ports:
- name: "http"
internal: 3001
external: "{{ services_external_http.kuma }}"
external: 3001
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- name: plex
vm:
- docker-host10
- docker-host00
container_name: plex
image: lscr.io/linuxserver/plex:1.41.5
image: lscr.io/linuxserver/plex:latest
restart: unless-stopped
volumes:
- name: "Configuration"
internal: /config
external: "{{ docker.directories.local }}/plex/config/"
external: /opt/local/plex/config/
- name: "TV Series"
internal: /tv:ro
external: /media/series
@@ -49,7 +85,7 @@ services:
ports:
- name: "http"
internal: 32400
external: "{{ services_external_http.plex }}"
external: 32400
- name: ""
internal: 1900
external: 1900
@@ -78,13 +114,14 @@ services:
- VERSION=docker
- name: jellyfin
vm:
- docker-host01
- docker-host02
container_name: jellyfin
image: jellyfin/jellyfin:10.10
image: jellyfin/jellyfin
restart: "unless-stopped"
volumes:
- name: "Configuration"
internal: /config
external: "{{ docker.directories.local }}/jellyfin/config"
external: /opt/local/jellyfin/config
- name: "Cache"
internal: /cache
external: "{{ docker.directories.config }}/jellyfin/cache"
@@ -104,25 +141,26 @@ services:
ports:
- name: "http"
internal: 8096
external: "{{ services_external_http.jellyfin }}"
external: 8096
environment:
- name: hass
vm:
- docker-host01
- docker-host02
container_name: homeassistant
image: "ghcr.io/home-assistant/home-assistant:stable"
restart: unless-stopped
privileged: true
volumes:
- name: "Configuration"
internal: /config/
external: "{{ docker.directories.local }}/home-assistant/config/"
external: /opt/local/home-assistant/config/
- name: "Local Time"
internal: /etc/localtime:ro
external: /etc/localtime
ports:
- name: "http"
internal: 8123
external: "{{ services_external_http.hass }}"
external: 8123
- name: ""
internal: 4357
external: 4357
@@ -134,26 +172,28 @@ services:
external: 5683
- name: ddns
vm:
- docker-host12
- docker-host00
container_name: ddns-updater
image: qmcgaw/ddns-updater:2
image: ghcr.io/qdm12/ddns-updater
restart: unless-stopped
volumes:
- name: "Configuration"
internal: /updater/data/
external: "{{ docker.directories.local }}/ddns-updater/data/"
internal: /updater/data/"
external: "{{ docker.directories.config }}/ddns-updater/data/"
ports:
- name: "http"
internal: 8000
external: "{{ services_external_http.ddns }}"
external: 8001
- name: sonarr
vm:
- docker-host12
- docker-host00
container_name: sonarr
image: linuxserver/sonarr:4.0.14
image: lscr.io/linuxserver/sonarr:latest
restart: unless-stopped
volumes:
- name: "Configuration"
internal: /config
external: "{{ docker.directories.local }}/sonarr/config"
external: /opt/local/sonarr/config
- name: "Tv Series"
internal: /tv
external: /media/series
@@ -163,20 +203,21 @@ services:
ports:
- name: "http"
internal: 8989
external: "{{ services_external_http.sonarr }}"
external: 8989
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- name: radarr
vm:
- docker-host12
- docker-host00
container_name: radarr
image: linuxserver/radarr:5.21.1
image: lscr.io/linuxserver/radarr:latest
restart: unless-stopped
volumes:
- name: "Configuration"
internal: /config
external: "{{ docker.directories.local }}/radarr/config"
external: /opt/local/radarr/config
- name: "Movies"
internal: /movies
external: /media/movies
@@ -186,20 +227,21 @@ services:
ports:
- name: "http"
internal: 7878
external: "{{ services_external_http.radarr }}"
external: 7878
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- name: lidarr
vm:
- docker-host12
- docker-host00
container_name: lidarr
image: linuxserver/lidarr:2.10.3
image: lscr.io/linuxserver/lidarr:latest
restart: unless-stopped
volumes:
- name: "Configuration"
internal: /config
external: "{{ docker.directories.local }}/lidarr/config"
external: /opt/local/lidarr/config
- name: "Music"
internal: /music
external: /media/songs
@@ -209,54 +251,56 @@ services:
ports:
- name: "http"
internal: 8686
external: "{{ services_external_http.lidarr }}"
external: 8686
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- name: prowlarr
vm:
- docker-host12
- docker-host00
container_name: prowlarr
image: linuxserver/prowlarr:1.32.2
image: lscr.io/linuxserver/prowlarr:latest
restart: unless-stopped
volumes:
- name: "Configuration"
internal: /config
external: "{{ docker.directories.local }}/prowlarr/config"
external: /opt/local/prowlarr/config
ports:
- name: "http"
internal: 9696
external: "{{ services_external_http.prowlarr }}"
external: 9696
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- name: paperless
vm:
- docker-host12
- docker-host00
container_name: paperless
image: ghcr.io/paperless-ngx/paperless-ngx:2.14
image: ghcr.io/paperless-ngx/paperless-ngx:latest
restart: unless-stopped
depends_on:
- paperless-postgres
- paperless-redis
- paperless-broker
volumes:
- name: "Configuration"
internal: /usr/src/paperless/data
external: "{{ docker.directories.local }}/paperless/data/data"
external: /opt/local/paperless/data/data
- name: "Media"
internal: /usr/src/paperless/media
external: "{{ docker.directories.local }}/paperless/data/media"
external: /opt/local/paperless/data/media
- name: "Document Export"
internal: /usr/src/paperless/export
external: "{{ docker.directories.local }}/paperless/data/export"
external: /opt/local/paperless/data/export
- name: "Document Consume"
internal: /usr/src/paperless/consume
external: "{{ docker.directories.local }}/paperless/data/consume"
external: /opt/local/paperless/data/consume
environment:
- "PAPERLESS_REDIS=redis://paperless-redis:6379"
- "PAPERLESS_REDIS=redis://paperless-broker:6379"
- "PAPERLESS_DBHOST=paperless-postgres"
- "PAPERLESS_DBUSER=paperless"
- "PAPERLESS_DBPASS={{ vault_docker.paperless.dbpass }}"
- "PAPERLESS_DBPASS={{ vault.docker.paperless.dbpass }}"
- "USERMAP_UID=1000"
- "USERMAP_GID=1000"
- "PAPERLESS_URL=https://paperless.{{ domain }}"
@@ -265,35 +309,30 @@ services:
ports:
- name: "http"
internal: 8000
external: "{{ services_external_http.paperless }}"
sub_service:
- name: postgres
version: 15
username: paperless
password: "{{ vault_docker.paperless.dbpass }}"
- name: redis
version: 7
external: 8000
- name: pdf
vm:
- docker-host12
- docker-host00
container_name: stirling
image: frooodle/s-pdf:0.45.0
image: frooodle/s-pdf:latest
restart: unless-stopped
ports:
- name: "http"
internal: 8080
external: "{{ services_external_http.pdf }}"
external: 8080
- name: git
vm:
- docker-host01
- docker-host02
container_name: gitea
image: gitea/gitea:1.23-rootless
image: gitea/gitea:1.23.1-rootless
restart: unless-stopped
volumes:
- name: "Configuration"
internal: /etc/gitea
external: "{{ docker.directories.local }}/gitea/config"
external: /opt/local/gitea/config
- name: "Data"
internal: /var/lib/gitea
external: "{{ docker.directories.local }}/gitea/data"
external: /opt/local/gitea/data
- name: "Time Zone"
internal: /etc/timezone:ro
external: /etc/timezone
@@ -303,7 +342,7 @@ services:
ports:
- name: "http"
internal: 3000
external: "{{ services_external_http.git }}"
external: 3000
- name: "ssh"
internal: 2222
external: 2222
@@ -312,23 +351,24 @@ services:
- USER_GID=1000
- name: changedetection
vm:
- docker-host12
- docker-host00
container_name: changedetection
image: dgtlmoon/changedetection.io:0.49
healthcheck: curl
image: dgtlmoon/changedetection.io
restart: unless-stopped
volumes:
- name: "Data"
internal: /datastore
external: "{{ docker.directories.local }}/changedetection/data/"
external: "{{ docker.directories.config }}/changedetection/data/"
ports:
- name: "http"
internal: 5000
external: "{{ services_external_http.changedetection }}"
external: 5000
- name: gluetun
vm:
- docker-host12
- docker-host00
container_name: gluetun
image: qmcgaw/gluetun:v3.40
image: qmcgaw/gluetun
restart: unless-stopped
cap_add:
- NET_ADMIN
devices:
@@ -338,7 +378,7 @@ services:
volumes:
- name: "Configuration"
internal: /gluetun
external: "{{ docker.directories.local }}/gluetun/config"
external: "{{ docker.directories.config }}/gluetun/config"
ports:
- name: "Qbit Client"
internal: 8082
@@ -353,21 +393,22 @@ services:
- VPN_SERVICE_PROVIDER=protonvpn
- UPDATER_VPN_SERVICE_PROVIDERS=protonvpn
- UPDATER_PERIOD=24h
- "SERVER_COUNTRIES={{ vault_docker.proton.country }}"
- "OPENVPN_USER={{ vault_docker.proton.openvpn_user }}"
- "OPENVPN_PASSWORD={{ vault_docker.proton.openvpn_password }}"
- "SERVER_COUNTRIES={{ vault.docker.proton.country }}"
- "OPENVPN_USER={{ vault.docker.proton.openvpn_user }}"
- "OPENVPN_PASSWORD={{ vault.docker.proton.openvpn_password }}"
- name: torrentleech
vm:
- docker-host12
- docker-host00
container_name: torrentleech
image: qbittorrentofficial/qbittorrent-nox
restart: unless-stopped
depends_on:
- gluetun
network_mode: "container:gluetun"
volumes:
- name: "Configuration"
internal: /config
external: "{{ docker.directories.local }}/torrentleech/config"
external: "{{ docker.directories.config }}/torrentleech/config"
- name: "Downloads"
internal: /downloads
external: /media/docker/data/arr_downloads
@@ -383,16 +424,17 @@ services:
- QBT_WEBUI_PORT="8083"
- name: qbit
vm:
- docker-host12
- docker-host00
container_name: qbit
image: qbittorrentofficial/qbittorrent-nox:5.0.4-1
image: qbittorrentofficial/qbittorrent-nox
restart: unless-stopped
depends_on:
- gluetun
network_mode: "container:gluetun"
volumes:
- name: "Configuration"
internal: /config
external: "{{ docker.directories.local }}/qbit/config"
external: "{{ docker.directories.config }}/qbit/config"
- name: "Downloads"
internal: /downloads
external: /media/docker/data/arr_downloads
@@ -408,11 +450,12 @@ services:
- QBT_WEBUI_PORT="8082"
- name: cadvisor
vm:
- docker-host12
- docker-host10
- docker-host00
- docker-host01
- docker-host02
container_name: cadvisor
image: gcr.io/cadvisor/cadvisor:v0.52.1
image: gcr.io/cadvisor/cadvisor:latest
restart: unless-stopped
ports:
- name: ""
internal: 8080
@@ -430,73 +473,60 @@ services:
- name: "Docker"
internal: /var/lib/docker:ro
external: /var/lib/docker
- name: karakeep
- name: elasticsearch
vm:
- docker-host01
container_name: karakeep
image: ghcr.io/karakeep-app/karakeep:0.23.2
container_name: elasticsearch
image: "docker.elastic.co/elasticsearch/elasticsearch:{{ elk_version }}"
restart: unless-stopped
ports:
- name: "http"
internal: 3000
external: "{{ services_external_http.karakeep }}"
- name: ""
internal: 9200
external: 9200
- name: ""
internal: 9300
external: 9300
volumes:
- name: "Data"
internal: /data
external: "{{ docker.directories.local }}/karakeep/config"
- name: "data"
internal: /usr/share/elasticsearch/data
external: "{{ docker.directories.config }}/elk/elasticsearch/data"
- name: "certs"
internal: /usr/share/elasticsearch/config/certs
external: "{{ docker.directories.config }}/elk/certs"
environment:
- MEILI_ADDR=http://karakeep-meilisearch:7700
- BROWSER_WEB_URL=http://karakeep-chrome:9222
- NEXTAUTH_SECRET={{ vault_docker.karakeep.nextauth_secret }}
- MEILI_MASTER_KEY={{ vault_docker.karakeep.meili_master_key }}
- NEXTAUTH_URL=https://karakeep.tudattr.dev/
- OPENAI_API_KEY={{ vault_docker.karakeep.openai_key }}
- DATA_DIR=/data
- DISABLE_SIGNUPS=true
sub_service:
- name: meilisearch
version: v1.11.1
nextauth_secret: "{{ vault_docker.karakeep.nextauth_secret }}"
meili_master_key: "{{ vault_docker.karakeep.meili_master_key }}"
openai_key: "{{ vault_docker.karakeep.openai_key }}"
- name: chrome
version: 123
- name: keycloak
- node.name=elasticsearch
- cluster.name=docker-cluster
- discovery.type=single-node
- "ELASTIC_PASSWORD={{ vault.docker.elk.elastic.password }}"
- xpack.security.enabled=true
- xpack.security.authc.api_key.enabled=true
- xpack.security.http.ssl.enabled=true
- xpack.security.http.ssl.key=certs/elasticsearch.key
- xpack.security.http.ssl.certificate=certs/elasticsearch.crt
- xpack.security.http.ssl.certificate_authorities=certs/ca.crt
- xpack.security.transport.ssl.enabled=true
- xpack.security.transport.ssl.verification_mode=certificate
- xpack.security.transport.ssl.key=certs/elasticsearch.key
- xpack.security.transport.ssl.certificate=certs/elasticsearch.crt
- xpack.security.transport.ssl.certificate_authorities=certs/ca.crt
- name: kibana
vm:
- docker-host01
container_name: keycloak
image: quay.io/keycloak/keycloak:26.2
depends_on:
- keycloak-postgres
container_name: kibana
image: "docker.elastic.co/kibana/kibana:{{ elk_version }}"
restart: unless-stopped
ports:
- name: "http"
internal: 8080
external: "{{ services_external_http.keycloak }}"
internal: 5601
external: 5601
volumes:
- name: "config"
internal: /opt/keycloak/data/import/homelab-realm.json
external: "{{ docker.directories.local }}/keycloak/homelab-realm.json"
- name: "config"
internal: /opt/keycloak/data/import/master-realm.json
external: "{{ docker.directories.local }}/keycloak/master-realm.json"
command:
- "start"
- "--import-realm"
- name: "certs"
internal: /usr/share/kibana/config/certs
external: "{{ docker.directories.config }}/elk/certs/"
environment:
- KC_DB=postgres
- KC_DB_URL=jdbc:postgresql://keycloak-postgres:5432/keycloak
- KC_DB_USERNAME={{ keycloak_config.database.username }}
- KC_DB_PASSWORD={{ keycloak_config.database.password }}
- KC_HOSTNAME=keycloak.{{ internal_domain }}
- KC_HTTP_ENABLED=true
- KC_HTTP_RELATIVE_PATH=/
- KC_PROXY=edge
- KC_PROXY_HEADERS=xforwarded
- KC_HOSTNAME_URL=https://keycloak.{{ internal_domain }}
- KC_HOSTNAME_ADMIN_URL=https://keycloak.{{ internal_domain }}
- KC_BOOTSTRAP_ADMIN_USERNAME=serviceadmin-{{ keycloak_admin_hash }}
- KC_BOOTSTRAP_ADMIN_PASSWORD={{ vault_docker.keycloak.admin.password }}
sub_service:
- name: postgres
version: 17
username: "{{ keycloak_config.database.username }}"
password: "{{ keycloak_config.database.password }}"
- ELASTICSEARCH_HOSTS=["https://elasticsearch:9200"]
- ELASTICSEARCH_USERNAME=kibana_system
- ELASTICSEARCH_PASSWORD={{ vault.docker.elk.elastic.password }}
- SERVER_SSL_ENABLED=true
- SERVER_SSL_CERTIFICATE=/usr/share/kibana/config/certs/kibana.crt
- SERVER_SSL_KEY=/usr/share/kibana/config/certs/kibana.key

28
group_vars/k3s/vars.yml Normal file
View File

@@ -0,0 +1,28 @@
db:
default_user:
user: "postgres"
name: "k3s"
user: "k3s"
password: "{{ vault.k3s.postgres.db.password }}"
listen_address: "{{ k3s.db.ip }}"
k3s:
net: "192.168.20.0/24"
server:
ips:
- 192.168.20.21
- 192.168.20.24
- 192.168.20.30
loadbalancer:
ip: 192.168.20.22
default_port: 6443
db:
ip: 192.168.20.23
default_port: "5432"
agent:
ips:
- 192.168.20.25
- 192.168.20.26
- 192.168.20.27
k3s_db_connection_string: "postgres://{{ db.user }}:{{ db.password }}@{{ k3s.db.ip }}:{{ k3s.db.default_port }}/{{ db.name }}"

10
host_vars/aya01.yml Normal file
View File

@@ -0,0 +1,10 @@
---
ansible_user: "root"
ansible_host: 192.168.20.12
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.pve.aya01.root.sudo }}"
host:
hostname: "aya01"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.34
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.docker.host00.sudo }}"
host:
hostname: "docker-host00"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.35
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.docker.host01.sudo }}"
host:
hostname: "docker-host01"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.36
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.docker.host02.sudo }}"
host:
hostname: "docker-host02"
ip: "{{ ansible_host }}"

10
host_vars/docker-lb.yml Normal file
View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.37
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.docker.lb.sudo }}"
host:
hostname: "docker-lb"
ip: "{{ ansible_host }}"

10
host_vars/inko.yml Normal file
View File

@@ -0,0 +1,10 @@
---
ansible_user: "root"
ansible_host: 192.168.20.14
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.pve.inko.root.sudo }}"
host:
hostname: "inko"
ip: "{{ ansible_host }}"

10
host_vars/k3s-agent00.yml Normal file
View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.25
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.agent00.sudo }}"
host:
hostname: "k3s-agent00"
ip: "{{ ansible_host }}"

10
host_vars/k3s-agent01.yml Normal file
View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.26
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.agent01.sudo }}"
host:
hostname: "k3s-agent01"
ip: "{{ ansible_host }}"

10
host_vars/k3s-agent02.yml Normal file
View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.27
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.agent02.sudo }}"
host:
hostname: "k3s-agent02"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,9 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.22
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.loadbalancer.sudo }}"
host:
hostname: "k3s-loadbalancer"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.32
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.longhorn00.sudo }}"
host:
hostname: "k3s-longhorn00"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.33
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.longhorn01.sudo }}"
host:
hostname: "k3s-longhorn01"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.31
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.longhorn02.sudo }}"
host:
hostname: "k3s-longhorn02"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,9 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.23
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.postgres.sudo }}"
host:
hostname: "k3s-postgres"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,9 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.21
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.server00.sudo }}"
host:
hostname: "k3s-server00"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.24
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.server01.sudo }}"
host:
hostname: "k3s-server01"
ip: "{{ ansible_host }}"

View File

@@ -0,0 +1,10 @@
---
ansible_user: "{{ user }}"
ansible_host: 192.168.20.30
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.k3s.server02.sudo }}"
host:
hostname: "k3s-server02"
ip: "{{ ansible_host }}"

10
host_vars/lulu.yml Normal file
View File

@@ -0,0 +1,10 @@
---
ansible_user: "root"
ansible_host: 192.168.20.28
ansible_port: 22
ansible_ssh_private_key_file: "{{ pk_path }}"
ansible_become_pass: "{{ vault.pve.lulu.root.sudo }}"
host:
hostname: "lulu"
ip: "{{ ansible_host }}"

58
inventory/production Normal file
View File

@@ -0,0 +1,58 @@
[proxmox]
aya01
lulu
inko
[k3s]
k3s-postgres
k3s-loadbalancer
k3s-server[00:02]
k3s-agent[00:02]
k3s-longhorn[00:02]
[vm]
k3s-postgres
k3s-loadbalancer
k3s-agent[00:02]
k3s-server[00:02]
k3s-longhorn[00:02]
docker-host[00:02]
[k3s_nodes]
k3s-server[00:02]
k3s-agent[00:02]
k3s-longhorn[00:02]
[docker]
docker-host[00:02]
docker-lb
[vps]
mii
[k3s_server]
k3s-server[00:02]
[k3s_agent]
k3s-agent[00:02]
[k3s_storage]
k3s-longhorn[00:02]
[db]
k3s-postgres
[loadbalancer]
k3s-loadbalancer
[docker_host]
docker-host[00:02]
[docker_lb]
docker-lb
[local]
localhost ansible_connection=local
[vm:vars]
ansible_ssh_common_args='-o ProxyCommand="ssh -p 22 -W %h:%p -q aya01"'

2
inventory/test Normal file
View File

@@ -0,0 +1,2 @@
[local]
test ansible_connection=local ansible_become_pass=vagrant

10
playbooks/common-k3s.yml Normal file
View File

@@ -0,0 +1,10 @@
---
- name: Run the common role on k3s
hosts: k3s
gather_facts: yes
vars_files:
- secrets.yml
roles:
- role: common
tags:
- common

19
playbooks/db.yml Normal file
View File

@@ -0,0 +1,19 @@
---
- name: Set up Servers
hosts: db
gather_facts: yes
vars_files:
- secrets.yml
roles:
- role: common
tags:
- common
- role: postgres
tags:
- postgres
- role: node_exporter
tags:
- node_exporter
- role: postgres_exporter
tags:
- postgres_exporter

View File

@@ -1,7 +1,9 @@
---
- name: Set up Servers
hosts: docker_host
gather_facts: true
gather_facts: yes
vars_files:
- secrets.yml
roles:
- role: common
tags:

View File

@@ -1,13 +1,13 @@
---
- name: Set up reverse proxy for docker
hosts: docker
gather_facts: true
hosts: docker_lb
gather_facts: yes
vars_files:
- secrets.yml
roles:
- role: common
tags:
- common
when: inventory_hostname in groups["docker_lb"]
- role: reverse_proxy
tags:
- reverse_proxy
when: inventory_hostname in groups["docker_lb"]

View File

@@ -1,5 +0,0 @@
---
- name: Setup Docker Hosts
ansible.builtin.import_playbook: docker-host.yml
- name: Setup Docker load balancer
ansible.builtin.import_playbook: docker-lb.yml

View File

@@ -1,6 +1,20 @@
- name: Set up Agents
hosts: k3s
gather_facts: true
hosts: k3s_nodes
gather_facts: yes
vars_files:
- secrets.yml
pre_tasks:
- name: Get K3s token from the first server
when: host.ip == k3s.server.ips[0] and inventory_hostname in groups["k3s_server"]
slurp:
src: /var/lib/rancher/k3s/server/node-token
register: k3s_token
become: true
- name: Set fact on k3s.server.ips[0]
when: host.ip == k3s.server.ips[0] and inventory_hostname in groups["k3s_server"]
set_fact: k3s_token="{{ k3s_token['content'] | b64decode | trim }}"
roles:
- role: common
when: inventory_hostname in groups["k3s_agent"]
@@ -8,9 +22,10 @@
- common
- role: k3s_agent
when: inventory_hostname in groups["k3s_agent"]
k3s_token: "{{ hostvars[(hostvars | dict2items | map(attribute='value') | map('dict2items') | map('selectattr', 'key', 'match', 'host') | map('selectattr', 'value.ip', 'match', k3s.server.ips[0] ) | select() | first | items2dict).host.hostname].k3s_token }}"
tags:
- k3s_agent
# - role: node_exporter
# when: inventory_hostname in groups["k3s_agent"]
# tags:
# - node_exporter
- role: node_exporter
when: inventory_hostname in groups["k3s_agent"]
tags:
- node_exporter

View File

@@ -1,17 +0,0 @@
---
- name: Set up Servers
hosts: k3s
gather_facts: true
roles:
- role: common
tags:
- common
when: inventory_hostname in groups["k3s_loadbalancer"]
- role: k3s_loadbalancer
tags:
- k3s_loadbalancer
when: inventory_hostname in groups["k3s_loadbalancer"]
- role: node_exporter
tags:
- node_exporter
when: inventory_hostname in groups["k3s_loadbalancer"]

View File

@@ -1,17 +1,16 @@
---
- name: Set up Servers
hosts: k3s
gather_facts: true
hosts: k3s_server
gather_facts: yes
vars_files:
- secrets.yml
roles:
- role: common
tags:
- common
when: inventory_hostname in groups["k3s_server"]
- role: k3s_server
tags:
- k3s_server
when: inventory_hostname in groups["k3s_server"]
# - role: node_exporter
# tags:
# - node_exporter
# when: inventory_hostname in groups["k3s_server"]
- role: node_exporter
tags:
- node_exporter

View File

@@ -1,6 +1,20 @@
- name: Set up storage
hosts: k3s_nodes
gather_facts: true
gather_facts: yes
vars_files:
- secrets.yml
pre_tasks:
- name: Get K3s token from the first server
when: host.ip == k3s.server.ips[0] and inventory_hostname in groups["k3s_server"]
slurp:
src: /var/lib/rancher/k3s/server/node-token
register: k3s_token
become: true
- name: Set fact on k3s.server.ips[0]
when: host.ip == k3s.server.ips[0] and inventory_hostname in groups["k3s_server"]
set_fact: k3s_token="{{ k3s_token['content'] | b64decode | trim }}"
roles:
- role: common
when: inventory_hostname in groups["k3s_storage"]
@@ -8,6 +22,7 @@
- common
- role: k3s_storage
when: inventory_hostname in groups["k3s_storage"]
k3s_token: "{{ hostvars[(hostvars | dict2items | map(attribute='value') | map('dict2items') | map('selectattr', 'key', 'match', 'host') | map('selectattr', 'value.ip', 'match', k3s.server.ips[0] ) | select() | first | items2dict).host.hostname].k3s_token }}"
tags:
- k3s_storage
- role: node_exporter

View File

@@ -0,0 +1,16 @@
---
- name: Set up Servers
hosts: loadbalancer
gather_facts: yes
vars_files:
- secrets.yml
roles:
- role: common
tags:
- common
- role: loadbalancer
tags:
- loadbalancer
- role: node_exporter
tags:
- node_exporter

View File

@@ -1,15 +0,0 @@
---
- name: Run proxmox vm playbook
hosts: proxmox
gather_facts: true
vars:
is_localhost: "{{ inventory_hostname == '127.0.0.1' }}"
is_proxmox_node: "{{ 'proxmox_nodes' in group_names }}"
roles:
- role: common
tags:
- common
when: not is_localhost
- role: proxmox
tags:
- proxmox

9
playbooks/test.yml Normal file
View File

@@ -0,0 +1,9 @@
---
- hosts: db
gather_facts: yes
vars_files:
- secrets.yml
tasks:
- name: Print the database connection string
debug:
msg: "{{ k3s_db_connection_string }}"

5
playbooks/ubuntu.yml Normal file
View File

@@ -0,0 +1,5 @@
- name: Provision Local Ubuntu Machine
hosts: local
gather_facts: true
roles:
- ubuntu

View File

@@ -1,7 +0,0 @@
certifi==2025.1.31
charset-normalizer==3.4.1
idna==3.10
nc-dnsapi==0.1.3
proxmoxer==2.2.0
requests==2.32.3
urllib3==2.3.0

View File

@@ -1,80 +0,0 @@
xterm-ghostty|ghostty|Ghostty,
am, bce, ccc, hs, km, mc5i, mir, msgr, npc, xenl, AX, Su, Tc, XT, fullkbd,
colors#0x100, cols#80, it#8, lines#24, pairs#0x7fff,
acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r,
csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C,
cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A,
cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m,
dl=\E[%p1%dM, dl1=\E[M, dsl=\E]2;\007, ech=\E[%p1%dX,
ed=\E[J, el=\E[K, el1=\E[1K, flash=\E[?5h$<100/>\E[?5l,
fsl=^G, home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH,
ich=\E[%p1%d@, ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=\n,
indn=\E[%p1%dS,
initc=\E]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
invis=\E[8m, kDC=\E[3;2~, kEND=\E[1;2F, kHOM=\E[1;2H,
kIC=\E[2;2~, kLFT=\E[1;2D, kNXT=\E[6;2~, kPRV=\E[5;2~,
kRIT=\E[1;2C, kbs=^?, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB,
kcuf1=\EOC, kcuu1=\EOA, kdch1=\E[3~, kend=\EOF, kent=\EOM,
kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~,
kf13=\E[1;2P, kf14=\E[1;2Q, kf15=\E[1;2R, kf16=\E[1;2S,
kf17=\E[15;2~, kf18=\E[17;2~, kf19=\E[18;2~, kf2=\EOQ,
kf20=\E[19;2~, kf21=\E[20;2~, kf22=\E[21;2~,
kf23=\E[23;2~, kf24=\E[24;2~, kf25=\E[1;5P, kf26=\E[1;5Q,
kf27=\E[1;5R, kf28=\E[1;5S, kf29=\E[15;5~, kf3=\EOR,
kf30=\E[17;5~, kf31=\E[18;5~, kf32=\E[19;5~,
kf33=\E[20;5~, kf34=\E[21;5~, kf35=\E[23;5~,
kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q, kf39=\E[1;6R,
kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~, kf42=\E[17;6~,
kf43=\E[18;6~, kf44=\E[19;6~, kf45=\E[20;6~,
kf46=\E[21;6~, kf47=\E[23;6~, kf48=\E[24;6~,
kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q, kf51=\E[1;3R,
kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~,
kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~,
kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~,
kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~,
kf8=\E[19~, kf9=\E[20~, khome=\EOH, kich1=\E[2~,
kind=\E[1;2B, kmous=\E[<, knp=\E[6~, kpp=\E[5~,
kri=\E[1;2A, oc=\E]104\007, op=\E[39;49m, rc=\E8,
rep=%p1%c\E[%p2%{1}%-%db, rev=\E[7m, ri=\EM,
rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, rmam=\E[?7l,
rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, rmso=\E[27m,
rmul=\E[24m, rs1=\E]\E\\\Ec, sc=\E7,
setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
sgr0=\E(B\E[m, sitm=\E[3m, smacs=\E(0, smam=\E[?7h,
smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h\E=, smso=\E[7m,
smul=\E[4m, tbc=\E[3g, tsl=\E]2;, u6=\E[%i%d;%dR, u7=\E[6n,
u8=\E[?%[;0123456789]c, u9=\E[c, vpa=\E[%i%p1%dd,
BD=\E[?2004l, BE=\E[?2004h, Clmg=\E[s,
Cmg=\E[%i%p1%d;%p2%ds, Dsmg=\E[?69l, E3=\E[3J,
Enmg=\E[?69h, Ms=\E]52;%p1%s;%p2%s\007, PE=\E[201~,
PS=\E[200~, RV=\E[>c, Se=\E[2 q,
Setulc=\E[58:2::%p1%{65536}%/%d:%p1%{256}%/%{255}%&%d:%p1%{255}%&%d%;m,
Smulx=\E[4:%p1%dm, Ss=\E[%p1%d q,
Sync=\E[?2026%?%p1%{1}%-%tl%eh%;,
XM=\E[?1006;1000%?%p1%{1}%=%th%el%;, XR=\E[>0q,
fd=\E[?1004l, fe=\E[?1004h, kDC3=\E[3;3~, kDC4=\E[3;4~,
kDC5=\E[3;5~, kDC6=\E[3;6~, kDC7=\E[3;7~, kDN=\E[1;2B,
kDN3=\E[1;3B, kDN4=\E[1;4B, kDN5=\E[1;5B, kDN6=\E[1;6B,
kDN7=\E[1;7B, kEND3=\E[1;3F, kEND4=\E[1;4F,
kEND5=\E[1;5F, kEND6=\E[1;6F, kEND7=\E[1;7F,
kHOM3=\E[1;3H, kHOM4=\E[1;4H, kHOM5=\E[1;5H,
kHOM6=\E[1;6H, kHOM7=\E[1;7H, kIC3=\E[2;3~, kIC4=\E[2;4~,
kIC5=\E[2;5~, kIC6=\E[2;6~, kIC7=\E[2;7~, kLFT3=\E[1;3D,
kLFT4=\E[1;4D, kLFT5=\E[1;5D, kLFT6=\E[1;6D,
kLFT7=\E[1;7D, kNXT3=\E[6;3~, kNXT4=\E[6;4~,
kNXT5=\E[6;5~, kNXT6=\E[6;6~, kNXT7=\E[6;7~,
kPRV3=\E[5;3~, kPRV4=\E[5;4~, kPRV5=\E[5;5~,
kPRV6=\E[5;6~, kPRV7=\E[5;7~, kRIT3=\E[1;3C,
kRIT4=\E[1;4C, kRIT5=\E[1;5C, kRIT6=\E[1;6C,
kRIT7=\E[1;7C, kUP=\E[1;2A, kUP3=\E[1;3A, kUP4=\E[1;4A,
kUP5=\E[1;5A, kUP6=\E[1;6A, kUP7=\E[1;7A, kxIN=\E[I,
kxOUT=\E[O, rmxx=\E[29m, rv=\E\\[[0-9]+;[0-9]+;[0-9]+c,
setrgbb=\E[48:2:%p1%d:%p2%d:%p3%dm,
setrgbf=\E[38:2:%p1%d:%p2%d:%p3%dm, smxx=\E[9m,
xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;,
xr=\EP>\\|[ -~]+a\E\\,

View File

@@ -1,19 +0,0 @@
Protocol 2
PermitRootLogin yes
MaxAuthTries 3
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes
AllowAgentForwarding no
AllowTcpForwarding yes
X11Forwarding no
PrintMotd no
TCPKeepAlive no
ClientAliveCountMax 2
TrustedUserCAKeys /etc/ssh/vault-ca.pub
UseDNS yes
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server

View File

@@ -1 +0,0 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxIbkko72kVSfYDjJpiMH9SjHUGqBn3MbBvmotsPQhybFgnnkBpX/3fM9olP+Z6PGsmbOEs0fOjPS6uY5hjKcKsyHdZfS6cA4wjY/DL8fwATAW5FCDBtMpdg2/sb8j9jutHHs4sQeRBolVwKcv+ZAaJNnOzNHwxVUfT9bNwShthnAFjkY7oZo657FRomlkDJjmGQuratP0veKA8jYzqqPWwWidTGQerLYTyJ3Z8pbQa5eN7svrvabjjDLbVTDESE8st9WEmwvAwoj7Kz+WovCy0Uz7LRFVmaRiapM8SXtPPUC0xfyzAB3NxwBtxizdUMlShvLcL6cujcUBMulVMpsqEaOESTpmVTrMJhnJPZG/3j9ziGoYIa6hMj1J9/qLQ5dDNVVXMxw99G31x0LJoy12IE90P4Cahux8iN0Cp4oB4+B6/qledxs1fcRzsnQY/ickjKhqcJwgHzsnwjDkeYRaYte5x4f/gJ77kA20nPto7mxr2mhWot/i9B1KlMURVXOH/q4nrzhJ0hPJpM0UtzQ58TmzE4Osf/B5yoe8V//6XnelbmG/nKCIzg12d7PvaLjbFMn8IgOwDMRlip+vpyadRr/+pCawrfo4vLF7BsnJ84aoByIpbwaysgaYHtjfZWImorMVkgviC4O6Hn9/ZiLNze2A9DaNUnLVJ0nYNbmv9Q==

View File

@@ -2,23 +2,11 @@
- name: Copy bash-configs
ansible.builtin.template:
src: "files/bash/{{ item }}"
dest: "{{ ansible_env.HOME }}/.{{ item }}"
owner: "{{ ansible_user_id }}"
group: "{{ ansible_user_id }}"
dest: "/home/{{ user }}/.{{ item }}"
owner: "{{ user }}"
group: "{{ user }}"
mode: "644"
loop:
- bashrc
- bash_aliases
- name: Copy ghostty infocmp
ansible.builtin.copy:
src: files/ghostty/infocmp
dest: "{{ ansible_env.HOME }}/ghostty"
owner: "{{ ansible_user_id }}"
group: "{{ ansible_user_id }}"
mode: "0644"
register: ghostty_terminfo
- name: Compile ghostty terminalinfo
ansible.builtin.command: "tic -x {{ ansible_env.HOME }}/ghostty"
when: ghostty_terminfo.changed
become: true

View File

@@ -11,6 +11,7 @@
url: https://raw.githubusercontent.com/eza-community/eza/main/deb.asc
dest: /etc/apt/keyrings/gierens.asc
mode: "0644"
register: gpg_key_result
become: true
- name: Add Gierens repository to apt sources

View File

@@ -1,14 +1,14 @@
---
- name: Set a hostname
ansible.builtin.hostname:
name: "{{ inventory_hostname }}"
name: "{{ host.hostname }}"
become: true
- name: Update /etc/hosts to reflect the new hostname
ansible.builtin.lineinfile:
path: /etc/hosts
regexp: '^127\.0\.1\.1'
line: "127.0.1.1 {{ inventory_hostname }}"
line: "127.0.1.1 {{ host.hostname }}"
state: present
backup: true
become: true

View File

@@ -1,10 +1,10 @@
---
- name: Configure Time
ansible.builtin.include_tasks: time.yml
- name: Configure Packages
ansible.builtin.include_tasks: packages.yml
- name: Configure Hostname
ansible.builtin.include_tasks: hostname.yml
- name: Configure Packages
ansible.builtin.include_tasks: packages.yml
- name: Configure Extra-Packages
ansible.builtin.include_tasks: extra_packages.yml
- name: Configure Bash

View File

@@ -5,24 +5,9 @@
upgrade: true
autoremove: true
become: true
when: ansible_user_id != "root"
- name: Install base packages
ansible.builtin.apt:
name: "{{ common_packages }}"
state: present
become: true
when: ansible_user_id != "root"
- name: Update and upgrade packages
ansible.builtin.apt:
update_cache: true
upgrade: true
autoremove: true
when: ansible_user_id == "root"
- name: Install base packages
ansible.builtin.apt:
name: "{{ common_packages }}"
state: present
when: ansible_user_id == "root"

View File

@@ -1,28 +1,17 @@
---
- name: Copy user sshd_config
- name: Copy sshd_config
ansible.builtin.template:
src: files/ssh/user/sshd_config
src: templates/ssh/sshd_config
dest: /etc/ssh/sshd_config
mode: "644"
backup: true
notify:
- Restart sshd
become: true
when: ansible_user_id != "root"
- name: Copy root sshd_config
ansible.builtin.template:
src: files/ssh/root/sshd_config
dest: /etc/ssh/sshd_config
mode: "644"
backup: true
notify:
- Restart sshd
when: ansible_user_id == "root"
- name: Copy pubkey
ansible.builtin.copy:
src: files/ssh/vault-ca.pub
dest: "/etc/ssh/vault-ca.pub"
content: "{{ pubkey }}"
dest: "/home/{{ user }}/.ssh/authorized_keys"
owner: "{{ user }}"
group: "{{ user }}"
mode: "644"
become: true

View File

@@ -1,11 +1,4 @@
---
- name: Set timezone
- name: Set timezone to "{{ timezone }}"
community.general.timezone:
name: "{{ timezone }}"
become: true
when: ansible_user_id != "root"
- name: Set timezone
community.general.timezone:
name: "{{ timezone }}"
when: ansible_user_id == "root"

View File

@@ -1,3 +1,4 @@
Include /etc/ssh/sshd_config.d/*.conf
Protocol 2
PermitRootLogin no
MaxAuthTries 3
@@ -12,7 +13,6 @@ X11Forwarding no
PrintMotd no
TCPKeepAlive no
ClientAliveCountMax 2
TrustedUserCAKeys /etc/ssh/vault-ca.pub
UseDNS yes
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server

View File

@@ -1,15 +0,0 @@
common_packages:
- build-essential
- curl
- git
- iperf3
- neovim
- rsync
- smartmontools
- sudo
- systemd-timesyncd
- tree
- screen
- bat
- fd-find
- ripgrep

View File

@@ -8,14 +8,4 @@
- name: Restart compose
community.docker.docker_compose_v2:
project_src: "{{ docker.directories.compose }}"
state: present
retries: 3
delay: 5
become: true
- name: Restart host
ansible.builtin.reboot:
connect_timeout: 5
reboot_timeout: 600
test_command: whoami
become: true
state: restarted

View File

@@ -1,50 +0,0 @@
---
- name: Check if debian.sources file exists
ansible.builtin.stat:
path: /etc/apt/sources.list.d/debian.sources
register: debian_sources_stat
- name: Replace Components line to include non-free and non-free-firmware
ansible.builtin.replace:
path: /etc/apt/sources.list.d/debian.sources
regexp: "^Components:.*$"
replace: "Components: main non-free non-free-firmware"
when: debian_sources_stat.stat.exists
become: true
- name: Setup VM Packages
ansible.builtin.apt:
name: "{{ item }}"
state: present
update_cache: true
loop: "{{ docker_host_package_common_dependencies }}"
become: true
- name: Gather installed package facts
ansible.builtin.package_facts:
manager: auto
- name: Filter for specific cloud kernel packages
ansible.builtin.set_fact:
cloud_kernel_packages: >-
{{
ansible_facts.packages.keys()
| select('search', 'linux-image')
| select('search', 'cloud')
| list
}}
- name: Use the list to remove the found packages
ansible.builtin.apt:
name: "{{ cloud_kernel_packages }}"
state: absent
autoremove: true
when: cloud_kernel_packages | length > 0
become: true
- name: Restart host
ansible.builtin.reboot:
connect_timeout: 5
reboot_timeout: 600
test_command: whoami
become: true

View File

@@ -1,31 +0,0 @@
---
- name: Set fact if this host should run Keycloak
ansible.builtin.set_fact:
is_keycloak_host: "{{ inventory_hostname in (services | selectattr('name', 'equalto', 'keycloak') | map(attribute='vm') | first) }}"
- name: Create Keycloak directories
ansible.builtin.file:
path: "{{ docker.directories.local }}/keycloak/"
owner: "{{ ansible_user_id }}"
group: "{{ ansible_user_id }}"
state: directory
mode: "0755"
when: is_keycloak_host | bool
become: true
- name: Setup Keycloak realms
ansible.builtin.template:
src: "templates/keycloak/realm.json.j2"
dest: "{{ docker.directories.local }}/keycloak/{{ keycloak.realm }}-realm.json"
owner: "{{ ansible_user_id }}"
group: "{{ ansible_user_id }}"
mode: "644"
backup: true
when: is_keycloak_host | bool
loop: "{{ keycloak_config.realms }}"
loop_control:
loop_var: keycloak
notify:
- Restart docker
- Restart compose
become: true

View File

@@ -3,8 +3,8 @@
ansible.builtin.template:
src: "templates/compose.yaml.j2"
dest: "{{ docker.directories.compose }}/compose.yaml"
owner: "{{ ansible_user_id }}"
group: "{{ ansible_user_id }}"
owner: "{{ user }}"
group: "{{ user }}"
mode: "644"
backup: true
notify:

View File

@@ -9,20 +9,19 @@
- /media/series
- /media/movies
- /media/songs
- "{{ docker.directories.local }}"
- "{{ docker.directories.config }}"
- "{{ docker.directories.opt }}"
- "{{ docker.directories.compose }}"
- /opt/local
become: true
- name: Set ownership to {{ ansible_user_id }}
- name: Set ownership to {{ user }}
ansible.builtin.file:
path: "{{ item }}"
owner: "{{ ansible_user_id }}"
group: "{{ ansible_user_id }}"
owner: "{{ user }}"
group: "{{ user }}"
loop:
- "{{ docker.directories.local }}"
- "{{ docker.directories.config }}"
- "{{ docker.directories.compose }}"
- "{{ docker.directories.opt }}"
- /opt/local
- /media
become: true

View File

@@ -1,20 +1,18 @@
---
- name: Setup VM
ansible.builtin.include_tasks: 10_setup.yml
ansible.builtin.include_tasks: setup.yml
- name: Install docker
ansible.builtin.include_tasks: 20_installation.yml
ansible.builtin.include_tasks: installation.yml
- name: Setup user and group for docker
ansible.builtin.include_tasks: 30_user_group_setup.yml
ansible.builtin.include_tasks: user_group_setup.yml
- name: Setup directory structure for docker
ansible.builtin.include_tasks: 40_directory_setup.yml
- name: Deploy configs
ansible.builtin.include_tasks: 50_provision.yml
ansible.builtin.include_tasks: directory_setup.yml
- name: Deploy docker compose
ansible.builtin.include_tasks: 60_deploy_compose.yml
ansible.builtin.include_tasks: deploy_compose.yml
- name: Publish metrics
ansible.builtin.include_tasks: 70_export.yml
ansible.builtin.include_tasks: export.yml

View File

@@ -0,0 +1,9 @@
---
- name: Enable HW accelerate for VM
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop:
- firmware-misc-nonfree
- nfs-common
become: true

View File

@@ -5,12 +5,10 @@
state: present
become: true
- name: Append the group docker to "{{ ansible_user_id }}"
- name: Append the group docker to "{{ user }}"
ansible.builtin.user:
name: "{{ ansible_user_id }}"
name: "{{ user }}"
shell: /bin/bash
groups: docker
append: true
become: true
notify:
- Restart host

View File

@@ -1,13 +1,12 @@
services:
{% for service in services %}
{% if inventory_hostname in service.vm %}
{{ service.name }}:
{{service.name}}:
container_name: {{ service.container_name }}
image: {{ service.image }}
restart: unless-stopped
restart: {{ service.restart }}
{% if service.network_mode is not defined %}
hostname: {{ service.name }}
hostname: {{service.name}}
networks:
- net
{% endif %}
@@ -16,40 +15,11 @@ services:
ports:
{% for port in service.ports %}
{% if port.internal != 'proxy_only' %}
- {{ port.external }}:{{ port.internal }}
- {{port.external}}:{{port.internal}}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% if service.ports is defined and service.ports is iterable %}
{% set first_http_port = service.ports | default([]) | selectattr('name', 'defined') | selectattr('name', 'search', 'http') | first %}
{% set chosen_http_port_value = none %}
{% if first_http_port is not none %}
{% if first_http_port.internal is defined and first_http_port.internal == 'proxy_only' %}
{% if first_http_port.external is defined %}
{% set chosen_http_port_value = first_http_port.external %}
{% endif %}
{% else %}
{% set chosen_http_port_value = first_http_port.internal %}
{% endif %}
{% if chosen_http_port_value is defined %}
healthcheck:
{% set healthcheck = 'curl' %}
{% if service.healthcheck is defined %}
{% set healthcheck = service.healthcheck %}
{% endif %}
{% if healthcheck == 'curl' %}
test: ["CMD", "curl", "-f", "--silent", "--show-error", "--connect-timeout", "5", "http://localhost:{{ chosen_http_port_value }}/"]
{% elif healthcheck == 'wget' %}
test: ["CMD-SHELL", "wget --quiet --spider --timeout=5 http://localhost:{{ chosen_http_port_value }}/ || exit 1"]
{% endif %}
interval: 30s
timeout: 10s
retries: 5
start_period: 20s
{% endif %}
{% endif %}
{% endif %}
{% if service.cap_add is defined and service.cap_add is iterable %}
cap_add:
{% for cap in service.cap_add %}
@@ -71,88 +41,46 @@ services:
{% if service.volumes is defined and service.volumes is iterable %}
volumes:
{% for volume in service.volumes %}
- {{ volume.external }}:{{ volume.internal }}
- {{volume.external}}:{{volume.internal}}
{% endfor %}
{% endif %}
{% if service.environment is defined and service.environment is iterable %}
environment:
{% for env in service.environment %}
- {{ env }}
- {{env}}
{% endfor %}
{% endif %}
{% if service.devices is defined and service.devices is iterable %}
devices:
{% for device in service.devices %}
- {{ device.external }}:{{ device.internal }}
- {{device.external}}:{{device.internal}}
{% endfor %}
{% endif %}
{% if service.command is defined and service.command is iterable %}
command:
{% for command in service.command %}
- {{ command }}
{% endfor %}
{% endif %}
{% if service.sub_service is defined and service.sub_service is iterable %}
{% for sub in service.sub_service %}
{% if sub.name is defined and sub.name == "postgres" %}
{{ service.name }}-postgres:
container_name: {{ service.name }}-postgres
image: docker.io/library/postgres:{{ sub.version }}
{% if service.name == 'paperless' %}
{{service.name}}-broker:
container_name: paperless-broker
image: docker.io/library/redis:7
restart: unless-stopped
hostname: {{ service.name }}-postgres
networks:
- net
volumes:
- /opt/local/{{ service.name }}/postgres/data:/var/lib/postgresql/data
- /opt/local/paperless/redis/data:/data
{{service.name}}-postgres:
container_name: paperless-postgres
image: docker.io/library/postgres:15
restart: unless-stopped
networks:
- net
volumes:
- /opt/local/paperless/db/data:/var/lib/postgresql/data
environment:
POSTGRES_DB: {{ service.name }}
POSTGRES_USER: {{ sub.username }}
POSTGRES_PASSWORD: {{ sub.password }}
{% endif %}
{% if sub.name is defined and sub.name == "redis" %}
{{ service.name }}-redis:
container_name: {{ service.name }}-redis
image: docker.io/library/redis:{{ sub.version }}
restart: unless-stopped
hostname: {{ service.name }}-redis
networks:
- net
volumes:
- /opt/local/{{ service.name }}/redis/data:/data
{% endif %}
{% if sub.name is defined and sub.name == "chrome" %}
{{ service.name }}-chrome:
image: gcr.io/zenika-hub/alpine-chrome:{{ sub.version }}
container_name: {{ service.name }}-chrome
restart: unless-stopped
networks:
- net
command:
- --no-sandbox
- --disable-gpu
- --disable-dev-shm-usage
- --remote-debugging-address=0.0.0.0
- --remote-debugging-port=9222
- --hide-scrollbars
{% endif %}
{% if sub.name is defined and sub.name == "meilisearch" %}
{{ service.name }}-meilisearch:
container_name: {{ service.name }}-meilisearch
image: getmeili/meilisearch:{{ sub.version }}
restart: unless-stopped
hostname: {{ service.name }}-meilisearch
networks:
- net
volumes:
- /opt/local/{{ service.name }}/mailisearch/data:/meili_data
environment:
- MEILI_NO_ANALYTICS=true
- NEXTAUTH_SECRET={{ sub.nextauth_secret }}
- MEILI_MASTER_KEY={{ sub.meili_master_key }}
- OPENAI_API_KEY="{{ sub.openai_key }}"
{% endif %}
{% endfor %}
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: 5fnhn%u2YWY3paNvMAjdoufYPQ2Hf3Yi
{% endif %}
{% endif %}
{% endfor %}
networks:
@@ -162,3 +90,6 @@ networks:
driver: default
config:
- subnet: 172.16.69.0/24
volumes:
prometheus_data: {}

View File

@@ -1,79 +0,0 @@
{
"realm": "{{ keycloak.realm }}",
"enabled": true,
"displayName": "{{ keycloak.display_name }}",
"displayNameHtml": "<div class=\"kc-logo-text\">{{keycloak.display_name}}</div>",
"bruteForceProtected": true,
"users": [
{% if keycloak.users is defined and keycloak.users is iterable %}
{% for user in keycloak.users %}
{
"username": "{{ user.username }}",
"enabled": true,
"credentials": [
{
"type": "password",
"value": "{{ user.password }}",
"temporary": false
}
],
"realmRoles": [
{% for realm_role in user.realm_roles %}
"{{ realm_role }}"{%- if not loop.last %},{% endif %}{{''}}
{% endfor %}
],
"clientRoles": {
"account": [
{% for account in user.client_roles.account %}
"{{ account }}"{%- if not loop.last %},{% endif %}{{''}}
{% endfor %}
]
}
},{% if not loop.last %}{% endif %}
{% endfor %}
{% endif %}
{
"username": "{{ keycloak.admin.username }}",
"enabled": true,
"credentials": [
{
"type": "password",
"value": "{{ keycloak.admin.password }}",
"temporary": false
}
],
"realmRoles": [
{% for realm_role in keycloak.admin.realm_roles %}
"{{ realm_role }}"{% if not loop.last %},{% endif %}{{''}}
{% endfor %}
],
"clientRoles": {
"realm-management": [
{% for realm_management in keycloak.admin.client_roles.realm_management %}
"{{ realm_management }}"{%- if not loop.last %},{% endif %}{{''}}
{% endfor %}
],
"account": [
{% for account in keycloak.admin.client_roles.account %}
"{{ account }}"{%- if not loop.last %},{% endif %}{{''}}
{% endfor %}
]
}
}
],
"roles": {
"realm": [
{% for role in keycloak.roles.realm %}
{
"name": "{{ role.name }}",
"description": "{{ role.name }}"
}{% if not loop.last %},{% endif %}
{% endfor %}
]
},
"defaultRoles": [
{% for role in keycloak.roles.default_roles %}
"{{ role }}"{% if not loop.last %},{% endif %}{{''}}
{% endfor %}
]
}

View File

@@ -1,9 +0,0 @@
docker_host_package_common_dependencies:
- nfs-common
- firmware-misc-nonfree
- linux-image-amd64
apt_lock_files:
- /var/lib/dpkg/lock
- /var/lib/dpkg/lock-frontend
- /var/cache/apt/archives/lock

View File

@@ -11,11 +11,11 @@
dest: /tmp/k3s_install.sh
mode: "0755"
- name: Install K3s on agent
- name: Install K3s on the secondary servers
when: not k3s_status.stat.exists
ansible.builtin.command: |
/tmp/k3s_install.sh
environment:
K3S_URL: "https://{{ hostvars['k3s-loadbalancer'].ansible_default_ipv4.address }}:{{ k3s.loadbalancer.default_port }}"
K3S_URL: "https://{{ k3s.loadbalancer.ip }}:{{ k3s.loadbalancer.default_port }}"
K3S_TOKEN: "{{ k3s_token }}"
become: true

View File

@@ -1,16 +0,0 @@
---
- name: Installation
ansible.builtin.include_tasks: installation.yml
- name: Configure
ansible.builtin.include_tasks: configuration.yml
- name: Setup DNS on Netcup
community.general.netcup_dns:
api_key: "{{ k3s_loadbalancer_netcup_api_key }}"
api_password: "{{ k3s_loadbalancer_netcup_api_password }}"
customer_id: "{{ k3s_loadbalancer_netcup_customer_id }}"
domain: "{{ domain }}"
name: "k3s"
type: "A"
value: "{{ hostvars['k3s-loadbalancer'].ansible_default_ipv4.address }}"
delegate_to: localhost

View File

@@ -1,87 +0,0 @@
include /etc/nginx/modules-enabled/*.conf;
events {}
stream {
# TCP Load Balancing for the K3s API
upstream k3s_servers {
{% for ip in k3s_server_ips %}
server {{ ip }}:{{ k3s.loadbalancer.default_port }};
{% endfor %}
}
server {
listen {{k3s.loadbalancer.default_port}};
proxy_pass k3s_servers;
}
upstream dns_servers {
{% for ip in k3s_server_ips %}
server {{ ip }}:53;
{% endfor %}
}
server {
listen 53 udp;
proxy_pass dns_servers;
}
}
# http {
# upstream k3s_servers_http {
# least_conn;
# {% for ip in k3s_server_ips %}
# server {{ ip }}:80;
# {% endfor %}
# }
#
# upstream k3s_servers_https {
# least_conn;
# {% for ip in k3s_server_ips %}
# server {{ ip }}:443;
# {% endfor %}
# }
#
# server {
# listen 80;
#
# location / {
# proxy_pass http://k3s_servers_http;
# proxy_set_header Host $http_host;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto http;
# }
# }
#
# server {
# listen 443 ssl;
#
# server_name staging.k3s.seyshiro.de *.staging.k3s.seyshiro.de;
#
# ssl_certificate /etc/nginx/ssl/staging_tls.crt;
# ssl_certificate_key /etc/nginx/ssl/staging_tls.key;
#
# location / {
# proxy_pass https://k3s_servers_https;
# proxy_set_header Host $host;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto https;
# }
# }
#
# server {
# listen 443 ssl;
#
# server_name k3s.seyshiro.de *.k3s.seyshiro.de;
#
# ssl_certificate /etc/nginx/ssl/production_tls.crt;
# ssl_certificate_key /etc/nginx/ssl/production_tls.key;
#
# location / {
# proxy_pass https://k3s_servers_https;
# proxy_set_header Host $host;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto https;
# }
# }
# }

View File

@@ -1,7 +0,0 @@
k3s_loadbalancer_nginx_config_path: "/etc/nginx/nginx.conf"
k3s_loadbalancer_netcup_api_key: "{{ netcup_api_key }}"
k3s_loadbalancer_netcup_api_password: "{{ netcup_api_password }}"
k3s_loadbalancer_netcup_customer_id: "{{ netcup_customer_id }}"
domain: "{{ internal_domain }}"

View File

@@ -1,87 +0,0 @@
---
- name: Slurp original k3s.yaml from primary K3s server
ansible.builtin.slurp:
src: /etc/rancher/k3s/k3s.yaml
register: original_k3s_kubeconfig_slurp
become: true
- name: Parse original k3s.yaml content to extract cert data
ansible.builtin.set_fact:
original_parsed_k3s_kubeconfig: "{{ original_k3s_kubeconfig_slurp.content | b64decode | from_yaml }}"
delegate_to: localhost
run_once: true
- name: Set facts for certificate and key data needed by the template
ansible.builtin.set_fact:
k3s_server_ca_data: "{{ original_parsed_k3s_kubeconfig.clusters[0].cluster['certificate-authority-data'] }}"
k3s_client_cert_data: "{{ original_parsed_k3s_kubeconfig.users[0].user['client-certificate-data'] }}"
k3s_client_key_data: "{{ original_parsed_k3s_kubeconfig.users[0].user['client-key-data'] }}"
delegate_to: localhost
run_once: true
- name: Decode and save K3s Server CA certificate
ansible.builtin.copy:
content: "{{ k3s_server_ca_data | b64decode }}"
dest: "/tmp/k3s-ca.crt"
mode: "0644"
delegate_to: localhost
become: false
- name: Decode and save K3s Client certificate
ansible.builtin.copy:
content: "{{ k3s_client_cert_data | b64decode }}"
dest: "/tmp/k3s-client.crt"
mode: "0644"
delegate_to: localhost
become: false
- name: Decode and save K3s Client key
ansible.builtin.copy:
content: "{{ k3s_client_key_data | b64decode }}"
dest: "/tmp/k3s-client.key"
mode: "0600"
delegate_to: localhost
become: false
- name: Add K3s cluster to kubeconfig
ansible.builtin.command: >
kubectl config set-cluster "{{ k3s_cluster_name }}"
--server="https://{{ k3s_server_name }}:6443"
--certificate-authority=/tmp/k3s-ca.crt
--embed-certs=true
environment:
KUBECONFIG: "{{ ansible_env.HOME }}/.kube/config"
delegate_to: localhost
become: false
- name: Add K3s user credentials to kubeconfig
ansible.builtin.command: >
kubectl config set-credentials "{{ k3s_user_name }}"
--client-certificate=/tmp/k3s-client.crt
--client-key=/tmp/k3s-client.key
--embed-certs=true
environment:
KUBECONFIG: "{{ ansible_env.HOME }}/.kube/config"
delegate_to: localhost
become: false
- name: Add K3s context to kubeconfig
ansible.builtin.command: >
kubectl config set-context "{{ k3s_context_name }}"
--cluster="{{ k3s_cluster_name }}"
--user="{{ k3s_user_name }}"
environment:
KUBECONFIG: "{{ ansible_env.HOME }}/.kube/config"
delegate_to: localhost
become: false
- name: Clean up temporary certificate and key files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "/tmp/k3s-ca.crt"
- "/tmp/k3s-client.crt"
- "/tmp/k3s-client.key"
delegate_to: localhost
become: false

View File

@@ -1,26 +1,58 @@
---
- name: See if k3s file exists
ansible.builtin.stat:
path: /usr/local/bin/k3s
register: k3s_status
- name: Download K3s install script to /tmp/
when: not k3s_status.stat.exists
ansible.builtin.get_url:
url: https://get.k3s.io
dest: /tmp/k3s_install.sh
mode: "0755"
- name: Install K3s server with node taint and TLS SAN
when: (ansible_default_ipv4.address == k3s_primary_server_ip)
ansible.builtin.command: |
/tmp/k3s_install.sh server \
--node-taint CriticalAddonsOnly=true:NoExecute \
--tls-san {{ hostvars['k3s-loadbalancer'].ansible_default_ipv4.address }}
--tls-san {{ k3s_server_name }}
become: true
register: k3s_primary_install
- name: Install K3s on the secondary servers
when: (ansible_default_ipv4.address != k3s_primary_server_ip)
when: (host.ip == k3s.server.ips[0] and (not k3s_status.stat.exists))
ansible.builtin.command: |
/tmp/k3s_install.sh server \
--node-taint CriticalAddonsOnly=true:NoExecute \
--tls-san {{ k3s.loadbalancer.ip }}
environment:
K3S_TOKEN: "{{ k3s_token }}"
K3S_DATASTORE_ENDPOINT: "{{ k3s_db_connection_string }}"
become: true
async: 300
poll: 0
register: k3s_primary_install
- name: Wait for K3s to be installed
when: (host.ip == k3s.server.ips[0] and (not k3s_status.stat.exists))
ansible.builtin.async_status:
jid: "{{ k3s_primary_install.ansible_job_id }}"
register: k3s_primary_install_status
until: k3s_primary_install_status.finished
retries: 60
delay: 5
become: true
- name: Get K3s token from the first server
when: host.ip == k3s.server.ips[0]
ansible.builtin.slurp:
src: /var/lib/rancher/k3s/server/node-token
register: k3s_token
become: true
- name: Set fact on k3s.server.ips[0]
when: host.ip == k3s.server.ips[0]
ansible.builtin.set_fact:
k3s_token: "{{ k3s_token['content'] | b64decode | trim }}"
- name: Install K3s on the secondary servers
when: (host.ip != k3s.server.ips[0] and (not k3s_status.stat.exists))
ansible.builtin.command: |
/tmp/k3s_install.sh server \
--node-taint CriticalAddonsOnly=true:NoExecute \
--tls-san {{ k3s.loadbalancer.ip }}
environment:
K3S_DATASTORE_ENDPOINT: "{{ k3s_db_connection_string }}"
K3S_TOKEN: "{{ hostvars[(hostvars | dict2items | map(attribute='value') | map('dict2items') | map('selectattr', 'key', 'match', 'host') | map('selectattr', 'value.ip', 'match', k3s.server.ips[0] ) | select() | first | items2dict).host.hostname].k3s_token }}"
become: true

View File

@@ -1,21 +1,2 @@
---
- name: See if k3s file exists
ansible.builtin.stat:
path: /usr/local/bin/k3s
register: k3s_status
- include_tasks: installation.yml
when: not k3s_status.stat.exists
- include_tasks: create_kubeconfig.yml
when: ansible_default_ipv4.address == k3s_primary_server_ip
- name: Check if k3s token vault file already exists
ansible.builtin.stat:
path: "{{ playbook_dir }}/{{ k3s_server_token_vault_file }}"
register: k3s_vault_file_stat
delegate_to: localhost
run_once: true
- include_tasks: pull_token.yml
when: not k3s_vault_file_stat.stat.exists

View File

@@ -1,24 +0,0 @@
- name: Get K3s token from the first server
when:
- ansible_default_ipv4.address == k3s_primary_server_ip
ansible.builtin.slurp:
src: /var/lib/rancher/k3s/server/node-token
register: k3s_token
become: true
- name: Set fact on k3s_primary_server_ip
ansible.builtin.set_fact:
k3s_token: "{{ k3s_token['content'] | b64decode | trim }}"
- name: Write K3s token to local file for encryption
ansible.builtin.copy:
content: |
k3s_token: "{{ k3s_token }}"
dest: "{{ playbook_dir }}/{{ k3s_server_token_vault_file }}"
mode: "0600"
delegate_to: localhost
run_once: true
- name: Encrypt k3s token
ansible.builtin.shell: cd ../; ansible-vault encrypt "{{ playbook_dir }}/{{k3s_server_token_vault_file}}"
delegate_to: localhost

View File

@@ -1 +0,0 @@
k3s_server_token_vault_file: ../vars/group_vars/k3s/secrets_token.yml

View File

@@ -18,6 +18,6 @@
--node-taint storage=true:NoExecute \
--node-label longhorn=true
environment:
K3S_URL: "https://{{ hostvars['k3s-loadbalancer'].ansible_default_ipv4.address }}:{{ k3s.loadbalancer.default_port }}"
K3S_URL: "https://{{ k3s.loadbalancer.ip }}:{{ k3s.loadbalancer.default_port }}"
K3S_TOKEN: "{{ k3s_token }}"
become: true

View File

@@ -2,7 +2,7 @@
- name: Template the nginx config file with dynamic upstreams
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: "{{ k3s_loadbalancer_nginx_config_path }}"
dest: "{{ nginx_config_path }}"
owner: root
group: root
mode: "0644"
@@ -10,7 +10,7 @@
notify:
- Restart nginx
vars:
k3s_server_ips: "{{ k3s_primary_server_ip }}"
k3s_server_ips: "{{ k3s.server.ips }}"
- name: Enable nginx
ansible.builtin.systemd:

View File

@@ -0,0 +1,5 @@
---
- name: Installation
ansible.builtin.include_tasks: installation.yml
- name: Configure
ansible.builtin.include_tasks: configuration.yml

View File

@@ -0,0 +1,89 @@
include /etc/nginx/modules-enabled/*.conf;
events {}
stream {
# TCP Load Balancing for the K3s API
upstream k3s_servers {
{% for ip in k3s_server_ips %}
server {{ ip }}:{{k3s.loadbalancer.default_port}};
{% endfor %}
}
server {
listen {{k3s.loadbalancer.default_port}};
proxy_pass k3s_servers;
}
upstream dns_servers {
{% for ip in k3s_server_ips %}
server {{ ip }}:53;
{% endfor %}
}
server {
listen 53 udp;
proxy_pass dns_servers;
}
}
http {
upstream k3s_servers_http {
least_conn;
{% for ip in k3s_server_ips %}
server {{ ip }}:80;
{% endfor %}
}
upstream k3s_servers_https {
least_conn;
{% for ip in k3s_server_ips %}
server {{ ip }}:443;
{% endfor %}
}
server {
listen 80;
location / {
proxy_pass http://k3s_servers_http;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto http;
}
}
server {
listen 443 ssl;
server_name staging.k3s.seyshiro.de *.staging.k3s.seyshiro.de;
ssl_certificate /etc/nginx/ssl/staging_tls.crt;
ssl_certificate_key /etc/nginx/ssl/staging_tls.key;
location / {
proxy_pass https://k3s_servers_https;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
server {
listen 443 ssl;
server_name k3s.seyshiro.de *.k3s.seyshiro.de;
ssl_certificate /etc/nginx/ssl/production_tls.crt;
ssl_certificate_key /etc/nginx/ssl/production_tls.key;
location / {
proxy_pass https://k3s_servers_https;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
}

View File

@@ -0,0 +1 @@
nginx_config_path: "/etc/nginx/nginx.conf"

View File

@@ -0,0 +1,6 @@
---
- name: Restart postgres
ansible.builtin.systemd:
name: postgresql
state: restarted
become: true

View File

@@ -0,0 +1,10 @@
---
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
become: true
- name: Install ansible dependencies
ansible.builtin.apt:
name: "{{ ansible_dependencies }}"
become: true

View File

@@ -0,0 +1,69 @@
---
- name: "Create postgres user: {{ db.user }}"
community.postgresql.postgresql_user:
state: present
name: "{{ db.user }}"
password: "{{ db.password }}"
become: true
become_user: "{{ db.default_user.user }}"
vars:
ansible_remote_temp: "/tmp/"
- name: "Create database: {{ db.name }}"
community.postgresql.postgresql_db:
state: present
name: "{{ db.name }}"
encoding: UTF8
lc_collate: "en_US.UTF-8"
lc_ctype: "en_US.UTF-8"
become: true
become_user: postgres
vars:
ansible_remote_temp: "/tmp/"
- name: "Grant privileges to {{ db.user }}"
community.postgresql.postgresql_privs:
db: "{{ db.name }}"
privs: ALL
type: database
roles: "{{ db.user }}"
become: true
become_user: postgres
vars:
ansible_remote_temp: "/tmp/"
- name: "Grant all privileges on schema public to {{ db.user }};"
community.postgresql.postgresql_privs:
db: "{{ db.name }}"
privs: ALL
type: schema
obj: "public"
roles: "{{ db.user }}"
become: true
become_user: postgres
vars:
ansible_remote_temp: "/tmp/"
- name: "Allow md5 connection for the user {{ db.user }}"
community.postgresql.postgresql_pg_hba:
dest: "/etc/postgresql/15/main/pg_hba.conf"
contype: host
databases: all
method: md5
address: "{{ k3s.net }}"
users: "{{ db.user }}"
create: false
become: true
notify:
- Restart postgres
- name: "Set public listen address"
become: true
ansible.builtin.lineinfile:
dest: "/etc/postgresql/15/main/conf.d/listen.conf"
regexp: "^#?listen_addresses="
line: "listen_addresses='{{ db.listen_address | default('localhost') }}'"
state: present
mode: "644"
create: true
notify: "Restart postgres"

View File

@@ -0,0 +1,14 @@
---
- name: Install postgres
ansible.builtin.apt:
name: "{{ postgres_packages }}"
state: present
become: true
- name: Start and enable the service
ansible.builtin.systemd:
name: postgresql
state: started
daemon_reload: true
enabled: true
become: true

View File

@@ -0,0 +1,7 @@
---
- name: Install ansible dependencies for this role
ansible.builtin.include_tasks: ansible_deps.yml
- name: Install postgres
ansible.builtin.include_tasks: installation.yml
- name: Configure Database
ansible.builtin.include_tasks: configuration.yml

View File

@@ -0,0 +1,9 @@
ansible_dependencies:
- python3-pip
- python3-psycopg
- python3-pexpect
- acl
postgres_packages:
- postgresql
- postgresql-client

View File

@@ -0,0 +1,6 @@
---
- name: "Restart {{ bin_name }}"
ansible.builtin.service:
name: "{{ bin_name }}"
state: restarted
become: true

View File

@@ -0,0 +1,18 @@
---
- name: Determine latest GitHub release (local)
delegate_to: localhost
ansible.builtin.uri:
url: "https://api.github.com/repos/{{ repository }}/releases/{{ version }}"
body_format: json
register: _github_release
until: _github_release.status == 200
retries: 3
- name: Set version
ansible.builtin.set_fact:
tag: "{{ _github_release.json.tag_name
| regex_replace('^v?([0-9\\.]+)$', '\\1') }}"
- name: Set download_url
ansible.builtin.set_fact:
download_url: "https://github.com/{{ repository }}/releases/download/v{{ tag }}/{{ bin_name }}-{{ tag }}.linux-{{ go_arch }}.tar.gz"

View File

@@ -0,0 +1,29 @@
---
- name: Download/Extract "{{ download_url }}"
ansible.builtin.unarchive:
src: "{{ download_url }}"
dest: /tmp/
remote_src: true
mode: "755"
- name: "Move binary into path: {{ bin_path }}"
ansible.builtin.copy:
src: "/tmp/{{ bin_name }}-{{ tag }}.linux-{{ go_arch }}/{{ bin_name }}"
dest: "{{ bin_path }}"
mode: "755"
remote_src: true
become: true
- name: "Create user: {{ bin_name }}"
ansible.builtin.user:
name: "{{ bin_name }}"
shell: /sbin/nologin
state: present
become: true
- name: Copy the node_exporter systemd unit file.
ansible.builtin.template:
src: "{{ bin_name }}.service.j2"
dest: "/etc/systemd/system/{{ bin_name }}.service"
mode: "644"
become: true

View File

@@ -0,0 +1,7 @@
---
- name: Get Version
ansible.builtin.include_tasks: get_version.yml
- name: Install exporter
ansible.builtin.include_tasks: install.yml
- name: Create service
ansible.builtin.include_tasks: systemd.yml

View File

@@ -0,0 +1,10 @@
---
- name: "Ensure service is running and enabled: {{ bin_name }}"
ansible.builtin.service:
name: "{{ bin_name }}"
state: started
daemon_reload: true
enabled: true
notify:
- Restart "{{ bin_name }}"
become: true

View File

@@ -0,0 +1,14 @@
[Unit]
Description=PostgresExporter
[Service]
TimeoutStartSec=0
User={{ bin_name }}
ExecStart={{ bin_path }} --web.listen-address={{ host.ip }}:{{ bind_port }} {{ options }}
Environment="DATA_SOURCE_URI=localhost:5432/postgres?sslmode=disable"
Environment="DATA_SOURCE_USER={{ db.user }}"
Environment="DATA_SOURCE_PASS={{ db.password }}"
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,16 @@
go_arch_map:
i386: "386"
x86_64: "amd64"
aarch64: "arm64"
armv7l: "armv7"
armv6l: "armv6"
go_arch: "{{ go_arch_map[ansible_architecture] | default(ansible_architecture) }}"
repository: "prometheus-community/postgres_exporter"
bind_port: 9187
version: "latest"
serve: "localhost"
options: ""
bin_name: postgres_exporter
bin_path: "/usr/local/bin/{{ bin_name }}"

View File

@@ -1,83 +0,0 @@
#!/bin/bash
# Configuration
VM_ID=303
TARGET_IP="192.168.20.36" # Replace with the IP of your VM
PORT=22
CHECK_INTERVAL=300 # 5 minutes in seconds
LOG_FILE="/var/log/vm_monitor.log"
# Function to log messages
log_message() {
echo "$(date): $1" | tee -a $LOG_FILE
}
# Check if running on a Proxmox host
if ! command -v qm &>/dev/null; then
log_message "qm command not found. This script must run on a Proxmox host."
exit 1
fi
# Function to check port
check_port() {
# Try nc first if available
if command -v nc &>/dev/null; then
if nc -z -w 5 $TARGET_IP $PORT 2>/dev/null; then
return 0 # Port is open
else
return 1 # Port is closed
fi
# Fall back to nmap if nc is not available
elif command -v nmap &>/dev/null; then
if nmap -p $PORT $TARGET_IP | grep -q "$PORT/tcp.*open"; then
return 0 # Port is open
else
return 1 # Port is closed
fi
else
log_message "Neither nc nor nmap found. Please install one of them."
exit 1
fi
}
# Function to restart the VM
restart_vm() {
log_message "Port $PORT is not reachable. Restarting VM $VM_ID..."
# Stop the VM
qm stop $VM_ID
if [ $? -ne 0 ]; then
log_message "Failed to stop VM $VM_ID. Trying force stop..."
qm stop $VM_ID --force
fi
# Wait for VM to fully stop
log_message "Waiting for VM to stop..."
sleep 10
# Start the VM
qm start $VM_ID
if [ $? -ne 0 ]; then
log_message "Failed to start VM $VM_ID. Manual intervention required."
exit 1
fi
log_message "VM $VM_ID has been restarted."
}
# Main loop
log_message "Starting monitoring of VM $VM_ID on port $PORT..."
log_message "Press Ctrl+C to exit."
while true; do
# Check if port 22 is open
if ! check_port; then
restart_vm
else
log_message "Port $PORT is reachable. VM is running normally."
fi
# Wait for the next check
log_message "Sleeping for $CHECK_INTERVAL seconds..."
sleep $CHECK_INTERVAL
done

View File

@@ -1,6 +0,0 @@
---
- name: Reboot Node
ansible.builtin.reboot:
connect_timeout: 5
reboot_timeout: 600
test_command: whoami

View File

@@ -1,8 +0,0 @@
---
- name: Prepare Localhost
ansible.builtin.include_tasks: ./01_setup_localhost.yml
when: is_localhost
- name: Prepare Localhost
ansible.builtin.include_tasks: ./05_setup_node.yml
when: is_proxmox_node

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