From d07446a8790ce7f3c2379d02cae5b13a6c5e8058 Mon Sep 17 00:00:00 2001 From: Mark Hoekveen Date: Sat, 21 Mar 2026 13:17:20 +0100 Subject: [PATCH] wireguard, stash, docker debian, jellyfin --- handlers/main.yml | 11 ++ playbook.yml | 5 + scripts/generate-wireguard-keys.sh | 177 ++++++++++++++++++++++ tasks/create_users.yml | 3 +- tasks/docker_service.yml | 3 +- tasks/install_docker_debian.yml | 3 + tasks/jellyfin.yml | 4 + tasks/jellyfin/docker-compose.yml.j2 | 22 +++ tasks/jellyfin/vault.yml | 6 + tasks/stash.yml | 56 +------ tasks/stash/docker-compose.yml.j2 | 41 ++++++ tasks/wireguard.yml | 211 +++++++++++++++++++++++++++ tasks/wireguard/README.md | 59 ++++++++ tasks/wireguard/add_client.sh | 141 ++++++++++++++++++ tasks/wireguard/client.conf.j2 | 12 ++ tasks/wireguard/wg0.netdev.j2 | 17 +++ tasks/wireguard/wg0.network.j2 | 10 ++ vault/wireguard.yml | 40 +++++ 18 files changed, 767 insertions(+), 54 deletions(-) create mode 100755 scripts/generate-wireguard-keys.sh create mode 100644 tasks/jellyfin.yml create mode 100644 tasks/jellyfin/docker-compose.yml.j2 create mode 100644 tasks/jellyfin/vault.yml create mode 100644 tasks/stash/docker-compose.yml.j2 create mode 100644 tasks/wireguard.yml create mode 100644 tasks/wireguard/README.md create mode 100644 tasks/wireguard/add_client.sh create mode 100644 tasks/wireguard/client.conf.j2 create mode 100644 tasks/wireguard/wg0.netdev.j2 create mode 100644 tasks/wireguard/wg0.network.j2 create mode 100644 vault/wireguard.yml diff --git a/handlers/main.yml b/handlers/main.yml index 8515636..905d34e 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -11,3 +11,14 @@ - name: reload systemd systemd: daemon_reload: yes + +- name: reload systemd-networkd + systemd: + name: systemd-networkd + state: reloaded + +- name: save iptables + shell: | + iptables-save > /etc/iptables/rules.v4 + args: + executable: /bin/bash diff --git a/playbook.yml b/playbook.yml index 06fe181..6902c17 100644 --- a/playbook.yml +++ b/playbook.yml @@ -91,6 +91,11 @@ tasks: - import_tasks: tasks/vcmp_timer.yml - import_tasks: tasks/audiobookshelf.yml + - import_tasks: tasks/stash.yml + - import_tasks: tasks/jellyfin.yml + - import_tasks: tasks/wireguard.yml + handlers: + - import_tasks: handlers/main.yml tags: leaf - name: Vaermina install hosts: vaermina diff --git a/scripts/generate-wireguard-keys.sh b/scripts/generate-wireguard-keys.sh new file mode 100755 index 0000000..e1b9765 --- /dev/null +++ b/scripts/generate-wireguard-keys.sh @@ -0,0 +1,177 @@ +#!/bin/bash +# Generate WireGuard vanity keys and update the vault file +# Usage: ./generate-wireguard-keys.sh + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(dirname "$SCRIPT_DIR")" +VAULT_FILE="$REPO_DIR/vault/wireguard.yml" +TEMP_FILE=$(mktemp) + +echo "===================================" +echo "WireGuard Vanity Key Generator" +echo "===================================" +echo "" + +# Check if wireguard-vanity-address is installed +if ! command -v wireguard-vanity-address &> /dev/null; then + echo "Error: wireguard-vanity-address not found" + echo "Install it with: cargo install wireguard-vanity-address" + exit 1 +fi + +# Check if wg is installed +if ! command -v wg &> /dev/null; then + echo "Error: wg (wireguard-tools) not found" + echo "Install it with: sudo apt install wireguard-tools" + exit 1 +fi + +# Function to generate a vanity keypair +generate_vanity_key() { + local name=$1 + local search=$2 + local search_len=${#search} + local timeout=${3:-120} + + echo "Generating vanity key for $name (searching for '$search')..." + + # Try to generate vanity key with timeout + local output=$(timeout $timeout wireguard-vanity-address --in $search_len "$search" 2>&1 | grep "^private" | head -n 1 || true) + + if [ -n "$output" ]; then + local private_key=$(echo "$output" | awk '{print $2}') + local public_key=$(echo "$output" | awk '{print $4}') + echo " ✓ Found vanity key: $public_key" + else + echo " ⚠ No vanity key found in ${timeout}s, generating regular key..." + local private_key=$(wg genkey) + local public_key=$(echo "$private_key" | wg pubkey) + echo " ✓ Generated regular key: $public_key" + fi + + echo "$private_key|$public_key" +} + +echo "This will generate new WireGuard keys. This will:" +echo " 1. Generate vanity keys for all peers" +echo " 2. Update vault/wireguard.yml with the new keys" +echo "" +read -p "Continue? (y/N) " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi +echo "" + +# Generate keys +echo "Generating keys (this may take a few minutes)..." +echo "" + +SERVER_KEYS=$(generate_vanity_key "server" "talos" 180) +SERVER_PRIVATE=$(echo "$SERVER_KEYS" | cut -d'|' -f1) +SERVER_PUBLIC=$(echo "$SERVER_KEYS" | cut -d'|' -f2) +echo "" + +CLIENT1_KEYS=$(generate_vanity_key "client1" "lptp" 120) +CLIENT1_PRIVATE=$(echo "$CLIENT1_KEYS" | cut -d'|' -f1) +CLIENT1_PUBLIC=$(echo "$CLIENT1_KEYS" | cut -d'|' -f2) +echo "" + +CLIENT2_KEYS=$(generate_vanity_key "client2" "phon" 120) +CLIENT2_PRIVATE=$(echo "$CLIENT2_KEYS" | cut -d'|' -f1) +CLIENT2_PUBLIC=$(echo "$CLIENT2_KEYS" | cut -d'|' -f2) +echo "" + +MASSER_KEYS=$(generate_vanity_key "masser" "mssr" 120) +MASSER_PRIVATE=$(echo "$MASSER_KEYS" | cut -d'|' -f1) +MASSER_PUBLIC=$(echo "$MASSER_KEYS" | cut -d'|' -f2) +echo "" + +# Create the vault file content +cat > "$TEMP_FILE" << EOF +--- +# WireGuard VPN Keys +# These are encrypted with ansible-vault +# To edit: ansible-vault edit vault/wireguard.yml + +wireguard_server: + private_key: "$SERVER_PRIVATE" + public_key: "$SERVER_PUBLIC" + vanity_search: "talos" + +wireguard_clients: + - name: client1 + private_key: "$CLIENT1_PRIVATE" + public_key: "$CLIENT1_PUBLIC" + vanity_search: "lptp" + ip_address: "10.8.0.2/24" + description: "Example laptop client" + + - name: client2 + private_key: "$CLIENT2_PRIVATE" + public_key: "$CLIENT2_PUBLIC" + vanity_search: "phon" + ip_address: "10.8.0.3/24" + description: "Example phone client" + + - name: masser + private_key: "$MASSER_PRIVATE" + public_key: "$MASSER_PUBLIC" + vanity_search: "mssr" + ip_address: "10.8.0.4/24" + description: "Masser phone" +EOF + +echo "===================================" +echo "Keys generated successfully!" +echo "===================================" +echo "" +echo "Summary:" +echo " Server: $SERVER_PUBLIC" +echo " Client1: $CLIENT1_PUBLIC" +echo " Client2: $CLIENT2_PUBLIC" +echo " Masser: $MASSER_PUBLIC" +echo "" + +# Check if vault file should be encrypted +if [ -f "$VAULT_FILE" ]; then + # Check if existing file is encrypted + if head -n 1 "$VAULT_FILE" | grep -q '^\$ANSIBLE_VAULT'; then + echo "Encrypting with ansible-vault..." + ansible-vault encrypt "$TEMP_FILE" --output="$VAULT_FILE" + rm -f "$TEMP_FILE" + echo "✓ Keys saved to $VAULT_FILE (encrypted)" + else + echo "⚠ Warning: Existing vault file is not encrypted" + echo "Saving unencrypted keys to $VAULT_FILE" + mv "$TEMP_FILE" "$VAULT_FILE" + echo "" + echo "To encrypt the file, run:" + echo " ansible-vault encrypt $VAULT_FILE" + fi +else + echo "Do you want to encrypt the vault file with ansible-vault? (recommended)" + read -p "Encrypt? (Y/n) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Nn]$ ]]; then + ansible-vault encrypt "$TEMP_FILE" --output="$VAULT_FILE" + rm -f "$TEMP_FILE" + echo "✓ Keys saved to $VAULT_FILE (encrypted)" + else + mv "$TEMP_FILE" "$VAULT_FILE" + echo "✓ Keys saved to $VAULT_FILE (unencrypted)" + echo "" + echo "⚠ Warning: Keys are stored unencrypted!" + echo "To encrypt them later, run:" + echo " ansible-vault encrypt $VAULT_FILE" + fi +fi + +echo "" +echo "Next steps:" +echo " 1. Run the playbook to deploy: ansible-playbook -i inventory.yml playbook.yml --limit talos --tags leaf" +echo " 2. Update your WireGuard clients with the new server public key" +echo "" diff --git a/tasks/create_users.yml b/tasks/create_users.yml index e57778f..bb35247 100644 --- a/tasks/create_users.yml +++ b/tasks/create_users.yml @@ -17,5 +17,6 @@ key: "{{ lookup('file', '/home/' + item + '/.ssh/id_rsa.pub')}}" when: - users is defined - - lookup('first_found', '/home/' + item + '/.ssh/id_rsa.pub', errors='ignore') + - lookup('first_found', dict(files=['/home/' + item + '/.ssh/id_rsa.pub'], skip=true)) | length > 0 + #- lookup('first_found', '/home/' + item + '/.ssh/id_rsa.pub', errors='ignore') loop: "{{ users }}" diff --git a/tasks/docker_service.yml b/tasks/docker_service.yml index 5490158..8c0926e 100644 --- a/tasks/docker_service.yml +++ b/tasks/docker_service.yml @@ -2,7 +2,8 @@ block: - name: Include service vault include_vars: - file: "{{ service_name }}/vault.yml" + dir: "{{ service_name }}" + files_matching: vault.yml - name: Create service directory file: path: "/opt/{{ service_name }}" diff --git a/tasks/install_docker_debian.yml b/tasks/install_docker_debian.yml index 3e2eb32..ffe337b 100644 --- a/tasks/install_docker_debian.yml +++ b/tasks/install_docker_debian.yml @@ -39,3 +39,6 @@ name: nerevar groups: docker append: yes +- name: Create reverse proxy network + community.docker.docker_network: + name: proxy diff --git a/tasks/jellyfin.yml b/tasks/jellyfin.yml new file mode 100644 index 0000000..7806588 --- /dev/null +++ b/tasks/jellyfin.yml @@ -0,0 +1,4 @@ +- name: Deploy Jellyfin + include_tasks: tasks/docker_service.yml + vars: + service_name: jellyfin diff --git a/tasks/jellyfin/docker-compose.yml.j2 b/tasks/jellyfin/docker-compose.yml.j2 new file mode 100644 index 0000000..4921090 --- /dev/null +++ b/tasks/jellyfin/docker-compose.yml.j2 @@ -0,0 +1,22 @@ +services: + jellyfin: + image: jellyfin/jellyfin:10.11 + container_name: jellyfin + # Optional - specify the uid and gid you would like Jellyfin to use instead of root + user: 0:1003 + ports: + - 8096:8096/tcp + - 7359:7359/udp + volumes: + - ./config:/config + - ./cache:/cache + - type: bind + source: /mnt/azura + target: /media + read_only: true + devices: + - /dev/dri:/dev/dri + restart: 'unless-stopped' + # Optional - may be necessary for docker healthcheck to pass if running in host network mode + extra_hosts: + - 'host.docker.internal:host-gateway' diff --git a/tasks/jellyfin/vault.yml b/tasks/jellyfin/vault.yml new file mode 100644 index 0000000..f8a87ae --- /dev/null +++ b/tasks/jellyfin/vault.yml @@ -0,0 +1,6 @@ +$ANSIBLE_VAULT;1.1;AES256 +35646162643235336465636563653039656633313038313138323630336162373163626139613763 +3862666462343762363332323231616666336339306138310a343666353834623430363535613866 +30623233643664393466373734633734323334363761616665636161316432373333613130333164 +3639636538353035620a396463666662373665613235373138623739613665656236633533646162 +3765 diff --git a/tasks/stash.yml b/tasks/stash.yml index ab16ca0..3c5a323 100644 --- a/tasks/stash.yml +++ b/tasks/stash.yml @@ -1,52 +1,4 @@ -- name: Create temp directory - tempfile: - state: directory - suffix: bininstall - register: temp_dir - -- name: Download binary - get_url: - url: "https://github.com/stashapp/stash/releases/latest/download/stash-linux" - dest: "{{ temp_dir.path }}/stash" - mode: 0755 - -- name: Move binary to system path - copy: - src: "{{ temp_dir.path }}/stash" - dest: "/usr/local/bin/" - mode: 0755 - remote_src: yes - -- name: Copy service file - copy: - src: stash/stash.service - dest: /etc/systemd/system/ - mode: 0644 - -- name: Stash user - user: - name: stash - system: yes - create_home: no - shell: /sbin/nologin - groups: aurbis - -- name: Create config dir - file: - path: /var/lib/stash - state: directory - mode: "0755" - owner: "stash" - group: "stash" - -- name: Start and enable stash service - systemd: - name: stash - state: started - enabled: yes - daemon_reload: yes - -- name: Clean up temp directory - file: - path: "{{ temp_dir.path }}" - state: absent +- name: Deploy Stash + include_tasks: tasks/docker_service.yml + vars: + service_name: stash diff --git a/tasks/stash/docker-compose.yml.j2 b/tasks/stash/docker-compose.yml.j2 new file mode 100644 index 0000000..695bbe8 --- /dev/null +++ b/tasks/stash/docker-compose.yml.j2 @@ -0,0 +1,41 @@ +services: + stash: + image: stashapp/stash:latest + container_name: stash + restart: unless-stopped + ## the container's port must be the same with the STASH_PORT in the environment section + ports: + - "9999:9999" + ## If you intend to use stash's DLNA functionality uncomment the below network mode and comment out the above ports section + # network_mode: host + logging: + driver: "json-file" + options: + max-file: "10" + max-size: "2m" + environment: + - STASH_STASH=/data/ + - STASH_GENERATED=/generated/ + - STASH_METADATA=/metadata/ + - STASH_CACHE=/cache/ + ## Adjust below to change default port (9999) + - STASH_PORT=9999 + volumes: + - /etc/localtime:/etc/localtime:ro + ## Adjust below paths (the left part) to your liking. + ## E.g. you can change ./config:/root/.stash to ./stash:/root/.stash + ## The left part is the path on your host, the right part is the path in the stash container. + + ## Keep configs, scrapers, and plugins here. + - ./config:/root/.stash + ## Point this at your collection. + ## The left side is where your collection is on your host, the right side is where it will be in stash. + - /mnt/nocturnal/share/downloads:/data + ## This is where your stash's metadata lives + - ./metadata:/metadata + ## Any other cache content. + - ./cache:/cache + ## Where to store binary blob data (scene covers, images) + - ./blobs:/blobs + ## Where to store generated content (screenshots,previews,transcodes,sprites) + - ./generated:/generated diff --git a/tasks/wireguard.yml b/tasks/wireguard.yml new file mode 100644 index 0000000..bb241d8 --- /dev/null +++ b/tasks/wireguard.yml @@ -0,0 +1,211 @@ +- name: Load WireGuard keys from vault + include_vars: + file: vault/wireguard.yml + +- name: Validate wireguard configuration + assert: + that: + - wireguard_server is defined + - wireguard_clients is defined + - wireguard_clients | length > 0 + fail_msg: "wireguard_server and wireguard_clients must be defined in vault/wireguard.yml" + +- name: Install WireGuard packages + apt: + name: + - wireguard + - wireguard-tools + - qrencode + state: present + update_cache: yes + +- name: Create WireGuard directory structure + file: + path: "{{ item }}" + state: directory + owner: root + group: root + mode: "0700" + loop: + - /etc/wireguard + - /etc/wireguard/clients + - /etc/wireguard/keys + +- name: Generate key generation script + copy: + dest: /etc/wireguard/generate_key.sh + mode: "0700" + content: | + #!/bin/bash + # Generate a WireGuard keypair + # Usage: ./generate_key.sh + + OUTPUT_PREFIX="$1" + + if [ -z "$OUTPUT_PREFIX" ]; then + echo "Usage: $0 " + exit 1 + fi + + echo "Generating WireGuard keypair..." + umask 077 + wg genkey | tee "${OUTPUT_PREFIX}.key" | wg pubkey > "${OUTPUT_PREFIX}.pub" + chmod 600 "${OUTPUT_PREFIX}.key" + chmod 644 "${OUTPUT_PREFIX}.pub" + echo "Keys generated: ${OUTPUT_PREFIX}.key and ${OUTPUT_PREFIX}.pub" + +- name: Deploy add_client.sh script + copy: + src: wireguard/add_client.sh + dest: /etc/wireguard/add_client.sh + mode: "0755" + owner: root + group: root + +- name: Create server private key file + copy: + content: "{{ wireguard_server.private_key }}" + dest: "/etc/wireguard/keys/server.key" + owner: root + group: root + mode: "0600" + no_log: true + +- name: Create server public key file + copy: + content: "{{ wireguard_server.public_key }}" + dest: "/etc/wireguard/keys/server.pub" + owner: root + group: root + mode: "0644" + +- name: Create client private key files + copy: + content: "{{ item.private_key }}" + dest: "/etc/wireguard/keys/{{ item.name }}.key" + owner: root + group: root + mode: "0600" + loop: "{{ wireguard_clients }}" + loop_control: + label: "{{ item.name }}" + no_log: true + +- name: Create client public key files + copy: + content: "{{ item.public_key }}" + dest: "/etc/wireguard/keys/{{ item.name }}.pub" + owner: root + group: root + mode: "0644" + loop: "{{ wireguard_clients }}" + loop_control: + label: "{{ item.name }}" + +- name: Create systemd-networkd WireGuard netdev configuration + template: + src: wireguard/wg0.netdev.j2 + dest: /etc/systemd/network/99-wg0.netdev + owner: root + group: systemd-network + mode: "0640" + notify: reload systemd-networkd + no_log: true + +- name: Create systemd-networkd WireGuard network configuration + template: + src: wireguard/wg0.network.j2 + dest: /etc/systemd/network/99-wg0.network + owner: root + group: root + mode: "0644" + notify: reload systemd-networkd + +- name: Enable IP forwarding + sysctl: + name: net.ipv4.ip_forward + value: "1" + state: present + sysctl_set: yes + reload: yes + +- name: Install iptables-persistent for firewall rules + apt: + name: iptables-persistent + state: present + +- name: Configure iptables masquerading for WireGuard + iptables: + table: nat + chain: POSTROUTING + out_interface: "{{ ansible_default_ipv4.interface }}" + source: 10.8.0.0/24 + jump: MASQUERADE + comment: WireGuard masquerading + notify: save iptables + +- name: Configure iptables forwarding for WireGuard + iptables: + chain: FORWARD + in_interface: wg0 + jump: ACCEPT + comment: WireGuard forward in + notify: save iptables + +- name: Configure iptables forwarding from WireGuard + iptables: + chain: FORWARD + out_interface: wg0 + jump: ACCEPT + comment: WireGuard forward out + notify: save iptables + +- name: Enable and start systemd-networkd + systemd: + name: systemd-networkd + enabled: yes + state: started + +- name: Generate client configuration files + template: + src: wireguard/client.conf.j2 + dest: "/etc/wireguard/clients/{{ item.name }}.conf" + owner: root + group: root + mode: "0600" + loop: "{{ wireguard_clients }}" + loop_control: + label: "{{ item.name }}" + no_log: true + +- name: Generate QR codes for client configurations + shell: | + qrencode -t ansiutf8 -r /etc/wireguard/clients/{{ item.name }}.conf > /etc/wireguard/clients/{{ item.name }}.qr.txt + qrencode -t png -r /etc/wireguard/clients/{{ item.name }}.conf -o /etc/wireguard/clients/{{ item.name }}.qr.png + loop: "{{ wireguard_clients }}" + loop_control: + label: "{{ item.name }}" + +- name: Display WireGuard setup information + debug: + msg: + - "==============================================" + - "WireGuard Server Setup Complete!" + - "==============================================" + - "Server public key: {{ wireguard_server.public_key }}" + - "Server endpoint: {{ ansible_host }}:51820" + - "WireGuard network: 10.8.0.0/24" + - "Server IP: 10.8.0.1" + - "" + - "Configured clients ({{ wireguard_clients | length }}):" + - "{% for client in wireguard_clients %} - {{ client.name }} ({{ client.ip_address }}) - {{ client.description }}{% endfor %}" + - "" + - "Client configurations:" + - "{% for client in wireguard_clients %} - /etc/wireguard/clients/{{ client.name }}.conf{% endfor %}" + - "" + - "To view QR code in terminal, run:" + - " cat /etc/wireguard/clients/.qr.txt" + - "" + - "To generate additional client keys:" + - " /etc/wireguard/generate_key.sh /etc/wireguard/keys/" + - "==============================================" diff --git a/tasks/wireguard/README.md b/tasks/wireguard/README.md new file mode 100644 index 0000000..1b451db --- /dev/null +++ b/tasks/wireguard/README.md @@ -0,0 +1,59 @@ +# WireGuard VPN Server + +Configures Talos as a WireGuard VPN server providing access to home network (192.168.178.0/24). + +## Configuration + +Clients are defined in `vault/wireguard.yml`: + +```yaml +wireguard_server: + private_key: "..." + public_key: "..." + +wireguard_clients: + - name: masser + private_key: "..." + public_key: "..." + ip_address: "10.8.0.2/24" + description: "Masser phone" +``` + +## Adding a Client + +1. Generate vanity key: + ```bash + timeout 120 wireguard-vanity-address --in 4 newc 2>&1 | grep "^private" | head -1 + ``` + +2. Edit vault: + ```bash + ansible-vault edit vault/wireguard.yml + ``` + +3. Add to `wireguard_clients` list: + ```yaml + - name: newclient + private_key: "paste_from_step_1" + public_key: "paste_from_step_1" + ip_address: "10.8.0.X/24" + description: "Description" + ``` + +4. Deploy: + ```bash + ansible-playbook -i inventory.yml playbook.yml --limit talos --tags leaf + ``` + +5. Get client config from server: + ```bash + ssh nerevar@192.168.178.64 'sudo cat /etc/wireguard/clients/newclient.qr.txt' + ``` + +## Network Details + +- WireGuard network: 10.8.0.0/24 +- Server IP: 10.8.0.1 +- Server endpoint: home.hoekveen.net:51820 +- Server public key stored in vault +- Port forwarding required: UDP 51820 -> 192.168.178.64 \ No newline at end of file diff --git a/tasks/wireguard/add_client.sh b/tasks/wireguard/add_client.sh new file mode 100644 index 0000000..23026a0 --- /dev/null +++ b/tasks/wireguard/add_client.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# Helper script to add a new WireGuard client +# Usage: ./add_client.sh +# Example: ./add_client.sh mylaptop 4 + +set -e + +CLIENT_NAME="$1" +IP_SUFFIX="$2" + +if [ -z "$CLIENT_NAME" ] || [ -z "$IP_SUFFIX" ]; then + echo "Usage: $0 " + echo "Example: $0 mylaptop 4" + echo "" + echo "This will:" + echo " - Generate WireGuard keys for the client" + echo " - Assign IP 10.8.0.$IP_SUFFIX to the client" + echo " - Add peer to WireGuard server config" + echo " - Generate client config file and QR codes" + exit 1 +fi + +WG_DIR="/etc/wireguard" +KEYS_DIR="$WG_DIR/keys" +CLIENTS_DIR="$WG_DIR/clients" +NETDEV_FILE="/etc/systemd/network/99-wg0.netdev" + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo "Please run as root" + exit 1 +fi + +# Check if client already exists +if [ -f "$KEYS_DIR/${CLIENT_NAME}.key" ]; then + echo "Error: Client '$CLIENT_NAME' already exists!" + exit 1 +fi + +# Check if IP is already in use +if grep -q "10.8.0.${IP_SUFFIX}/32" "$NETDEV_FILE"; then + echo "Error: IP 10.8.0.${IP_SUFFIX} is already assigned!" + echo "Choose a different IP suffix." + exit 1 +fi + +echo "==========================================" +echo "Adding WireGuard Client: $CLIENT_NAME" +echo "==========================================" +echo "Client IP: 10.8.0.${IP_SUFFIX}" +echo "" + +# Generate keys +echo "Generating keys..." +cd "$KEYS_DIR" +"$WG_DIR/generate_key.sh" "$CLIENT_NAME" + +CLIENT_PRIVATE_KEY=$(cat "${KEYS_DIR}/${CLIENT_NAME}.key") +CLIENT_PUBLIC_KEY=$(cat "${KEYS_DIR}/${CLIENT_NAME}.pub") +SERVER_PUBLIC_KEY=$(cat "${KEYS_DIR}/server.pub") + +echo "Keys generated successfully!" +echo "Public key: $CLIENT_PUBLIC_KEY" +echo "" + +# Add peer to netdev file +echo "Adding peer to server configuration..." +cat >> "$NETDEV_FILE" << EOF + +# $CLIENT_NAME +[WireGuardPeer] +PublicKey=$CLIENT_PUBLIC_KEY +AllowedIPs=10.8.0.${IP_SUFFIX}/32 +PersistentKeepalive=25 +EOF + +echo "Peer added to $NETDEV_FILE" +echo "" + +# Get server endpoint (try to detect public IP) +SERVER_ENDPOINT=$(curl -s ifconfig.me 2>/dev/null || echo "YOUR_PUBLIC_IP") +if [ "$SERVER_ENDPOINT" = "YOUR_PUBLIC_IP" ]; then + # Fallback to ansible_host if available + SERVER_ENDPOINT=$(hostname -I | awk '{print $1}') +fi + +# Create client config +echo "Creating client configuration..." +cat > "$CLIENTS_DIR/${CLIENT_NAME}.conf" << EOF +[Interface] +Address = 10.8.0.${IP_SUFFIX}/24 +PrivateKey = $CLIENT_PRIVATE_KEY +DNS = 10.8.0.1 + +[Peer] +PublicKey = $SERVER_PUBLIC_KEY +Endpoint = ${SERVER_ENDPOINT}:51820 +# Route home network traffic through WireGuard +# To route ALL traffic (full VPN), change to: 0.0.0.0/0 +AllowedIPs = 192.168.178.0/24, 10.8.0.0/24 +PersistentKeepalive = 25 +EOF + +chmod 600 "$CLIENTS_DIR/${CLIENT_NAME}.conf" +echo "Client config created at $CLIENTS_DIR/${CLIENT_NAME}.conf" +echo "" + +# Generate QR codes +echo "Generating QR codes..." +qrencode -t ansiutf8 -r "$CLIENTS_DIR/${CLIENT_NAME}.conf" > "$CLIENTS_DIR/${CLIENT_NAME}.qr.txt" +qrencode -t png -r "$CLIENTS_DIR/${CLIENT_NAME}.conf" -o "$CLIENTS_DIR/${CLIENT_NAME}.qr.png" +echo "QR codes generated!" +echo "" + +# Reload systemd-networkd +echo "Reloading systemd-networkd..." +networkctl reload +sleep 2 +echo "" + +# Display summary +echo "==========================================" +echo "Client Added Successfully!" +echo "==========================================" +echo "Client name: $CLIENT_NAME" +echo "Client IP: 10.8.0.${IP_SUFFIX}" +echo "Public key: $CLIENT_PUBLIC_KEY" +echo "" +echo "Configuration files:" +echo " - $CLIENTS_DIR/${CLIENT_NAME}.conf" +echo " - $CLIENTS_DIR/${CLIENT_NAME}.qr.txt (terminal QR)" +echo " - $CLIENTS_DIR/${CLIENT_NAME}.qr.png (image QR)" +echo "" +echo "To view QR code in terminal:" +echo " cat $CLIENTS_DIR/${CLIENT_NAME}.qr.txt" +echo "" +echo "Next steps:" +echo " 1. Copy the client config to your device" +echo " 2. Import into WireGuard client app" +echo " 3. Connect and test with: ping 10.8.0.1" +echo "==========================================" diff --git a/tasks/wireguard/client.conf.j2 b/tasks/wireguard/client.conf.j2 new file mode 100644 index 0000000..03cf448 --- /dev/null +++ b/tasks/wireguard/client.conf.j2 @@ -0,0 +1,12 @@ +[Interface] +Address = {{ item.ip_address }} +PrivateKey = {{ item.private_key }} +DNS = 10.8.0.1 + +[Peer] +PublicKey = {{ wireguard_server.public_key }} +Endpoint = home.hoekveen.net:51820 +# Route all home network traffic through WireGuard +# To route ALL traffic (full VPN), change to: 0.0.0.0/0 +AllowedIPs = 192.168.178.0/24, 10.8.0.0/24 +PersistentKeepalive = 25 diff --git a/tasks/wireguard/wg0.netdev.j2 b/tasks/wireguard/wg0.netdev.j2 new file mode 100644 index 0000000..1f1a0be --- /dev/null +++ b/tasks/wireguard/wg0.netdev.j2 @@ -0,0 +1,17 @@ +[NetDev] +Name=wg0 +Kind=wireguard +Description=WireGuard VPN tunnel for home network access + +[WireGuard] +PrivateKey={{ wireguard_server.private_key }} +ListenPort=51820 + +{% for client in wireguard_clients %} +# {{ client.name }} - {{ client.description }} +[WireGuardPeer] +PublicKey={{ client.public_key }} +AllowedIPs={{ client.ip_address | regex_replace('/\d+$', '/32') }} +PersistentKeepalive=25 + +{% endfor %} diff --git a/tasks/wireguard/wg0.network.j2 b/tasks/wireguard/wg0.network.j2 new file mode 100644 index 0000000..78a9762 --- /dev/null +++ b/tasks/wireguard/wg0.network.j2 @@ -0,0 +1,10 @@ +[Match] +Name=wg0 + +[Network] +Address=10.8.0.1/24 +IPMasquerade=ipv4 +IPForward=yes + +[Route] +Destination=192.168.178.0/24 diff --git a/vault/wireguard.yml b/vault/wireguard.yml new file mode 100644 index 0000000..10da211 --- /dev/null +++ b/vault/wireguard.yml @@ -0,0 +1,40 @@ +$ANSIBLE_VAULT;1.1;AES256 +30643738326463306330626262343761663766623936653166373965643665626332613466633233 +6364363836653735353630383134343662386237343961610a396530323364663237376665313539 +61646263386432346461623338613132633935626630333338373634623164326632343261323336 +6237646464356362630a653637393132626661386261313037366531316435643865356234323161 +61643930313337653463373133386338386464306439383464343931303633336433363564653439 +35383362656563653338623162663862623437373132636134633834623635356430363833313233 +30396139613265653466303437373237343365626164386639303733336666653830393833336536 +33616431663132643839366332383464396539653466613936353235323565626163613831356339 +36636138366133323462346331323866323934366565616433323535356535313661613863623262 +32373239613630666234613437343361373565386139363631656564363965323134666133626465 +33643064323866663365616132366430303236336134323362646662313939656563383333613234 +32396330636635656132653538653066333264663730613564373136346531323237653335366330 +36333136343537616633333232363066336635636265326462306363346335333133653436636232 +30363030646564333132643164303066386630323264393438356331303437303163666432306365 +32643630336466663862623433326131633661396136663039663830383830633638393066666632 +33613865636266663535636231666164383234323432656336396539393863623637373533336234 +30656233323238316434356564653432363566386433326530636439663137353534643464323035 +62626238626265323664626430303037653733316535326235663132376130643161303934303436 +65653130306331313533343365643461366364376433626661626531323233373430373666356364 +64613961326632623137663836656662656133303263613930366130333936633036303232636265 +30353137376362643436333062343430323964613364366532616162636539306139323437633063 +66623639666638363564386363336436626465353635666263396336323063333439626166343236 +34303133613364666334326532356364343439656564336162663639323464666435376339356436 +34346365303761353439646439353334303365633363613033303935633463316632346364303530 +61303662393636383232333963313235643434323833636263666565396438343732366531656261 +37306439623165363164613563303566313461633936363133316434313039363335353139356137 +66613631383866653365623234353538646364323735353433336431323337316565353431613034 +61333838356466343336323662343037656662333465306665333433626637356330393036363765 +38333436333063356339636139653933373938373164646139333133393333386433323738616131 +34393133313563316339336230316362363130366239313837323236303939353362636136343363 +62383061353466323330383964333534366362666331393333373964316563633639393065366165 +37636564313937393138396665303562633261396636616435636237336231306163326538623837 +32313064343963356666653035623834306662366265373437316636373361636263356464373930 +31356235313963383230663165303161643838356534353339643436323262326338386231383061 +31303064356233303539356363653038656637646632313238366364383439393866326133653462 +39303936373762323665333135393334343131313732616236363233396666616564366233383763 +33663336316430333163333938653466353361633230396633353963303038353230643364356634 +30616230613139656662303830303632366531653838396130313730663930616666336632623137 +646436343130346531313934616132636234