initial commit
This commit is contained in:
commit
cf658a2846
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
### Example user template template
|
||||
### Example user template
|
||||
|
||||
# IntelliJ project files
|
||||
.idea
|
||||
*.iml
|
||||
out
|
||||
gen
|
||||
### Ansible template
|
||||
*.retry
|
||||
|
||||
# custom
|
||||
inventory.txt
|
||||
*.iso
|
||||
gitea-db.container
|
||||
gitea-srv.container
|
||||
nextcloud-db.container
|
||||
nextcloud-srv.container
|
||||
27
ansible/README.md
Normal file
27
ansible/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Ansible MicroOS VM setup
|
||||
|
||||
1. Install devsec hardening collection
|
||||
```shell
|
||||
ansible-galaxy collection install devsec.hardening
|
||||
```
|
||||
2. Create the inventory.txt file for the server
|
||||
3. Run the hardening playbook. Does not run completely through because of MicroOS immutability. At some point, a PR properly supporting MicroOS could be opened to https://github.com/dev-sec/ansible-os-hardening
|
||||
```shell
|
||||
ansible-playbook -i inventory.txt hardening.yml
|
||||
```
|
||||
4. Run the custom_hardening playbook. This mostly sets SSH parameters to best practice values.
|
||||
```shell
|
||||
ansible-playbook -i inventory.txt custom_hardening.yml
|
||||
```
|
||||
5. Run the allow_privileged_ports_rootless playbook. This allows a rootless traefik container to use ports 80 and 443.
|
||||
```shell
|
||||
ansible-playbook -i inventory.txt allow_privileged_ports_rootless.yml
|
||||
```
|
||||
6. Run the deploy_services playbook. This creates groups and users for each service, creates a btrfs subvolume for data and copies the quadlet files to the correct location, then activates the service.
|
||||
```shell
|
||||
ansible-playbook -i inventory.txt deploy_services.yml
|
||||
```
|
||||
7. Run the deploy_traefik_config playbool. This copies the traefik configuration to the correct location.
|
||||
```shell
|
||||
ansible-playbook -i inventory.txt deploy_traefik_config.yml
|
||||
```
|
||||
18
ansible/allow_privileged_ports_rootless.yml
Normal file
18
ansible/allow_privileged_ports_rootless.yml
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Allow normal users to bind to port 80
|
||||
hosts: all
|
||||
become: yes
|
||||
tasks:
|
||||
- name: Set sysctl to allow normal users to bind to ports starting from 80
|
||||
sysctl:
|
||||
name: net.ipv4.ip_unprivileged_port_start
|
||||
value: 80
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
- name: Verify the sysctl setting
|
||||
command: sysctl net.ipv4.ip_unprivileged_port_start
|
||||
register: sysctl_result
|
||||
|
||||
- debug:
|
||||
msg: "net.ipv4.ip_unprivileged_port_start: {{ sysctl_result.stdout }}"
|
||||
67
ansible/custom_hardening.yml
Normal file
67
ansible/custom_hardening.yml
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
- name: Apply SSH best practices configuration
|
||||
hosts: all
|
||||
become: yes
|
||||
tasks:
|
||||
- name: Ensure the sshd_config.d directory exists
|
||||
file:
|
||||
path: /etc/ssh/sshd_config.d
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
|
||||
- name: Create a configuration file to apply SSH best practices
|
||||
copy:
|
||||
content: |
|
||||
# Disable password authentication
|
||||
PasswordAuthentication no
|
||||
|
||||
# Disable challenge-response authentication
|
||||
ChallengeResponseAuthentication no
|
||||
|
||||
# Allow root login
|
||||
PermitRootLogin yes
|
||||
|
||||
# Disable empty passwords
|
||||
PermitEmptyPasswords no
|
||||
|
||||
# Disable X11 forwarding
|
||||
X11Forwarding no
|
||||
|
||||
# Use only protocol 2
|
||||
Protocol 2
|
||||
|
||||
# Log more verbosely
|
||||
LogLevel VERBOSE
|
||||
|
||||
# Keep-alive packets to ensure connection stability
|
||||
TCPKeepAlive yes
|
||||
ClientAliveInterval 60
|
||||
ClientAliveCountMax 10
|
||||
dest: /etc/ssh/sshd_config.d/best_practices.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Restart SSH service to apply changes
|
||||
service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
|
||||
- name: Verify SSH configuration settings
|
||||
shell: "sshd -T"
|
||||
register: ssh_config_result
|
||||
|
||||
- name: Check specific SSH settings
|
||||
debug:
|
||||
msg: "{{ ssh_config_result.stdout_lines | select('search', 'passwordauthentication no') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'challengeresponseauthentication no') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'permitrootlogin yes') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'permitemptypasswords no') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'x11forwarding no') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'protocol 2') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'loglevel verbose') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'clientaliveinterval 60') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'clientalivecountmax 3') | list }}\n
|
||||
{{ ssh_config_result.stdout_lines | select('search', 'tcpkeepalive yes') | list }}"
|
||||
82
ansible/deploy_services.yml
Normal file
82
ansible/deploy_services.yml
Normal file
@ -0,0 +1,82 @@
|
||||
- name: Deploy services
|
||||
hosts: all
|
||||
become: yes
|
||||
tasks:
|
||||
# Install base software for rootless podman containers
|
||||
- name: Check if systemd-container is installed
|
||||
ansible.builtin.command:
|
||||
cmd: "zypper se -i systemd-container"
|
||||
register: systemd_container_installed
|
||||
ignore_errors: yes
|
||||
- name: Check if podman is installed
|
||||
ansible.builtin.command:
|
||||
cmd: "zypper se -i podman"
|
||||
register: podman_installed
|
||||
ignore_errors: yes
|
||||
- name: Install software if not installed
|
||||
ansible.builtin.command:
|
||||
cmd: "transactional-update --non-interactive pkg in systemd-container podman"
|
||||
become: yes
|
||||
when: systemd_container_installed.rc != 0 or podman_installed.rc != 0
|
||||
register: software_installed
|
||||
- name: Reboot if software was installed
|
||||
ansible.builtin.reboot:
|
||||
when: software_installed.changed
|
||||
|
||||
# Deploy services as rootless containers
|
||||
- name: Deploy traefik
|
||||
include_role:
|
||||
name: rootless-podman-service
|
||||
vars:
|
||||
service_name: "traefik"
|
||||
systemd_service_name: "traefik"
|
||||
quadlet_template_src: "./my_service_templates"
|
||||
- name: Deploy wekantesting
|
||||
include_role:
|
||||
name: rootless-podman-service
|
||||
vars:
|
||||
service_name: "wekantesting"
|
||||
systemd_service_name: "null" #"wekantesting-pod"
|
||||
quadlet_template_src: "./my_service_templates"
|
||||
- name: Deploy wekan
|
||||
include_role:
|
||||
name: rootless-podman-service
|
||||
vars:
|
||||
service_name: "wekan"
|
||||
systemd_service_name: "null" #"wekan-pod"
|
||||
quadlet_template_src: "./my_service_templates"
|
||||
- name: Deploy gitea
|
||||
include_role:
|
||||
name: rootless-podman-service
|
||||
vars:
|
||||
service_name: "gitea"
|
||||
systemd_service_name: "gitea-pod"
|
||||
quadlet_template_src: "./my_service_templates"
|
||||
- name: Deploy mumble
|
||||
include_role:
|
||||
name: rootless-podman-service
|
||||
vars:
|
||||
service_name: "mumble"
|
||||
systemd_service_name: "mumble"
|
||||
quadlet_template_src: "./my_service_templates"
|
||||
- name: Deploy bitwarden
|
||||
include_role:
|
||||
name: rootless-podman-service
|
||||
vars:
|
||||
service_name: "bitwarden"
|
||||
systemd_service_name: "bitwarden"
|
||||
quadlet_template_src: "./my_service_templates"
|
||||
- name: Deploy actual
|
||||
include_role:
|
||||
name: rootless-podman-service
|
||||
vars:
|
||||
service_name: "actual"
|
||||
systemd_service_name: "actual"
|
||||
quadlet_template_src: "./my_service_templates"
|
||||
- name: Deploy nextcloud
|
||||
include_role:
|
||||
name: rootless-podman-service
|
||||
vars:
|
||||
service_name: "nextcloud"
|
||||
systemd_service_name: "nextcloud-pod"
|
||||
quadlet_template_src: "./my_service_templates"
|
||||
11
ansible/deploy_traefik_config.yml
Normal file
11
ansible/deploy_traefik_config.yml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Copy Traefik configuration files to the server
|
||||
hosts: all
|
||||
become: yes
|
||||
tasks:
|
||||
- name: Synchronize Traefik configuration files
|
||||
synchronize:
|
||||
src: ./traefik_config/
|
||||
dest: /var/vol/traefik/
|
||||
rsync_opts:
|
||||
- "--chown=traefik:traefik"
|
||||
89
ansible/hardening.yml
Normal file
89
ansible/hardening.yml
Normal file
@ -0,0 +1,89 @@
|
||||
- name: Apply DevSec hardening
|
||||
hosts: all
|
||||
become: yes
|
||||
vars:
|
||||
sysctl_overwrite:
|
||||
# Enable IPv4 traffic forwarding. Needed for containers.
|
||||
net.ipv4.ip_forward: 1
|
||||
os_security_users_allow:
|
||||
- "change_user" # Ensure this user is allowed to avoid modifying /bin/su (does not work on read-only filesystems)
|
||||
os_family: "Suse"
|
||||
os_release: "Tumbleweed" # Treat MicroOS as Tumbleweed
|
||||
os_version: "{{ ansible_distribution_version }}"
|
||||
os_vars:
|
||||
packages:
|
||||
- sudo
|
||||
- openssh
|
||||
ignore_fs_types:
|
||||
- squashfs
|
||||
- iso9660
|
||||
- vfat
|
||||
auth_pam:
|
||||
- common-password
|
||||
- common-auth
|
||||
- common-account
|
||||
- common-session
|
||||
pam_passwords:
|
||||
- password requisite pam_pwquality.so retry=3
|
||||
- password required pam_unix.so use_authtok remember=5 sha512 shadow
|
||||
securetty: [console, tty1, tty2, tty3, tty4, tty5, tty6]
|
||||
sshd:
|
||||
package: openssh
|
||||
service: sshd
|
||||
config: /etc/ssh/sshd_config
|
||||
kernel_modules_disabled:
|
||||
- cramfs
|
||||
- freevxfs
|
||||
- jffs2
|
||||
- hfs
|
||||
- hfsplus
|
||||
- squashfs
|
||||
- udf
|
||||
- vfat
|
||||
auditd_package: audit # This is the correct package name for auditd in openSUSE
|
||||
os_env_umask: "027" # Setting a default umask value
|
||||
os_auth_uid_min: "1000" # Setting the minimum user ID for non-system users
|
||||
os_auth_uid_max: "60000" # Setting the maximum user ID for non-system users
|
||||
os_auth_gid_min: 1000
|
||||
os_auth_gid_max: 60000
|
||||
os_auth_sys_uid_min: "100" # Setting the minimum user ID for system users
|
||||
os_auth_sys_uid_max: "499" # Setting the maximum user ID for system users
|
||||
os_auth_sys_gid_min: 100
|
||||
os_auth_sys_gid_max: 499
|
||||
os_auth_sub_uid_min: 100000
|
||||
os_auth_sub_uid_max: 600100000
|
||||
os_auth_sub_uid_count: 65536
|
||||
os_auth_sub_gid_min: 100000
|
||||
os_auth_sub_gid_max: 600100000
|
||||
os_auth_sub_gid_count: 65536
|
||||
os_shadow_perms:
|
||||
owner: root
|
||||
group: shadow
|
||||
mode: "0640"
|
||||
os_passwd_perms:
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
hidepid_option: "2" # allowed values: 0, 1, 2
|
||||
os_mnt_boot_group: 'root'
|
||||
os_mnt_boot_owner: 'root'
|
||||
os_mnt_dev_group: 'root'
|
||||
os_mnt_dev_owner: 'root'
|
||||
os_mnt_dev_shm_group: 'root'
|
||||
os_mnt_dev_shm_owner: 'root'
|
||||
os_mnt_home_group: 'root'
|
||||
os_mnt_home_owner: 'root'
|
||||
os_mnt_run_group: 'root'
|
||||
os_mnt_run_owner: 'root'
|
||||
os_mnt_tmp_group: 'root'
|
||||
os_mnt_tmp_owner: 'root'
|
||||
os_mnt_var_group: 'root'
|
||||
os_mnt_var_owner: 'root'
|
||||
os_mnt_var_log_group: 'root'
|
||||
os_mnt_var_log_owner: 'root'
|
||||
os_mnt_var_log_audit_group: 'root'
|
||||
os_mnt_var_log_audit_owner: 'root'
|
||||
os_mnt_var_tmp_group: 'root'
|
||||
os_mnt_var_tmp_owner: 'root'
|
||||
roles:
|
||||
- devsec.hardening.os_hardening
|
||||
17
ansible/my_service_templates/actual/actual.container
Normal file
17
ansible/my_service_templates/actual/actual.container
Normal file
@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Actual deployment
|
||||
|
||||
[Container]
|
||||
ContainerName=actual
|
||||
Image=docker.io/actualbudget/actual-server:latest
|
||||
PublishPort=127.0.0.1:8500:5006
|
||||
Volume=/var/vol/actual:/data:Z
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
# Restart service when sleep finishes
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
17
ansible/my_service_templates/bitwarden/bitwarden.container
Normal file
17
ansible/my_service_templates/bitwarden/bitwarden.container
Normal file
@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Bitwarden deployment
|
||||
|
||||
[Container]
|
||||
ContainerName=bitwarden
|
||||
Image=docker.io/vaultwarden/server:latest
|
||||
PublishPort=127.0.0.1:8400:80
|
||||
Volume=/var/vol/bitwarden:/data:Z
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
# Restart service when sleep finishes
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Gitea database
|
||||
|
||||
[Container]
|
||||
ContainerName=gitea-db
|
||||
Image=docker.io/postgres:14
|
||||
Volume=/var/vol/gitea/db:/var/lib/postgresql/data:Z
|
||||
Environment=LANG=en_US.utf8
|
||||
Environment=PGDATA=/var/lib/postgresql/data/pgdata
|
||||
Environment=POSTGRES_USER=<REDACTED>
|
||||
Environment=POSTGRES_PASSWORD=<REDACTED>
|
||||
AutoUpdate=registry
|
||||
Pod=gitea.pod
|
||||
@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Gitea server
|
||||
|
||||
[Container]
|
||||
ContainerName=gitea-srv
|
||||
Image=docker.io/gitea/gitea:latest
|
||||
Volume=/var/vol/gitea/data:/data:Z
|
||||
Environment=USER_UID=1000
|
||||
Environment=USER_GID=1000
|
||||
Environment=GITEA__database__DB_TYPE=postgres
|
||||
Environment=GITEA__database__DB_HOST=127.0.0.1:5432
|
||||
Environment=GITEA__database__DB_NAME=<REDACTED>
|
||||
Environment=GITEA__database__DB_USER=<REDACTED>
|
||||
Environment=GITEA__database__DB_PASSWD=<REDACTED>
|
||||
AutoUpdate=registry
|
||||
Pod=gitea.pod
|
||||
10
ansible/my_service_templates/gitea/gitea.pod
Normal file
10
ansible/my_service_templates/gitea/gitea.pod
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Gitea deployment
|
||||
|
||||
[Pod]
|
||||
PodName=gitea
|
||||
PublishPort=127.0.0.1:8300:3000,7722:22
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
17
ansible/my_service_templates/mumble/mumble.container
Normal file
17
ansible/my_service_templates/mumble/mumble.container
Normal file
@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Mumble deployment
|
||||
|
||||
[Container]
|
||||
ContainerName=mumble
|
||||
Image=docker.io/mumblevoip/mumble-server:latest
|
||||
PublishPort=64738:64738,64738:64738/udp
|
||||
Volume=/var/vol/mumble:/data:Z
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
# Restart service when sleep finishes
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Nextcloud database
|
||||
|
||||
[Container]
|
||||
ContainerName=nextcloud-db
|
||||
Image=docker.io/postgres:12
|
||||
Volume=/var/vol/nextcloud/db:/var/lib/postgresql/data:Z
|
||||
Environment=LANG=en_US.utf8
|
||||
Environment=PGDATA=/var/lib/postgresql/data/pgdata
|
||||
Environment=POSTGRES_USER=<REDACTED>
|
||||
Environment=POSTGRES_PASSWORD=<REDACTED>
|
||||
AutoUpdate=registry
|
||||
Pod=nextcloud.pod
|
||||
@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Nextcloud server
|
||||
|
||||
[Container]
|
||||
ContainerName=nextcloud-srv
|
||||
Image=docker.io/nextcloud:27
|
||||
Volume=/var/vol/nextcloud/data:/var/www/html:Z
|
||||
Environment=USER_UID=1000
|
||||
Environment=USER_GID=1000
|
||||
Environment=PHP_MEMORY_LIMIT=4G
|
||||
Environment=POSTGRES_HOST=127.0.0.1:5432
|
||||
Environment=POSTGRES_DB=<REDACTED>
|
||||
Environment=POSTGRES_USER=<REDACTED>
|
||||
Environment=POSTGRES_PASSWORD=<REDACTED>
|
||||
AutoUpdate=registry
|
||||
Pod=nextcloud.pod
|
||||
10
ansible/my_service_templates/nextcloud/nextcloud.pod
Normal file
10
ansible/my_service_templates/nextcloud/nextcloud.pod
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Nextcloud deployment
|
||||
|
||||
[Pod]
|
||||
PodName=nextcloud
|
||||
PublishPort=127.0.0.1:8600:80
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
17
ansible/my_service_templates/traefik/traefik.container
Normal file
17
ansible/my_service_templates/traefik/traefik.container
Normal file
@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Ingress for this server
|
||||
|
||||
[Container]
|
||||
ContainerName=traefik
|
||||
Image=docker.io/traefik:latest
|
||||
Network=host
|
||||
Volume=/var/vol/traefik:/etc/traefik:Z
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
# Restart service when sleep finishes
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
9
ansible/my_service_templates/wekan/wekan-db.container
Normal file
9
ansible/my_service_templates/wekan/wekan-db.container
Normal file
@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Wekan database
|
||||
|
||||
[Container]
|
||||
ContainerName=wekan-db
|
||||
Image=docker.io/mongo:6
|
||||
Volume=/var/vol/wekan/db:/data/db:Z
|
||||
AutoUpdate=registry
|
||||
Pod=wekan.pod
|
||||
10
ansible/my_service_templates/wekan/wekan-srv.container
Normal file
10
ansible/my_service_templates/wekan/wekan-srv.container
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Wekan server
|
||||
|
||||
[Container]
|
||||
ContainerName=wekan-srv
|
||||
Image=ghcr.io/wekan/wekan:v7.09-arm64
|
||||
Environment=ROOT_URL=https://wekan.rohrschacht.de
|
||||
Environment=MONGO_URL=mongodb://127.0.0.1:27017/wekan
|
||||
AutoUpdate=registry
|
||||
Pod=wekan.pod
|
||||
10
ansible/my_service_templates/wekan/wekan.pod
Normal file
10
ansible/my_service_templates/wekan/wekan.pod
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Wekan deployment
|
||||
|
||||
[Pod]
|
||||
PodName=wekan
|
||||
PublishPort=8100:8080
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Wekan testing database
|
||||
|
||||
[Container]
|
||||
ContainerName=wekantesting-db
|
||||
Image=docker.io/mongo:6
|
||||
Volume=/var/vol/wekantesting/db:/data/db:Z
|
||||
AutoUpdate=registry
|
||||
Pod=wekantesting.pod
|
||||
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Wekan testing server
|
||||
|
||||
[Container]
|
||||
ContainerName=wekantesting-srv
|
||||
Image=ghcr.io/wekan/wekan:v7.09-arm64
|
||||
Environment=ROOT_URL=https://wekantesting.rohrschacht.de
|
||||
Environment=MONGO_URL=mongodb://127.0.0.1:27017/wekan
|
||||
AutoUpdate=registry
|
||||
Pod=wekantesting.pod
|
||||
10
ansible/my_service_templates/wekantesting/wekantesting.pod
Normal file
10
ansible/my_service_templates/wekantesting/wekantesting.pod
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Wekan testing deployment
|
||||
|
||||
[Pod]
|
||||
PodName=wekantesting
|
||||
PublishPort=8200:8080
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
6
ansible/rootless-podman-service/defaults/main.yml
Normal file
6
ansible/rootless-podman-service/defaults/main.yml
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
service_name: "default_service"
|
||||
systemd_service_name: "default_service"
|
||||
btrfs_base_path: "/var/vol"
|
||||
quadlet_template_src: "./templates"
|
||||
force_systemd_restart: false
|
||||
18
ansible/rootless-podman-service/tasks/copy_quadlet_files.yml
Normal file
18
ansible/rootless-podman-service/tasks/copy_quadlet_files.yml
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Ensure Quadlet configuration directory exists
|
||||
ansible.builtin.file:
|
||||
path: "/home/{{ service_name }}/.config/containers/systemd"
|
||||
state: directory
|
||||
owner: "{{ service_name }}"
|
||||
group: "{{ service_name }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Copy Quadlet files to the user's systemd directory
|
||||
ansible.builtin.copy:
|
||||
src: "{{ quadlet_template_src }}/{{ service_name }}/"
|
||||
dest: "/home/{{ service_name }}/.config/containers/systemd/"
|
||||
owner: "{{ service_name }}"
|
||||
group: "{{ service_name }}"
|
||||
mode: '0644'
|
||||
remote_src: no
|
||||
register: quadlet_files_copied
|
||||
@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Ensure Btrfs base path exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ btrfs_base_path }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create Btrfs subvolume
|
||||
ansible.builtin.command:
|
||||
cmd: "btrfs subvolume create {{ btrfs_base_path }}/{{ service_name }}"
|
||||
args:
|
||||
creates: "{{ btrfs_base_path }}/{{ service_name }}"
|
||||
|
||||
- name: Set permissions for Btrfs subvolume
|
||||
ansible.builtin.file:
|
||||
path: "{{ btrfs_base_path }}/{{ service_name }}"
|
||||
owner: "{{ service_name }}"
|
||||
group: "{{ service_name }}"
|
||||
14
ansible/rootless-podman-service/tasks/create_user.yml
Normal file
14
ansible/rootless-podman-service/tasks/create_user.yml
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Ensure group is present
|
||||
ansible.builtin.group:
|
||||
name: "{{ service_name }}"
|
||||
state: present
|
||||
|
||||
- name: Ensure user is present
|
||||
ansible.builtin.user:
|
||||
name: "{{ service_name }}"
|
||||
group: "{{ service_name }}"
|
||||
state: present
|
||||
shell: /bin/bash
|
||||
home: "/home/{{ service_name }}"
|
||||
create_home: yes
|
||||
4
ansible/rootless-podman-service/tasks/enable_linger.yml
Normal file
4
ansible/rootless-podman-service/tasks/enable_linger.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
- name: Enable linger for the user
|
||||
ansible.builtin.command:
|
||||
cmd: "loginctl enable-linger {{ service_name }}"
|
||||
16
ansible/rootless-podman-service/tasks/enable_service.yml
Normal file
16
ansible/rootless-podman-service/tasks/enable_service.yml
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
- name: Check if service is already running
|
||||
ansible.builtin.command:
|
||||
cmd: "machinectl shell {{ service_name }}@ /bin/bash -c 'systemctl --user is-active {{ systemd_service_name }}' | grep -qv inactive"
|
||||
register: service_status
|
||||
ignore_errors: yes
|
||||
- name: Enable and start the main service
|
||||
ansible.builtin.command:
|
||||
cmd: "machinectl shell {{ service_name }}@ /bin/bash -c 'systemctl --user daemon-reload && systemctl --user start {{ systemd_service_name }}'"
|
||||
become: yes
|
||||
when: service_status.rc != 0 and (quadlet_files_copied.changed or force_systemd_restart)
|
||||
- name: Restart the main service
|
||||
ansible.builtin.command:
|
||||
cmd: "machinectl shell {{ service_name }}@ /bin/bash -c 'systemctl --user daemon-reload && systemctl --user restart {{ systemd_service_name }}'"
|
||||
become: yes
|
||||
when: service_status.rc == 0 and (quadlet_files_copied.changed or force_systemd_restart)
|
||||
15
ansible/rootless-podman-service/tasks/main.yml
Normal file
15
ansible/rootless-podman-service/tasks/main.yml
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Create user and group
|
||||
include_tasks: create_user.yml
|
||||
|
||||
- name: Create Btrfs subvolume
|
||||
include_tasks: create_btrfs_subvolume.yml
|
||||
|
||||
- name: Enable linger for the user
|
||||
include_tasks: enable_linger.yml
|
||||
|
||||
- name: Copy Quadlet files
|
||||
include_tasks: copy_quadlet_files.yml
|
||||
|
||||
- name: Enable and start main service
|
||||
include_tasks: enable_service.yml
|
||||
92
ansible/traefik_config/dynamic.yml
Normal file
92
ansible/traefik_config/dynamic.yml
Normal file
@ -0,0 +1,92 @@
|
||||
http:
|
||||
routers:
|
||||
# Router for wekan.rohrschacht.de
|
||||
wekan-router:
|
||||
rule: "Host(`wekan.rohrschacht.de`)"
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
service: wekan-service
|
||||
|
||||
# Router for wekantesting.rohrschacht.de
|
||||
wekantesting-router:
|
||||
rule: "Host(`wekantesting.rohrschacht.de`)"
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
service: wekantesting-service
|
||||
|
||||
# Router for git.rohrschacht.de
|
||||
git-router:
|
||||
rule: "Host(`git.rohrschacht.de`) || Host(`gitea.rohrschacht.de`)"
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
service: gitea-service
|
||||
|
||||
# Router for vault.rohrschacht.de
|
||||
vault-router:
|
||||
rule: "Host(`vault.rohrschacht.de`)"
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
service: vault-service
|
||||
|
||||
# Router for actual.rohrschacht.de
|
||||
actual-router:
|
||||
rule: "Host(`actual.rohrschacht.de`)"
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
service: actual-service
|
||||
|
||||
# Router for nextcloud.rohrschacht.de
|
||||
nextcloud-router:
|
||||
rule: "Host(`nextcloud.rohrschacht.de`)"
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
service: nextcloud-service
|
||||
|
||||
services:
|
||||
# Service for wekan.rohrschacht.de
|
||||
wekan-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:8100"
|
||||
|
||||
# Service for wekantesting.rohrschacht.de
|
||||
wekantesting-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:8200"
|
||||
|
||||
# Service for gitea.rohrschacht.de
|
||||
gitea-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:8300"
|
||||
|
||||
# Service for vault.rohrschacht.de
|
||||
vault-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:8400"
|
||||
|
||||
# Service for vault.rohrschacht.de
|
||||
actual-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:8500"
|
||||
|
||||
# Service for nextcloud.rohrschacht.de
|
||||
nextcloud-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:8600"
|
||||
27
ansible/traefik_config/traefik.yml
Normal file
27
ansible/traefik_config/traefik.yml
Normal file
@ -0,0 +1,27 @@
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
permanent: true
|
||||
websecure:
|
||||
address: ":443"
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
email: tobias@rohrschacht.de
|
||||
storage: /etc/traefik/acme.json
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
|
||||
api:
|
||||
dashboard: false
|
||||
|
||||
providers:
|
||||
file:
|
||||
directory: /etc/traefik
|
||||
watch: true
|
||||
12
ignition/README.md
Normal file
12
ignition/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Ignition public SSH key setup
|
||||
|
||||
1. Configure ssh public key in ignition-config.yml
|
||||
2. Run butane to generate the ignition file
|
||||
```shell
|
||||
podman run --interactive --rm quay.io/coreos/butane:release --pretty --strict < ignition-config.yml > disk/ignition/config.ign
|
||||
```
|
||||
3. Create the disk image
|
||||
```shell
|
||||
./generate-iso.sh
|
||||
```
|
||||
4. Load ignition.iso as DVD image into the VM after setting it up with the microos VM image, before the first boot
|
||||
15
ignition/disk/ignition/config.ign
Normal file
15
ignition/disk/ignition/config.ign
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"ignition": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"passwd": {
|
||||
"users": [
|
||||
{
|
||||
"name": "root",
|
||||
"sshAuthorizedKeys": [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH+lOLplrdWardSEdw3aiEKj/P59UZwxpqQShfSVID/b"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
8
ignition/generate-iso.sh
Executable file
8
ignition/generate-iso.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkisofs -max-iso9660-filenames \
|
||||
-untranslated-filenames \
|
||||
-allow-multidot \
|
||||
-omit-period \
|
||||
-V ignition \
|
||||
-o "${1:-ignition.iso}" disk
|
||||
8
ignition/ignition-config.yml
Normal file
8
ignition/ignition-config.yml
Normal file
@ -0,0 +1,8 @@
|
||||
variant: fcos
|
||||
version: 1.0.0
|
||||
passwd:
|
||||
users:
|
||||
- name: root
|
||||
ssh_authorized_keys:
|
||||
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH+lOLplrdWardSEdw3aiEKj/P59UZwxpqQShfSVID/b
|
||||
|
||||
Loading…
Reference in New Issue
Block a user