diff --git a/homelab/applications/authelia/compose.yaml b/homelab/applications/authelia/compose.yaml deleted file mode 100644 index b961bc7..0000000 --- a/homelab/applications/authelia/compose.yaml +++ /dev/null @@ -1,29 +0,0 @@ -secrets: - STORAGE_ENCRYPTION_KEY: - file: './secrets/authelia_db_encryption_key' - -services: - authelia: - image: authelia/authelia:latest - container_name: authelia - volumes: - # Config files are still mounted as volumes - - ./config/configuration.yml:/config/configuration.yml:ro - - ./config/users_database.yml:/config/users_database.yml:ro - - # Persistent SQLite database directory - - ./db:/config/db - ports: - - '9091:9091' - secrets: ['STORAGE_ENCRYPTION_KEY'] - environment: - TZ: 'Etc/UTC' - AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: '/run/secrets/STORAGE_ENCRYPTION_KEY' - - restart: always - healthcheck: - test: ["CMD", "authelia", "healthcheck"] - interval: 5s - timeout: 5s - retries: 5 - start_period: 5s \ No newline at end of file diff --git a/homelab/applications/authelia/config/configuration.yml b/homelab/applications/authelia/config/configuration.yml deleted file mode 100644 index 38e4cbc..0000000 --- a/homelab/applications/authelia/config/configuration.yml +++ /dev/null @@ -1,59 +0,0 @@ -server: - address: 0.0.0.0:9091 - endpoints: - authz: - forward-auth: - implementation: 'ForwardAuth' - -log: - level: info - -# --- Storage --- -storage: - local: - path: /config/db/authelia.sqlite.db - -# --- Authentication --- -authentication_backend: - file: - path: /config/users_database.yml - password: - algorithm: argon2id - iterations: 1 - memory: 2097152 - parallelism: 4 - salt_length: 16 - key_length: 32 - password_reset: - disable: true - password_change: - disable: false - -# --- Session --- -session: - cookies: - - domain: 'saljic.me' - authelia_url: 'https://auth.saljic.me' - default_redirection_url: 'https://home.saljic.me' - -# --- Notifier (No Mail) --- -notifier: - filesystem: - filename: /config/db/notifications.log - -# --- Access Control --- -access_control: - default_policy: deny - rules: - - domain: "*.saljic.me" - policy: one_factor - -# --- Password policies --- -password_policy: - standard: - enabled: true - min_length: 8 - require_uppercase: true - require_lowercase: true - require_number: true - require_special: true \ No newline at end of file diff --git a/homelab/applications/authelia/config/users_database.yml b/homelab/applications/authelia/config/users_database.yml deleted file mode 100644 index f0575bc..0000000 --- a/homelab/applications/authelia/config/users_database.yml +++ /dev/null @@ -1,9 +0,0 @@ -users: - amar: - disabled: false - displayname: 'Amar Šaljić' - password: '$argon2id$v=19$m=65536,t=3,p=4$5PaYrZOdJzg3SMIAqql/uA$EFH9v2DfJZw6ni8uup4BWMEFvMwIlM5HbH7MLX7F4g0' - email: 'amar@saljic.me' - groups: - - 'admins' - - 'dev' \ No newline at end of file diff --git a/homelab/applications/caddy/Caddyfile b/homelab/applications/caddy/Caddyfile index c14733e..ddbde91 100644 --- a/homelab/applications/caddy/Caddyfile +++ b/homelab/applications/caddy/Caddyfile @@ -1,34 +1,48 @@ -# --- Authelia Portal --- -# This is the login page itself. It does NOT have forward_auth. -auth.saljic.me { - # Proxy to the Authelia Docker container on port 9091 - reverse_proxy localhost:9091 +# --- Home Assistant --- +(logging) { + log { + output file /var/log/caddy/access.log { + # Roll logs to save space + roll_size 100mb + roll_keep 10 + roll_keep_for 720h # 30 days + } + format json + level INFO # This ensures all requests (INFO, WARN, ERROR) are logged + } } -# --- Home Assistant (Protected App) --- -ha.saljic.me { - # 1. Apply authentication using Caddy's 'forward_auth' - forward_auth localhost:9091 { - uri /api/authz/forward-auth - copy_headers Remote-User Remote-Groups Remote-Name Remote-Email - } +# --- top domain --- +saljic.me { + import logging + respond "Welcome! In the making..." +} - # 2. If auth is successful, proxy to your Home Assistant instance +ha.saljic.me { + import logging reverse_proxy 10.10.10.6:8123 } +# --- FreshRSS --- +feed.saljic.me { + import logging + reverse_proxy 10.10.10.6:8081 +} + # --- Immich --- tagebuch.saljic.me { + import logging reverse_proxy 10.10.10.6:2283 } -# --- default --- -home.saljic.me { - header Content-Type text/html - respond < - saljic.me - Hello there - - HTML 200 +# --- Gitea --- +git.saljic.me { + import logging + reverse_proxy 10.10.10.6:8030 +} + +# --- ntfy --- +ntfy.saljic.me { + import logging + reverse_proxy 10.10.10.6:8500 } \ No newline at end of file diff --git a/homelab/applications/gitea/compose.yml b/homelab/applications/gitea/compose.yml new file mode 100644 index 0000000..46dc1f7 --- /dev/null +++ b/homelab/applications/gitea/compose.yml @@ -0,0 +1,14 @@ + services: + server: + image: docker.gitea.com/gitea:latest + container_name: gitea + environment: + - USER_UID=1000 + - USER_GID=1000 + - DISABLE_REGISTRATION=true + restart: always + volumes: + - ./data:/data + ports: + - "8030:3000" + - "222:22" \ No newline at end of file diff --git a/homelab/applications/homeassistant/config/configuration.yaml b/homelab/applications/homeassistant/config/configuration.yaml new file mode 100644 index 0000000..da2f478 --- /dev/null +++ b/homelab/applications/homeassistant/config/configuration.yaml @@ -0,0 +1,19 @@ +# Loads default set of integrations. Do not remove. +default_config: + +# Load frontend themes from the themes folder +frontend: + themes: !include_dir_merge_named themes + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml +http: + use_x_forwarded_for: true + trusted_proxies: + - 192.168.100.5 +homeassistant: + external_url: "https://ha.saljic.me" + internal_url: "http://10.10.10.6:8123" + +sensor: !include sensor.yaml diff --git a/homelab/applications/postgres/README.md b/homelab/applications/postgres/README.md new file mode 100644 index 0000000..980a0d0 --- /dev/null +++ b/homelab/applications/postgres/README.md @@ -0,0 +1,28 @@ +# Postgres +## Set up non-root user for container +We are providing a non-root user to the container to limit the attack surface for privilege escalations. In order for this to work in our setup, please make sure to check if you have a user called `postgres` set up. + +1. Check if user `postgres` exists and if the UID is 1002 + +``` +cat /etc/passwd | grep postgres +``` + +In case the `postgres` user exists but the UID is not 1002, please adjust it via +``` +sudo usermod -u 1002 postgres +``` + +In case the `postgres` user doesn't exist at all, please create the user incl. the right UID by running +``` +sudo useradd -u 1002 postgres +``` + +## About secrets +In order to manage secrets centrally in 1Password and due to the need for secrets in Postgres, using `docker compose` directly in the terminal does not work. + +## Bring up/tear down container +Please use the `start.sh` to spin up the container +### Prerequisites start.sh +- User executing the script is part of the `docker` group +- Env variable `OP_SERVICE_ACCOUNT_TOKEN` is set up \[check out top-level README.md for more information on how to set this up\] \ No newline at end of file diff --git a/homelab/applications/postgres/compose.yml b/homelab/applications/postgres/compose.yml new file mode 100644 index 0000000..ec0f5ae --- /dev/null +++ b/homelab/applications/postgres/compose.yml @@ -0,0 +1,18 @@ +secrets: + postgres_password: + environment: POSTGRES_PASSWORD + postgres_user: + environment: POSTGRES_USER +services: + postgres: + image: postgres:18 + container_name: postgres + user: "1002" + restart: always + shm_size: 1024mb + environment: + POSTGRES_USER_FILE: /run/secrets/postgres_user + POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password + secrets: ['postgres_password', 'postgres_user'] + ports: ['5432:5432'] + volumes: ['./data:/var/lib/postgresql'] diff --git a/homelab/applications/postgres/start.sh b/homelab/applications/postgres/start.sh new file mode 100644 index 0000000..a0dada1 --- /dev/null +++ b/homelab/applications/postgres/start.sh @@ -0,0 +1,13 @@ +#!/bin/zsh +# Exit immediately if a command exits with a non-zero status. +set -e + +echo "--- Starting Docker Secret Management ---" +# Mount secrets +export POSTGRES_USER="$(op read 'op://NAxS Homelab/Postgres Homelab/username')" +export POSTGRES_PASSWORD="$(op read 'op://NAxS Homelab/Postgres Homelab/password')" + +# Bring up container +docker compose up -d + +echo "--- Docker Secret Management Complete ---" \ No newline at end of file diff --git a/homelab/ubuntu-server-setup.sh b/homelab/ubuntu-server-setup.sh new file mode 100644 index 0000000..918715b --- /dev/null +++ b/homelab/ubuntu-server-setup.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Simple installer for Ubuntu server: +# - unattended-upgrades (security updates + automatic reboot) +# - Docker (engine + compose plugin) per Docker docs steps 1-3 +# - zsh (set as default shell for original user) +# - 1Password CLI for access to secrets +# - secret-tools for storing tokens needed (i.e. for 1Password CLI) + +# Must be run as root +if [ "$EUID" -ne 0 ]; then + echo "Please run as root: sudo bash $0" + exit 1 +fi + +# Detect target user to set default shell for +TARGET_USER="${SUDO_USER:-$(whoami)}" + +apt-get update + +# 1) Enable automatic security updates and automatic reboot +apt-get install -y unattended-upgrades + +# Enable periodic updates/unattended-upgrades +cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF' +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; +APT::Periodic::AutocleanInterval "7"; +EOF + +# Ensure automatic reboot after unattended-upgrades (time adjustable) +cat > /etc/apt/apt.conf.d/99auto-reboot <<'EOF' +Unattended-Upgrade::Automatic-Reboot "true"; +Unattended-Upgrade::Automatic-Reboot-Time "04:00"; +EOF + +# Start/enable unattended-upgrades (if system uses service/timer) +if systemctl list-unit-files | grep -q unattended-upgrades; then + systemctl enable --now unattended-upgrades || true +fi + +# 2) Install Docker (steps 1-3 from Docker docs) +# Install prerequisites +apt-get install -y ca-certificates curl gnupg lsb-release + +# Create keyrings dir and add Docker GPG key +install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +chmod a+r /etc/apt/keyrings/docker.asc + +# Add Docker apt repository +ARCH=$(dpkg --print-architecture) +. /etc/os-release +UBU_CODENAME="${UBUNTU_CODENAME:-$VERSION_CODENAME}" +echo "deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${UBU_CODENAME} stable" \ + > /etc/apt/sources.list.d/docker.list + +apt-get update + +# Install Docker Engine + plugins including compose plugin +apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + +# Verify Docker works by running hello-world (this will pull an image) +if command -v docker >/dev/null 2>&1; then + docker run --rm hello-world || true +fi + +# 3) Install zsh and make it the default shell for the target user +apt-get install -y zsh + +ZSH_PATH="$(which zsh)" +if ! grep -q "^${ZSH_PATH}$" /etc/shells; then + echo "${ZSH_PATH}" >> /etc/shells +fi + +# Change shell for target user (if possible) +if id "${TARGET_USER}" >/dev/null 2>&1; then + chsh -s "${ZSH_PATH}" "${TARGET_USER}" || echo "chsh failed for ${TARGET_USER}; you may need to run 'chsh -s ${ZSH_PATH} ${TARGET_USER}' manually" +else + echo "User ${TARGET_USER} not found; skipping chsh" +fi + +# 4) Install 1Password CLI for access to secrets +curl -sS https://downloads.1password.com/linux/keys/1password.asc | \ +gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg && \ +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/$(dpkg --print-architecture) stable main" | \ +tee /etc/apt/sources.list.d/1password.list && \ +mkdir -p /etc/debsig/policies/AC2D62742012EA22/ && \ +curl -sS https://downloads.1password.com/linux/debian/debsig/1password.pol | \ +tee /etc/debsig/policies/AC2D62742012EA22/1password.pol && \ +mkdir -p /usr/share/debsig/keyrings/AC2D62742012EA22 && \ +curl -sS https://downloads.1password.com/linux/keys/1password.asc | \ +gpg --dearmor --output /usr/share/debsig/keyrings/AC2D62742012EA22/debsig.gpg && \ +apt update && apt install 1password-cli + +# Check successful install +op --version + +# 5) Install gnome-keyring secret-tool for securely storing tokens +apt install pass gnupg2 + + + + +echo "Done. Recommended: log out and back in (or reboot) to start using zsh and ensure all services are active." \ No newline at end of file