Add WireGuard VPN, kobodl, and calibre-web

WireGuard for private service access (kobodl behind VPN).
kobodl downloads and de-DRMs Kobo store purchases.
calibre-web serves the library at books.monotrope.au.
sync.sh script handles ongoing download + import workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Louis Simoneau
2026-04-10 20:56:26 +10:00
parent 6a54777c5c
commit bcdc0c6cef
4 changed files with 230 additions and 1 deletions

View File

@@ -1,4 +1,4 @@
.PHONY: build serve deploy ssh setup miniflux gitea goatcounter hermes hermes-sync hermes-chat enrich .PHONY: build serve deploy ssh setup miniflux gitea goatcounter hermes hermes-sync hermes-chat enrich wireguard calibre calibre-sync
# Load .env if it exists # Load .env if it exists
-include .env -include .env
@@ -66,5 +66,17 @@ hermes-chat:
ssh -t root@$(MONOTROPE_HOST) docker exec -it hermes hermes chat ssh -t root@$(MONOTROPE_HOST) docker exec -it hermes hermes chat
wireguard:
@test -n "$(MONOTROPE_HOST)" || (echo "Error: MONOTROPE_HOST is not set"; exit 1)
ansible-playbook -i "$(MONOTROPE_HOST)," -u root infra/ansible/playbook.yml --tags wireguard
calibre:
@test -n "$(MONOTROPE_HOST)" || (echo "Error: MONOTROPE_HOST is not set"; exit 1)
ansible-playbook -i "$(MONOTROPE_HOST)," -u root infra/ansible/playbook.yml --tags calibre
calibre-sync:
@test -n "$(MONOTROPE_HOST)" || (echo "Error: MONOTROPE_HOST is not set"; exit 1)
ssh root@$(MONOTROPE_HOST) /opt/calibre/sync.sh
enrich: enrich:
uv run enrich.py uv run enrich.py

View File

@@ -60,6 +60,20 @@ git.monotrope.au {
encode zstd gzip encode zstd gzip
} }
# Calibre-web
books.monotrope.au {
reverse_proxy localhost:8083
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "camera=(), microphone=(), geolocation=()"
}
encode zstd gzip
}
# GoatCounter analytics # GoatCounter analytics
stats.monotrope.au { stats.monotrope.au {
reverse_proxy localhost:8081 reverse_proxy localhost:8081

View File

@@ -18,6 +18,7 @@
hermes_telegram_bot_token: "{{ lookup('env', 'HERMES_TELEGRAM_BOT_TOKEN') }}" hermes_telegram_bot_token: "{{ lookup('env', 'HERMES_TELEGRAM_BOT_TOKEN') }}"
hermes_telegram_allowed_users: "{{ lookup('env', 'HERMES_TELEGRAM_ALLOWED_USERS') }}" hermes_telegram_allowed_users: "{{ lookup('env', 'HERMES_TELEGRAM_ALLOWED_USERS') }}"
hermes_miniflux_api_key: "{{ lookup('env', 'HERMES_MINIFLUX_API_KEY') }}" hermes_miniflux_api_key: "{{ lookup('env', 'HERMES_MINIFLUX_API_KEY') }}"
wg_client_pubkey: "{{ lookup('env', 'WG_CLIENT_PUBKEY') }}"
tasks: tasks:
@@ -100,6 +101,7 @@
- miniflux - miniflux
- gitea - gitea
- goatcounter - goatcounter
- calibre
- name: Enable and start Caddy - name: Enable and start Caddy
systemd: systemd:
@@ -198,6 +200,75 @@
ufw: ufw:
state: enabled state: enabled
# ── WireGuard ───────────────────────────────────────────────────────────
- name: Install WireGuard
apt:
name: wireguard
state: present
tags: wireguard
- name: Generate WireGuard server private key
shell: wg genkey > /etc/wireguard/server_privatekey && chmod 600 /etc/wireguard/server_privatekey
args:
creates: /etc/wireguard/server_privatekey
tags: wireguard
- name: Generate WireGuard server public key
shell: cat /etc/wireguard/server_privatekey | wg pubkey > /etc/wireguard/server_publickey
args:
creates: /etc/wireguard/server_publickey
tags: wireguard
- name: Read server private key
slurp:
src: /etc/wireguard/server_privatekey
register: wg_server_privkey
tags: wireguard
- name: Read server public key
slurp:
src: /etc/wireguard/server_publickey
register: wg_server_pubkey
tags: wireguard
- name: Write WireGuard config
copy:
dest: /etc/wireguard/wg0.conf
owner: root
group: root
mode: '0600'
content: |
[Interface]
PrivateKey = {{ wg_server_privkey.content | b64decode | trim }}
Address = 10.100.0.1/24
ListenPort = 51820
[Peer]
PublicKey = {{ wg_client_pubkey }}
AllowedIPs = 10.100.0.2/32
notify: Restart WireGuard
tags: wireguard
- name: Allow WireGuard UDP port
ufw:
rule: allow
port: '51820'
proto: udp
tags: wireguard
- name: Enable and start WireGuard
systemd:
name: wg-quick@wg0
enabled: true
state: started
tags: wireguard
- name: Display server public key
debug:
msg: "WireGuard server public key: {{ wg_server_pubkey.content | b64decode | trim }}"
tags: wireguard
# ── Docker ────────────────────────────────────────────────────────────── # ── Docker ──────────────────────────────────────────────────────────────
- name: Create Docker keyring directory - name: Create Docker keyring directory
@@ -404,6 +475,83 @@
tags: hermes tags: hermes
# ── Calibre (kobodl + calibre-web) ────────────────────────────────────
- name: Create Calibre directory
file:
path: /opt/calibre
state: directory
owner: root
group: root
mode: '0750'
tags: calibre
- name: Copy Calibre docker-compose.yml
copy:
src: ../calibre/docker-compose.yml
dest: /opt/calibre/docker-compose.yml
owner: root
group: root
mode: '0640'
tags: calibre
- name: Pull and start Calibre services
command: docker compose up -d --pull always
args:
chdir: /opt/calibre
tags: calibre
- name: Fix downloads volume ownership
command: >
docker compose exec -T kobodl
chown 1000:1000 /downloads
args:
chdir: /opt/calibre
tags: calibre
- name: Check if Calibre library exists
command: >
docker compose exec -T calibre-web
test -f /library/metadata.db
args:
chdir: /opt/calibre
register: calibre_db_check
changed_when: false
failed_when: false
tags: calibre
- name: Initialise Calibre library
command: >
docker compose exec -T --user abc calibre-web
calibredb add --empty --with-library /library/
args:
chdir: /opt/calibre
when: calibre_db_check.rc != 0
tags: calibre
- name: Install calibre-sync script
copy:
dest: /opt/calibre/sync.sh
owner: root
group: root
mode: '0755'
content: |
#!/bin/bash
set -euo pipefail
cd /opt/calibre
# Download all books from Kobo
docker compose exec -T kobodl kobodl --config /home/config/kobodl.json book get --get-all --output-dir /downloads
# Import any new EPUBs into Calibre library
docker compose exec -T calibre-web sh -c '
for f in /downloads/*.epub; do
[ -f "$f" ] || continue
calibredb add "$f" --with-library /library/ && rm "$f"
done
'
tags: calibre
# ── GoatCounter ───────────────────────────────────────────────────────── # ── GoatCounter ─────────────────────────────────────────────────────────
- name: Create goatcounter system user - name: Create goatcounter system user
@@ -559,3 +707,8 @@
command: docker compose restart command: docker compose restart
args: args:
chdir: /opt/hermes chdir: /opt/hermes
- name: Restart WireGuard
systemd:
name: wg-quick@wg0
state: restarted

View File

@@ -0,0 +1,50 @@
services:
kobodl:
image: ghcr.io/subdavis/kobodl
restart: unless-stopped
user: "1000:1000"
command: --config /home/config/kobodl.json serve --host 0.0.0.0 --output-dir /downloads
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
ports:
- "10.100.0.1:5100:5000"
volumes:
- kobodl_config:/home/config
- downloads:/downloads
networks:
- default
- monotrope
calibre-web:
image: lscr.io/linuxserver/calibre-web:latest
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
ports:
- "127.0.0.1:8083:8083"
volumes:
- calibre_config:/config
- library:/library
- downloads:/downloads
environment:
PUID: "1000"
PGID: "1000"
TZ: "Australia/Sydney"
DOCKER_MODS: "linuxserver/mods:universal-calibre"
networks:
default:
monotrope:
external: true
volumes:
kobodl_config:
calibre_config:
library:
downloads: