initial commit
This commit is contained in:
88
group_vars/all/vars.yml
Normal file
88
group_vars/all/vars.yml
Normal file
@@ -0,0 +1,88 @@
|
||||
# ============================================================
|
||||
# Stoat.chat Fedora 44 - variables
|
||||
# ============================================================
|
||||
# Single source of truth for every tunable used by the stoat role.
|
||||
# Override on the command line like:
|
||||
# ansible-playbook -i inventory.ini site.yml -e stoat_domain=chat.example.com
|
||||
# ============================================================
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# >>> REQUIRED <<<
|
||||
# The domain that has an A/AAAA record pointing to this server.
|
||||
# Let's Encrypt certificates (via the built-in Caddy) are issued
|
||||
# for this name, and every URL Stoat advertises uses it.
|
||||
# Example: stoat.example.com (NOT the public stoat.chat domain)
|
||||
# ------------------------------------------------------------
|
||||
stoat_domain: chat.example.com
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Stoat application
|
||||
# ------------------------------------------------------------
|
||||
stoat_repo: https://github.com/stoatchat/self-hosted
|
||||
stoat_install_dir: /opt/stoat/self-hosted
|
||||
stoat_version: main
|
||||
|
||||
# Place Stoat behind an existing reverse proxy?
|
||||
# false (recommended): the built-in Caddy terminates TLS on 80/443
|
||||
# true: Caddy is moved to localhost:8880 and you must
|
||||
# provide your own front proxy + certificate
|
||||
stoat_reverse_proxy: false
|
||||
|
||||
# Enable camera / screen sharing via LiveKit?
|
||||
# Requires firewall ports 7881/tcp and 50000-50100/udp (see below)
|
||||
stoat_video: true
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Podman (rootless)
|
||||
# ------------------------------------------------------------
|
||||
# Unprivileged system user that runs the rootless podman stack
|
||||
stoat_service_user: stoat
|
||||
|
||||
podman_packages:
|
||||
- podman
|
||||
- podman-compose # compose file runner (supports service_healthy conditions)
|
||||
- podman-docker # `docker` CLI compatibility, backed by podman
|
||||
|
||||
# Rootless containers cannot bind ports < 1024 by default.
|
||||
# Caddy needs 80/443, so lower the unprivileged port floor.
|
||||
podman_unprivileged_port_start: 80
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Hardening - Fail2Ban
|
||||
# ------------------------------------------------------------
|
||||
fail2ban_port: "22"
|
||||
fail2ban_maxretry: 3 # failed attempts before a ban
|
||||
fail2ban_bantime: 3600 # ban length in seconds (1 hour)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Hardening - SSH
|
||||
# ------------------------------------------------------------
|
||||
sshd_permit_root_login: no
|
||||
sshd_password_authentication: no
|
||||
|
||||
# Enable ONLY if a TOTP / WebUI-2FA PAM module is already
|
||||
# configured in /etc/pam.d/sshd - otherwise you can lock yourself
|
||||
# out. Leave false unless you know what you are doing.
|
||||
sshd_mfa_required: false
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Hardening - firewall (firewalld, default deny)
|
||||
# ------------------------------------------------------------
|
||||
# Services: ssh for administration, http/https for Caddy TLS
|
||||
firewall_services:
|
||||
- ssh
|
||||
- http
|
||||
- https
|
||||
|
||||
# Ports: LiveKit voice/video (signaling tcp + media udp range)
|
||||
firewall_ports:
|
||||
- "7881/tcp"
|
||||
- "50000-50100/udp"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Base packages
|
||||
# ------------------------------------------------------------
|
||||
base_packages:
|
||||
- git # clone the stoat repo
|
||||
- curl
|
||||
- openssl # generate_config.sh secret generation
|
||||
@@ -1,186 +0,0 @@
|
||||
---
|
||||
# --------------------------------------------------------------
|
||||
# Ansible playbook: Harden a Fedora 44 server & install Fail2Ban
|
||||
# --------------------------------------------------------------
|
||||
# Requirements:
|
||||
# - Ansible >= 2.14
|
||||
# - A reachable Fedora 44 host with SSH access (password or key)
|
||||
# - `sudo` rights for the control user (via become: true)
|
||||
# --------------------------------------------------------------
|
||||
|
||||
- name: Harden Fedora 44 & install Fail2Ban
|
||||
hosts: all
|
||||
become: true # elevates to root for package/service ops
|
||||
vars:
|
||||
# ---- Tunables -------------------------------------------------
|
||||
# Change these to match your environment
|
||||
fail2ban_port: "22" # default SSH jail; can be "http","https",etc.
|
||||
fail2ban_maxretry: 3 # how many attempts before ban
|
||||
fail2ban_bantime: 3600 # seconds (1 h)
|
||||
# -----------------------------------------------------------------
|
||||
# Ensure we are on Fedora 44 (you can relax this if you wish)
|
||||
fedora_release: "44"
|
||||
tasks:
|
||||
# --------------------------------------------------------------
|
||||
# Verify OS version (informational, can be removed)
|
||||
# --------------------------------------------------------------
|
||||
- name: Gather os fact
|
||||
ansible.builtin.debug:
|
||||
var: ansible_distribution_version
|
||||
|
||||
- name: Ensure we are running Fedora {{ fedora_release }}
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_distribution == "Fedora"
|
||||
- ansible_distribution_version == fedora_release
|
||||
fail_msg: "This playbook targets Fedora {{ fedora_release }} only."
|
||||
success_msg: "Running on the correct Fedora version."
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Update / upgrade all packages
|
||||
# --------------------------------------------------------------
|
||||
- name: Update all packages to latest versions
|
||||
ansible.builtin.dnf:
|
||||
name: "*"
|
||||
state: latest
|
||||
update_cache: true
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Install essential hardening packages
|
||||
# --------------------------------------------------------------
|
||||
- name: Install fail2ban, firewalld, and sudo
|
||||
ansible.builtin.dnf:
|
||||
name:
|
||||
- fail2ban
|
||||
- firewalld
|
||||
- sudo
|
||||
state: present
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Start & enable firewalld (default-deny all, allow SSH)
|
||||
# --------------------------------------------------------------
|
||||
- name: Start and enable firewalld
|
||||
ansible.builtin.systemd:
|
||||
name: firewalld
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Allow SSH service through firewalld
|
||||
ansible.builtin.command:
|
||||
cmd: firewall-cmd --permanent --add-service=ssh
|
||||
changed_when: false # idempotent check handled by firewall_cmd
|
||||
|
||||
- name: Reload firewalld to apply rules
|
||||
ansible.builtin.command:
|
||||
cmd: firewall-cmd --reload
|
||||
changed_when: false
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Harden SSH (disable root login, enforce key‑only auth)
|
||||
# --------------------------------------------------------------
|
||||
- name: Ensure SSH config directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/ssh/sshd_config.d
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Disable root login via SSH
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config.d/00-disable-root.conf
|
||||
create: true
|
||||
mode: "0644"
|
||||
line: "PermitRootLogin no"
|
||||
state: present
|
||||
|
||||
- name: Enforce password-auth disabled (key-only)
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config.d/00-key-only.conf
|
||||
create: true
|
||||
mode: "0644"
|
||||
line: "PasswordAuthentication no"
|
||||
state: present
|
||||
|
||||
- name: Require MFA (optional) — add if you use Duo, etc.
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config.d/00-mfa.conf
|
||||
create: true
|
||||
mode: "0644"
|
||||
line: "AuthenticationMethods publickey,keyboard-interactive:pam"
|
||||
state: present
|
||||
|
||||
- name: Restart sshd to apply changes
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Configure Fail2Ban
|
||||
# --------------------------------------------------------------
|
||||
- name: template fail2ban jail.local
|
||||
ansible.builtin.template:
|
||||
src: jail.local.j2
|
||||
dest: /etc/fail2ban/jail.local
|
||||
mode: "0644"
|
||||
notify: Restart fail2ban
|
||||
|
||||
- name: Ensure fail2ban is started & enabled
|
||||
ansible.builtin.service:
|
||||
name: fail2ban
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# (Optional) Install & configure unattended‑security updates
|
||||
# --------------------------------------------------------------
|
||||
- name: Install dnf-automatic (security-only)
|
||||
ansible.builtin.dnf:
|
||||
name: dnf-automatic
|
||||
state: present
|
||||
|
||||
- name: Enable security-only automatic updates
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/dnf/automatic.conf
|
||||
regexp: "^update_type"
|
||||
line: "update_type = security"
|
||||
state: present
|
||||
|
||||
- name: Enable and start dnf-automatic.timer
|
||||
ansible.builtin.systemd:
|
||||
name: "@dnf-automatic.timer"
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Log rotation for security‑relevant logs
|
||||
# --------------------------------------------------------------
|
||||
- name: Ensure rsyslog is running
|
||||
ansible.builtin.service:
|
||||
name: rsyslog
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Add logrotate rule for fail2ban logs
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/logrotate.d/fail2ban
|
||||
create: true
|
||||
mode: "0644"
|
||||
content: |
|
||||
/var/log/fail2ban/*.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
missingok
|
||||
notifempty
|
||||
postrotate
|
||||
/usr/bin/fail2ban-client reload >/dev/null 2>&1 || true
|
||||
endscript
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# 9 Handlers
|
||||
# --------------------------------------------------------------
|
||||
handlers:
|
||||
- name: Restart fail2ban
|
||||
ansible.builtin.service:
|
||||
name: fail2ban
|
||||
state: restarted
|
||||
10
roles/stoat/handlers/main.yml
Normal file
10
roles/stoat/handlers/main.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# Handlers for the stoat role
|
||||
- name: Reload firewalld
|
||||
ansible.builtin.command: firewall-cmd --reload
|
||||
changed_when: false
|
||||
|
||||
- name: Restart fail2ban
|
||||
ansible.builtin.systemd:
|
||||
name: fail2ban
|
||||
state: restarted
|
||||
130
roles/stoat/tasks/harden.yml
Normal file
130
roles/stoat/tasks/harden.yml
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
# ------------------------------------------------------------
|
||||
# Firewall (firewalld) - default deny, allow only what is needed
|
||||
# ------------------------------------------------------------
|
||||
- name: Start and enable firewalld
|
||||
ansible.builtin.systemd:
|
||||
name: firewalld
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Check which firewall services are already allowed
|
||||
ansible.builtin.command: "firewall-cmd --query-service={{ item }}"
|
||||
loop: "{{ firewall_services }}"
|
||||
register: fw_service_check
|
||||
changed_when: false
|
||||
failed_when: fw_service_check.rc not in [0, 1]
|
||||
|
||||
- name: Allow firewall services (ssh, http, https)
|
||||
ansible.builtin.command: "firewall-cmd --permanent --add-service={{ item.item }}"
|
||||
loop: "{{ fw_service_check.results }}"
|
||||
when: item.rc | int != 0
|
||||
notify: Reload firewalld
|
||||
|
||||
- name: Check which firewall ports are already allowed
|
||||
ansible.builtin.command: "firewall-cmd --query-port={{ item }}"
|
||||
loop: "{{ firewall_ports }}"
|
||||
register: fw_port_check
|
||||
changed_when: false
|
||||
failed_when: fw_port_check.rc not in [0, 1]
|
||||
|
||||
- name: Allow firewall ports (LiveKit voice/video)
|
||||
ansible.builtin.command: "firewall-cmd --permanent --add-port={{ item.item }}"
|
||||
loop: "{{ fw_port_check.results }}"
|
||||
when: item.rc | int != 0
|
||||
notify: Reload firewalld
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# SSH hardening (Drop-in config, validated before restart)
|
||||
# ------------------------------------------------------------
|
||||
- name: Deploy sshd hardening drop-in
|
||||
ansible.builtin.template:
|
||||
src: sshd_hardening.conf.j2
|
||||
dest: /etc/ssh/sshd_config.d/00-hardening.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
register: sshd_conf
|
||||
|
||||
- name: Validate sshd configuration
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
when: sshd_conf is changed
|
||||
|
||||
- name: Restart sshd
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
when: sshd_conf is changed
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Fail2Ban - brute force protection
|
||||
# ------------------------------------------------------------
|
||||
- name: Install fail2ban
|
||||
ansible.builtin.dnf:
|
||||
name: fail2ban
|
||||
state: present
|
||||
|
||||
- name: Deploy fail2ban jail.local
|
||||
ansible.builtin.template:
|
||||
src: jail.local.j2
|
||||
dest: /etc/fail2ban/jail.local
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart fail2ban
|
||||
|
||||
- name: Enable and start fail2ban
|
||||
ansible.builtin.systemd:
|
||||
name: fail2ban
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Automatic security updates (dnf5-automatic)
|
||||
# ------------------------------------------------------------
|
||||
- name: Install dnf5-plugin-automatic
|
||||
ansible.builtin.dnf:
|
||||
name: dnf5-plugin-automatic
|
||||
state: present
|
||||
|
||||
- name: Enable automatic installation of updates
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/dnf/automatic.conf
|
||||
insertafter: "^\\[commands\\]"
|
||||
regexp: "^#?\\s*apply_updates\\s*="
|
||||
line: "apply_updates = yes"
|
||||
|
||||
- name: Restrict automatic updates to security errata
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/dnf/automatic.conf
|
||||
insertafter: "^\\[commands\\]"
|
||||
regexp: "^#?\\s*upgrade_type\\s*="
|
||||
line: "upgrade_type = security"
|
||||
|
||||
- name: Enable and start dnf5-automatic.timer
|
||||
ansible.builtin.systemd:
|
||||
name: dnf5-automatic.timer
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Log rotation for fail2ban logs
|
||||
# ------------------------------------------------------------
|
||||
- name: Add logrotate rule for fail2ban
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/logrotate.d/fail2ban
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
/var/log/fail2ban/*.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
missingok
|
||||
notifempty
|
||||
postrotate
|
||||
/usr/bin/fail2ban-client reload >/dev/null 2>&1 || true
|
||||
endscript
|
||||
}
|
||||
17
roles/stoat/tasks/main.yml
Normal file
17
roles/stoat/tasks/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
# stoat role - task order matters:
|
||||
# system -> update + baseline packages (git needed by stoat tasks)
|
||||
# harden -> firewall, ssh, fail2ban, automatic updates, logrotate
|
||||
# podman -> rootless podman + service user
|
||||
# stoat -> clone repo, generate config, systemd-managed compose stack
|
||||
- name: System update and base packages
|
||||
ansible.builtin.import_tasks: system.yml
|
||||
|
||||
- name: Harden the host
|
||||
ansible.builtin.import_tasks: harden.yml
|
||||
|
||||
- name: Install rootless podman
|
||||
ansible.builtin.import_tasks: podman.yml
|
||||
|
||||
- name: Deploy Stoat
|
||||
ansible.builtin.import_tasks: stoat.yml
|
||||
33
roles/stoat/tasks/podman.yml
Normal file
33
roles/stoat/tasks/podman.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
# ------------------------------------------------------------
|
||||
# Podman - rootless engine, compose support, unprivileged user
|
||||
# ------------------------------------------------------------
|
||||
- name: Install podman stack
|
||||
ansible.builtin.dnf:
|
||||
name: "{{ podman_packages }}"
|
||||
state: present
|
||||
|
||||
# Rootless podman cannot bind host ports < 1024 without this.
|
||||
# Caddy (Stoat's TLS reverse proxy) needs 80/443.
|
||||
- name: Allow rootless containers to bind privileged ports
|
||||
ansible.builtin.sysctl:
|
||||
name: net.ipv4.ip_unprivileged_port_start
|
||||
value: "{{ podman_unprivileged_port_start }}"
|
||||
sysctl_file: /etc/sysctl.d/99-podman.conf
|
||||
state: present
|
||||
reload: true
|
||||
|
||||
- name: Create dedicated service user for rootless podman
|
||||
ansible.builtin.user:
|
||||
name: "{{ stoat_service_user }}"
|
||||
system: true
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
comment: "Stoat rootless podman service user"
|
||||
register: stoat_system_user
|
||||
|
||||
# Keeps /run/user/<uid> alive and lets rootless containers start
|
||||
# on boot without an interactive login.
|
||||
- name: Enable lingering for the service user
|
||||
ansible.builtin.command: "loginctl enable-linger {{ stoat_service_user }}"
|
||||
changed_when: false
|
||||
75
roles/stoat/tasks/stoat.yml
Normal file
75
roles/stoat/tasks/stoat.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
# ------------------------------------------------------------
|
||||
# Stoat deployment - rootless podman-compose, managed by systemd
|
||||
# ------------------------------------------------------------
|
||||
- name: Clone Stoat self-hosted repository
|
||||
ansible.builtin.git:
|
||||
repo: "{{ stoat_repo }}"
|
||||
dest: "{{ stoat_install_dir }}"
|
||||
version: "{{ stoat_version }}"
|
||||
force: true
|
||||
become: true
|
||||
become_user: "{{ stoat_service_user }}"
|
||||
become_method: su
|
||||
environment:
|
||||
HOME: "/home/{{ stoat_service_user }}"
|
||||
|
||||
- name: Ensure generate_config.sh is executable
|
||||
ansible.builtin.file:
|
||||
path: "{{ stoat_install_dir }}/generate_config.sh"
|
||||
mode: "0755"
|
||||
become: true
|
||||
become_user: "{{ stoat_service_user }}"
|
||||
become_method: su
|
||||
|
||||
# The compose file bind-mounts these directories; create them up
|
||||
# front so rootless podman does not fail on missing sources.
|
||||
- name: Create data directories referenced by compose bind mounts
|
||||
ansible.builtin.file:
|
||||
path: "{{ stoat_install_dir }}/data/{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ stoat_service_user }}"
|
||||
group: "{{ stoat_service_user }}"
|
||||
mode: "0755"
|
||||
loop:
|
||||
- db
|
||||
- rabbit
|
||||
- minio
|
||||
- caddy-data
|
||||
- caddy-config
|
||||
|
||||
# generate_config.sh is interactive; it reads two questions from
|
||||
# stdin: reverse-proxy? [y/N] and camera/screen sharing? [Y/n].
|
||||
# `creates` makes this idempotent - the script refuses to overwrite
|
||||
# existing config without --overwrite, which is a destructive
|
||||
# operation we never want Ansible to do by accident.
|
||||
- name: Generate Stoat configuration and secrets
|
||||
ansible.builtin.shell: >-
|
||||
printf '{{ 'y' if stoat_reverse_proxy else 'n' }}\n{{ 'y' if stoat_video else 'n' }}\n'
|
||||
| ./generate_config.sh {{ stoat_domain }}
|
||||
args:
|
||||
chdir: "{{ stoat_install_dir }}"
|
||||
creates: "{{ stoat_install_dir }}/Revolt.toml"
|
||||
become: true
|
||||
become_user: "{{ stoat_service_user }}"
|
||||
become_method: su
|
||||
environment:
|
||||
HOME: "/home/{{ stoat_service_user }}"
|
||||
|
||||
- name: Deploy podman-compose systemd unit
|
||||
ansible.builtin.template:
|
||||
src: podman-compose-stoat.service.j2
|
||||
dest: /etc/systemd/system/podman-compose-stoat.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
# First start pulls ~2 GB of images + initializes MongoDB, which can
|
||||
# take several minutes. TimeoutStartSec covers this inside the unit.
|
||||
- name: Enable and start the Stoat service (first pull may take minutes)
|
||||
ansible.builtin.systemd:
|
||||
name: podman-compose-stoat
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
timeout: 900
|
||||
14
roles/stoat/tasks/system.yml
Normal file
14
roles/stoat/tasks/system.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
# ------------------------------------------------------------
|
||||
# System: bring the host up to date and install baseline tools
|
||||
# ------------------------------------------------------------
|
||||
- name: Update all packages to latest versions
|
||||
ansible.builtin.dnf:
|
||||
name: "*"
|
||||
state: latest
|
||||
update_cache: true
|
||||
|
||||
- name: Install base packages
|
||||
ansible.builtin.dnf:
|
||||
name: "{{ base_packages }}"
|
||||
state: present
|
||||
@@ -1,6 +1,7 @@
|
||||
# --------------------------------------------------------------
|
||||
# Fail2Ban jail.local - default settings for Fedora 44
|
||||
# Values like {{ fail2ban_port }} come from the playbook vars
|
||||
# Values like {{ fail2ban_port }} come from the vars file
|
||||
# (group_vars/all/vars.yml)
|
||||
# --------------------------------------------------------------
|
||||
|
||||
[DEFAULT]
|
||||
@@ -25,13 +26,14 @@ maxretry = {{ fail2ban_maxretry }}
|
||||
bantime = {{ fail2ban_bantime }}
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# HTTP auth jail - uncomment if you run a web server
|
||||
# HTTP auth jail - Caddy (Stoat's proxy) logs are inside the
|
||||
# podman container; enable only if you mount /var/log for it.
|
||||
# --------------------------------------------------------------
|
||||
#[http-auth]
|
||||
#enabled = true
|
||||
#port = http,https
|
||||
#filter = http-auth
|
||||
#logpath = /var/log/nginx/access.log
|
||||
#logpath = /var/log/caddy/access.log
|
||||
#maxretry = {{ fail2ban_maxretry }}
|
||||
#bantime = {{ fail2ban_bantime }}
|
||||
|
||||
24
roles/stoat/templates/podman-compose-stoat.service.j2
Normal file
24
roles/stoat/templates/podman-compose-stoat.service.j2
Normal file
@@ -0,0 +1,24 @@
|
||||
[Unit]
|
||||
Description=Stoat - self-hosted chat (podman-compose)
|
||||
Documentation=https://github.com/stoatchat/self-hosted
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
# oneshot + RemainAfterExit: the unit tracks compose state while
|
||||
# the containers themselves run detached under rootless podman.
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
User={{ stoat_service_user }}
|
||||
Group={{ stoat_service_user }}
|
||||
WorkingDirectory={{ stoat_install_dir }}
|
||||
# Rootless podman needs the per-user runtime directory; %U expands
|
||||
# to the UID of `User=`. Lingering (enable-linger) keeps it alive.
|
||||
Environment=XDG_RUNTIME_DIR=/run/user/%U
|
||||
ExecStart=/usr/bin/podman-compose up -d --remove-orphans
|
||||
ExecStop=/usr/bin/podman-compose down
|
||||
# First start pulls ~2 GB of images; be generous.
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
8
roles/stoat/templates/sshd_hardening.conf.j2
Normal file
8
roles/stoat/templates/sshd_hardening.conf.j2
Normal file
@@ -0,0 +1,8 @@
|
||||
# Managed by Ansible - stoat role (sshd hardening drop-in)
|
||||
# Values come from group_vars/all/vars.yml
|
||||
|
||||
PermitRootLogin {{ sshd_permit_root_login }}
|
||||
PasswordAuthentication {{ sshd_password_authentication }}
|
||||
{% if sshd_mfa_required %}
|
||||
AuthenticationMethods publickey,keyboard-interactive:pam
|
||||
{% endif %}
|
||||
14
site.yml
Normal file
14
site.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
# ============================================================
|
||||
# Stoat.chat Fedora 44 server
|
||||
# Harden the host, install rootless Podman, deploy Stoat.
|
||||
#
|
||||
# Usage:
|
||||
# ansible-playbook -i inventory.ini site.yml
|
||||
# ============================================================
|
||||
- name: Harden Fedora 44 and deploy Stoat (podman)
|
||||
hosts: servers
|
||||
become: true
|
||||
|
||||
roles:
|
||||
- stoat
|
||||
Reference in New Issue
Block a user