docker info
This commit is contained in:
134
caddy/Caddyfile
Normal file
134
caddy/Caddyfile
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
storage redis {
|
||||
address caddy-redis:6379
|
||||
}
|
||||
#acme_ca https://acme.zerossl.com/v2/DV90
|
||||
email charles.a.monroe@gmail.com
|
||||
acme_dns cloudflare {env.CF_API_KEY}
|
||||
}
|
||||
|
||||
# This reusable snippet contains the forward_auth configuration.
|
||||
(authelia) {
|
||||
forward_auth http://authelia:9091 {
|
||||
uri /api/verify?rd=https://auth.poppyglen.cc/
|
||||
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
|
||||
}
|
||||
}
|
||||
|
||||
auth.poppyglen.cc {
|
||||
reverse_proxy http://authelia:9091 {
|
||||
trusted_proxies 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 104.16.0.0/12 108.162.192.0/18 131.0.72.0/22 141.101.64.0/18 162.158.0.0/15 172.64.0.0/13 173.245.48.0/20 188.114.96.0/20 190.93.240.0/20 197.234.240.0/22 198.41.128.0/17 2400:cb00::/32 2405:8100::/32 2405:b500::/32 2606:4700::/32 2803:f800::/32 2a06:98c0::/29 2c0f:f248::/32
|
||||
}
|
||||
}
|
||||
|
||||
cloud.poppyglen.cc {
|
||||
request_body {
|
||||
max_size 10G
|
||||
}
|
||||
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains;"
|
||||
Referrer-Policy no-referrer
|
||||
X-Content-Type-Options nosniff
|
||||
X-Download-Options noopen
|
||||
X-Frame-Options SAMEORIGIN
|
||||
X-Permitted-Cross-Domain-Policies none
|
||||
X-Robots-Tag "noindex,nofollow"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()"
|
||||
}
|
||||
|
||||
log {
|
||||
output file /data/caddy_access.log
|
||||
format json
|
||||
}
|
||||
|
||||
route {
|
||||
import authelia
|
||||
|
||||
coraza_waf /etc/caddy/waf/nextcloud-waf.conf
|
||||
|
||||
handle_path /index.php/apps/memories/static/go-vod/* {
|
||||
reverse_proxy go-vod:8080
|
||||
}
|
||||
|
||||
handle_path /whiteboard/* {
|
||||
reverse_proxy http://nextcloud-whiteboard-server:3002
|
||||
}
|
||||
|
||||
reverse_proxy nextcloud-app:80 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http://local.poppyglen.cc {
|
||||
reverse_proxy nextcloud-app:80 {
|
||||
header_up Host {host}
|
||||
}
|
||||
|
||||
log {
|
||||
output file /data/caddy_access.log
|
||||
}
|
||||
}
|
||||
|
||||
vault.poppyglen.cc {
|
||||
route {
|
||||
coraza_waf /etc/caddy/waf/coraza.conf
|
||||
reverse_proxy vaultwarden:80
|
||||
}
|
||||
}
|
||||
|
||||
office.poppyglen.cc {
|
||||
reverse_proxy collabora:9980 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
|
||||
whiteboard.poppyglen.cc {
|
||||
reverse_proxy nextcloud-whiteboard-server:3002 {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote}
|
||||
}
|
||||
}
|
||||
|
||||
signal.poppyglen.cc {
|
||||
log {
|
||||
output file /var/log/caddy/signaling.log
|
||||
}
|
||||
|
||||
reverse_proxy nc-talk:8081 {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_ip}
|
||||
}
|
||||
}
|
||||
|
||||
mail.poppyglen.cc {
|
||||
respond "Mail server for poppyglen.cc"
|
||||
}
|
||||
|
||||
immich.poppyglen.cc {
|
||||
route {
|
||||
import authelia
|
||||
coraza_waf /etc/caddy/waf/coraza.conf
|
||||
reverse_proxy immich-server:2283 {
|
||||
header_up X-Real-IP {remote_ip}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jellyfin.poppyglen.cc {
|
||||
route {
|
||||
import authelia
|
||||
coraza_waf /etc/caddy/waf/coraza.conf
|
||||
reverse_proxy jellyfin:8096
|
||||
}
|
||||
}
|
||||
|
||||
sunshine.poppyglen.cc {
|
||||
reverse_proxy https://sunshine:47990 {
|
||||
transport http {
|
||||
tls_insecure_skip_verify
|
||||
}
|
||||
}
|
||||
}
|
||||
13
caddy/Dockerfile
Normal file
13
caddy/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
# Stage 1: Build the custom Caddy binary using the official builder image
|
||||
FROM caddy:builder AS builder
|
||||
|
||||
RUN xcaddy build \
|
||||
--with github.com/corazawaf/coraza-caddy \
|
||||
--with github.com/pberkel/caddy-storage-redis \
|
||||
--with github.com/caddy-dns/cloudflare
|
||||
|
||||
# Stage 2: Create the final, clean image using the standard Caddy image
|
||||
FROM caddy:latest
|
||||
|
||||
# Copy the custom-built Caddy binary from the builder stage
|
||||
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||
7
caddy/waf/coraza.conf
Normal file
7
caddy/waf/coraza.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
# Main Coraza WAF Configuration (Strict)
|
||||
|
||||
# 1. Load the OWASP setup file
|
||||
Include /etc/caddy/waf/owasp-crs/crs-setup.conf
|
||||
|
||||
# 2. Load all the OWASP rule files
|
||||
Include /etc/caddy/waf/owasp-crs/rules/*.conf
|
||||
19
caddy/waf/nextcloud-exclusions.conf
Normal file
19
caddy/waf/nextcloud-exclusions.conf
Normal file
@@ -0,0 +1,19 @@
|
||||
# Nextcloud-Specific Exclusions
|
||||
# These rules modify or remove the rules loaded from the main set.
|
||||
|
||||
# Rule 911100: Whitelist methods needed by WebDAV and Nextcloud APIs
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'PROPFIND'
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'PUT'
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'DELETE'
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'REPORT'
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'MKCOL'
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'COPY'
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'MOVE'
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'LOCK'
|
||||
SecRuleUpdateTargetById 911100 !REQUEST_METHOD:'UNLOCK'
|
||||
|
||||
# Rule 920420: Allow content types used by Nextcloud that are sometimes blocked.
|
||||
SecRuleRemoveById 920420
|
||||
|
||||
# Rule 942550: Disable a SQL injection rule that has frequent false positives.
|
||||
SecRuleRemoveById 942550
|
||||
7
caddy/waf/nextcloud-waf.conf
Normal file
7
caddy/waf/nextcloud-waf.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
# Custom WAF Configuration for Nextcloud
|
||||
|
||||
# 1. Load the main, strict rules first
|
||||
Include /etc/caddy/waf/coraza.conf
|
||||
|
||||
# 2. THEN, load the Nextcloud-specific exceptions
|
||||
Include /etc/caddy/waf/nextcloud-exclusions.conf
|
||||
799
docker-compose.yml
Normal file
799
docker-compose.yml
Normal file
@@ -0,0 +1,799 @@
|
||||
networks:
|
||||
nextcloud-net:
|
||||
driver: bridge
|
||||
enable_ipv6: true # <-- Enable IPv6 for this network
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 172.16.0.0/16 # <-- Define your desired IPv4 subnet
|
||||
- subnet: "fd00:22::/64" # <-- Define your desired IPv6 subnet
|
||||
#sunshine_net:
|
||||
# driver: macvlan
|
||||
# driver_opts:
|
||||
# parent: enp102s0f3u1u4 # Your physical network card
|
||||
# ipam:
|
||||
# config:
|
||||
# - subnet: 192.168.0.0/24 # Your network's subnet
|
||||
# gateway: 192.168.0.1 # Your network's gateway
|
||||
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: minio
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# IMPORTANT: Add these to your .env file
|
||||
- MINIO_ROOT_USER=${MINIO_ACCESS_KEY}
|
||||
- MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}
|
||||
volumes:
|
||||
- /mnt/Nextcloud/minio-data:/data
|
||||
ports:
|
||||
# Port for the S3 API endpoint (internal access, no need to publish)
|
||||
# - "9000:9000"
|
||||
# Port for the MinIO web console (publish to access from your browser)
|
||||
- "9001:9001"
|
||||
command: server /data --console-address ":9001"
|
||||
networks:
|
||||
- nextcloud-net
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
minio-init:
|
||||
image: minio/mc
|
||||
container_name: minio-init
|
||||
depends_on:
|
||||
minio:
|
||||
condition: service_healthy # Wait for MinIO to be healthy
|
||||
environment:
|
||||
# Pass the credentials from your .env file
|
||||
- MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
|
||||
- MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
|
||||
entrypoint: /bin/sh
|
||||
command: /init-minio.sh
|
||||
volumes:
|
||||
- ./init-minio.sh:/init-minio.sh # Mount the script
|
||||
networks:
|
||||
- nextcloud-net
|
||||
restart: "no"
|
||||
|
||||
authelia:
|
||||
image: authelia/authelia:latest
|
||||
container_name: authelia
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./authelia:/config
|
||||
networks:
|
||||
- nextcloud-net
|
||||
expose:
|
||||
- 9091
|
||||
environment:
|
||||
- TZ=America/Los_Angeles
|
||||
depends_on:
|
||||
mail:
|
||||
condition: service_started
|
||||
authelia-redis:
|
||||
condition: service_started
|
||||
|
||||
authelia-redis:
|
||||
image: redis:alpine
|
||||
container_name: authelia-redis
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/Nextcloud/authelia-redis:/data
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
cloudflare-ddns:
|
||||
image: docker.io/oznu/cloudflare-ddns:latest
|
||||
container_name: cloudflare_ddns
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- API_KEY=${CF_API_KEY}
|
||||
- ZONE=${CF_ZONE}
|
||||
- SUBDOMAIN=${CF_SUBDOMAIN}
|
||||
- PROXIED=${CF_PROXIED}
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
container_name: nextcloud-db
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
environment:
|
||||
- TZ=America/Los_Angeles
|
||||
- POSTGRES_DB=${POSTGRES_DB}
|
||||
- POSTGRES_USER=${POSTGRES_USER}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- DB_STORAGE_TYPE='HDD'
|
||||
volumes:
|
||||
- /mnt/Nextcloud/nextcloud_db:/var/lib/postgresql/data
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
app:
|
||||
build: ./nextcloud
|
||||
container_name: nextcloud-app
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
collabora:
|
||||
condition: service_started
|
||||
minio-init:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
POSTGRES_HOST: db
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
REDIS_HOST: redis
|
||||
NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
|
||||
NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
|
||||
NEXTCLOUD_TRUSTED_DOMAINS: cloud.poppyglen.cc local.poppyglen.cc
|
||||
OVERWRITEHOST: cloud.poppyglen.cc
|
||||
OVERWRITEPROTOCOL: https
|
||||
OVERWRITECLIURL: https://cloud.poppyglen.cc
|
||||
OVERWRITECONDADDR: 172.16.0.0\/12
|
||||
TRUSTED_PROXIES: 172.16.0.0/12
|
||||
USERS_JSON: ${USERS_JSON}
|
||||
HOME: /var/www # Set HOME for www-data
|
||||
GIPHY_API_KEY: ${GIPHY_API_KEY}
|
||||
WHITEBOARD_JWT: ${WHITEBOARD_JWT}
|
||||
TURN_SECRET: ${TURN_SECRET}
|
||||
SIGNALING_SECRET: ${SIGNALING_SECRET}
|
||||
MAIL_FROM_ADDRESS: admin
|
||||
MAIL_DOMAIN: ${MAIL_DOMAIN}
|
||||
MAIL_SMTPAUTH: 1
|
||||
MAIL_SMTPSECURE: ssl
|
||||
MAIL_SMTPHOST: ${MAIL_SMTPHOST}
|
||||
MAIL_SMTPPORT: 465
|
||||
MAIL_SMTPNAME: ${MAIL_ADMIN_EMAIL}
|
||||
MAIL_SMTPPASSWORD: ${MAIL_ADMIN_PASSWORD}
|
||||
MAIL_ADMIN_EMAIL: ${MAIL_ADMIN_EMAIL}
|
||||
MAIL_ADMIN_PASSWORD: ${MAIL_ADMIN_PASSWORD}
|
||||
EMAIL_IMAP_HOST: ${MAIL_SMTPHOST}
|
||||
EMAIL_SMTP_HOST: ${MAIL_SMTPHOST}
|
||||
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
|
||||
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
|
||||
volumes:
|
||||
- /mnt/Nextcloud/Nextcloud:/var/www/html
|
||||
- ./hooks:/docker-entrypoint-hooks.d
|
||||
- /mnt/Nextcloud/music:/music
|
||||
- /mnt/Nextcloud/tvshows:/tvshows
|
||||
- /mnt/Nextcloud/games:/games
|
||||
- /mnt/Nextcloud/movies:/movies
|
||||
- /mnt/Nextcloud/photos:/photos
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
cron:
|
||||
image: nextcloud:latest
|
||||
container_name: nextcloud-cron
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- app
|
||||
volumes:
|
||||
- /mnt/Nextcloud/Nextcloud:/var/www/html
|
||||
entrypoint: /cron.sh
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
#create 32 character password fpr each service
|
||||
#openssl rand -hex 32
|
||||
nc-talk:
|
||||
image: ghcr.io/nextcloud-releases/aio-talk:latest
|
||||
container_name: nc-talk
|
||||
init: true
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3478:3478/tcp
|
||||
- 3478:3478/udp
|
||||
- 8181:8081/tcp
|
||||
environment:
|
||||
- NC_DOMAIN=cloud.poppyglen.cc
|
||||
- TALK_HOST=signal.poppyglen.cc
|
||||
- TURN_SECRET=${TURN_SECRET}
|
||||
- SIGNALING_SECRET=${SIGNALING_SECRET}
|
||||
- TZ=America/Los_Angeles
|
||||
- TALK_PORT=3478
|
||||
- RECORDING_SECRET=${RECORDING_SECRET}
|
||||
- INTERNAL_SECRET=${INTERNAL_SECRET}
|
||||
depends_on:
|
||||
- app
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
collabora:
|
||||
image: collabora/code
|
||||
container_name: collabora
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nextcloud-net
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:9980/hosting/capabilities || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
environment:
|
||||
#- PUID=1000
|
||||
#- PGID=1000
|
||||
- TZ=America/Los_Angeles
|
||||
- password=${COLLABORA_PASSWORD}
|
||||
- username=${COLLABORA_USERNAME}
|
||||
- server_name=office.poppyglen.cc
|
||||
- aliasgroup1=https://cloud.poppyglen.cc:443
|
||||
- aliasgroup2=http://local.poppyglen.cc
|
||||
- extra_params=--o:ssl.enable=false --o:ssl.termination=true
|
||||
- DONT_GEN_PASSWD=1
|
||||
#ports:
|
||||
# - 9980:9980
|
||||
#volumes:
|
||||
# - /mnt/Nextcloud/Nextcloud:/var/www/html:ro
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
|
||||
redis:
|
||||
image: docker.io/library/redis:alpine
|
||||
container_name: nextcloud-redis
|
||||
restart: unless-stopped
|
||||
mem_limit: 2048m
|
||||
mem_reservation: 512m
|
||||
volumes:
|
||||
- /mnt/Nextcloud/redis:/data
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
#docker compose exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile
|
||||
caddy:
|
||||
build: ./caddy
|
||||
container_name: caddy
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
environment:
|
||||
- CF_API_KEY=${CF_API_KEY}
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
- "443:443/udp"
|
||||
volumes:
|
||||
- /mnt/Nextcloud/Nextcloud:/var/www/html:ro
|
||||
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
|
||||
#- /mnt/Nextcloud/caddy/data:/data
|
||||
#- /mnt/Nextcloud/caddy/config:/config
|
||||
- ./caddy/waf:/etc/caddy/waf
|
||||
#- /mnt/Nextcloud/caddy/logs:/var/log/caddy
|
||||
networks:
|
||||
- nextcloud-net
|
||||
depends_on:
|
||||
- caddy-redis
|
||||
|
||||
caddy-redis:
|
||||
image: redis:alpine
|
||||
container_name: caddy-redis
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/Nextcloud/caddy_redis:/data
|
||||
networks:
|
||||
- nextcloud-net
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
clamav:
|
||||
image: clamav/clamav:1.3
|
||||
container_name: clamav
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nextcloud-net
|
||||
volumes:
|
||||
- /mnt/Nextcloud/clamav_data:/var/lib/clamav
|
||||
|
||||
elasticsearch:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
|
||||
container_name: elasticsearch
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health?wait_for_status=yellow || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
environment:
|
||||
- discovery.type=single-node
|
||||
- xpack.security.enabled=false # Easiest for internal Docker communication
|
||||
- "ES_JAVA_OPTS=-Xms512m -Xmx512m" # Recommended to limit memory usage
|
||||
volumes:
|
||||
- /mnt/Nextcloud/es_data:/usr/share/elasticsearch/data
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
go-vod:
|
||||
image: radialapps/go-vod
|
||||
container_name: go-vod
|
||||
restart: always
|
||||
init: true
|
||||
depends_on:
|
||||
- app
|
||||
environment:
|
||||
- NEXTCLOUD_HOST=https://cloud.poppyglen.cc
|
||||
# - NEXTCLOUD_ALLOW_INSECURE=1 # (self-signed certs or no HTTPS)
|
||||
- NVIDIA_VISIBLE_DEVICES=all
|
||||
devices:
|
||||
- /dev/dri:/dev/dri # VA-API (omit for NVENC)
|
||||
volumes:
|
||||
- /mnt/Nextcloud/Nextcloud:/var/www/html:ro
|
||||
# runtime: nvidia # (NVENC)
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
nextcloud-whiteboard-server:
|
||||
image: ghcr.io/nextcloud-releases/whiteboard:stable
|
||||
container_name: nextcloud-whiteboard-server
|
||||
ports:
|
||||
- 3002:3002
|
||||
environment:
|
||||
NEXTCLOUD_URL: https://cloud.poppyglen.cc
|
||||
JWT_SECRET_KEY: ${WHITEBOARD_JWT}
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
cert-exporter:
|
||||
image: redis:alpine
|
||||
container_name: cert-exporter
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nextcloud-net
|
||||
volumes:
|
||||
- ./export-certs.sh:/export-certs.sh:ro
|
||||
- /mnt/Nextcloud/caddy/data:/export
|
||||
command: >
|
||||
sh -c "apk add --no-cache jq coreutils &&
|
||||
while true; do sh /export-certs.sh; sleep 12h; done"
|
||||
depends_on:
|
||||
caddy-redis:
|
||||
condition: service_healthy
|
||||
caddy:
|
||||
condition: service_started
|
||||
|
||||
#mkdir -p /mnt/Nextcloud/mail/{maildata,mailstate,mail-logs,config,caddy}
|
||||
#docker exec -it mail doveadm search -u admin@poppyglen.cc mailbox INBOX ALL
|
||||
#docker exec -it mail doveadm fetch -u admin@poppyglen.cc text mailbox INBOX uid 1
|
||||
mail:
|
||||
image: docker.io/mailserver/docker-mailserver:latest
|
||||
container_name: mail
|
||||
hostname: mail.poppyglen.cc
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
#- SSL_TYPE=manual
|
||||
#- SSL_CERT_PATH=/caddy-data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.poppyglen.cc/mail.poppyglen.cc.crt
|
||||
#- SSL_KEY_PATH=/caddy-data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.poppyglen.cc/mail.poppyglen.cc.key
|
||||
- SSL_TYPE=letsencrypt
|
||||
#- SSL_CERT_PATH=/caddy-data/mail.poppyglen.cc.crt
|
||||
#- SSL_KEY_PATH=/caddy-data/mail.poppyglen.cc.key
|
||||
- TZ=America/Los_Angeles
|
||||
- PERMIT_DOCKER=network
|
||||
- DMS_DEBUG=0
|
||||
- ONE_DIR=1
|
||||
- ENABLE_POSTGREY=0
|
||||
- ENABLE_FAIL2BAN=1
|
||||
- ENABLE_CLAMAV=1
|
||||
- CLAMAV_HOST=clamav
|
||||
- ENABLE_SPAMASSASSIN=1
|
||||
- ENABLE_OPENDKIM=1
|
||||
volumes:
|
||||
- /mnt/Nextcloud/mail/maildata:/var/mail
|
||||
- /mnt/Nextcloud/mail/mailstate:/var/mail-state
|
||||
- /mnt/Nextcloud/mail/mail-logs:/var/log/mail
|
||||
- /mnt/Nextcloud/mail/config:/tmp/docker-mailserver/
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /mnt/Nextcloud/caddy/data/mail.poppyglen.cc.crt:/etc/letsencrypt/live/mail.poppyglen.cc/fullchain.pem
|
||||
- /mnt/Nextcloud/caddy/data/mail.poppyglen.cc.key:/etc/letsencrypt/live/mail.poppyglen.cc/privkey.pem
|
||||
ports:
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
- "465:465"
|
||||
- "587:587"
|
||||
- "993:993"
|
||||
cap_add:
|
||||
- NET_ADMIN # Required for Fail2Ban
|
||||
- SYS_PTRACE
|
||||
networks:
|
||||
- nextcloud-net
|
||||
depends_on:
|
||||
- clamav
|
||||
- cert-exporter
|
||||
#docker exec -it mail setup config dkim
|
||||
#docker exec -it mail cat /tmp/docker-mailserver/opendkim/keys/poppyglen.cc/mail.txt | awk -F'"' '/"/{print $2}' | tr -d '\n'
|
||||
|
||||
ollama:
|
||||
image: ollama/ollama:rocm
|
||||
container_name: ollama
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/Nextcloud/ollama:/root/.ollama
|
||||
- /lib/modules:/lib/modules:ro
|
||||
- /sys/module:/sys/module:ro
|
||||
networks:
|
||||
- nextcloud-net
|
||||
environment:
|
||||
HSA_OVERRIDE_GFX_VERSION: ${HSA_OVERRIDE_GFX_VERSION}
|
||||
HIP_VISIBLE_DEVICES: "0"
|
||||
devices:
|
||||
- /dev/kfd:/dev/kfd
|
||||
- /dev/dri:/dev/dri
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
cap_add:
|
||||
- SYS_MODULE
|
||||
- SYS_RAWIO
|
||||
#docker exec -it ollama ollama pull qwen3:4b
|
||||
|
||||
recording-server:
|
||||
image: nextcloud/aio-talk-recording
|
||||
container_name: nextcloud-recording-server
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NEXTCLOUD_URL=https://cloud.poppyglen.cc
|
||||
- NC_DOMAIN=cloud.poppyglen.cc
|
||||
- NEXTCLOUD_RECORDING_USERNAME=${NEXTCLOUD_RECORDING_USERNAME}
|
||||
- NEXTCLOUD_RECORDING_PASSWORD=${NEXTCLOUD_RECORDING_PASSWORD}
|
||||
- RECORDING_SECRET=${RECORDING_SECRET}
|
||||
- INTERNAL_SECRET=${INTERNAL_SECRET}
|
||||
networks:
|
||||
- nextcloud-net # Must be on the same network as other services
|
||||
|
||||
immich-postgres:
|
||||
container_name: immich_postgres
|
||||
image: tensorchord/pgvecto-rs:pg16-v0.2.0 # Required for vector search
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${IMMICH_DB_PASSWORD}
|
||||
POSTGRES_USER: ${IMMICH_DB_USER}
|
||||
POSTGRES_DB: ${IMMICH_DB_NAME}
|
||||
DB_STORAGE_TYPE: 'HDD'
|
||||
volumes:
|
||||
- /mnt/Nextcloud/immich/immich_db:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
immich-redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2-alpine
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
image: ghcr.io/immich-app/immich-server:release
|
||||
#command: [ "start.sh", "immich" ]
|
||||
extends:
|
||||
file: ./immich/hwaccel.transcoding.yml
|
||||
service: vaapi
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
immich-postgres:
|
||||
condition: service_healthy
|
||||
immich-redis:
|
||||
condition: service_started
|
||||
environment:
|
||||
# This is an anchor that other services will reference
|
||||
&immich-common-env
|
||||
DB_HOSTNAME: immich-postgres
|
||||
DB_USERNAME: ${IMMICH_DB_USER}
|
||||
DB_PASSWORD: ${IMMICH_DB_PASSWORD}
|
||||
DB_DATABASE_NAME: ${IMMICH_DB_NAME}
|
||||
REDIS_HOSTNAME: immich-redis
|
||||
JWT_SECRET: ${IMMICH_JWT_SECRET}
|
||||
# You can add other Immich specific env vars here if needed
|
||||
volumes:
|
||||
- /mnt/Nextcloud/immich/immich_upload:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /mnt/Nextcloud/photos:/import:ro
|
||||
networks:
|
||||
- nextcloud-net
|
||||
#healthcheck:
|
||||
# test: ["CMD", "curl", "-f", "http://localhost:2283/api/server/ping"]
|
||||
# interval: 30s
|
||||
# timeout: 10s
|
||||
# retries: 5
|
||||
# start_period: 30s
|
||||
|
||||
immich-microservices:
|
||||
container_name: immich_microservices
|
||||
image: ghcr.io/immich-app/immich-server:release
|
||||
# This command is required to run microservices
|
||||
#command: [ "start.sh", "microservices" ]
|
||||
extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration
|
||||
file: ./immich/hwaccel.ml.yml
|
||||
service: rocm # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- immich-server
|
||||
environment:
|
||||
# Use the alias to inherit common environment variables
|
||||
<<: *immich-common-env
|
||||
volumes:
|
||||
- /mnt/Nextcloud/immich/immich_upload:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /mnt/Nextcloud/photos:/import:ro
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
image: ghcr.io/immich-app/immich-machine-learning:release
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/Nextcloud/immich/immich_models:/cache
|
||||
environment:
|
||||
# Use the alias to inherit common environment variables
|
||||
<<: *immich-common-env
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
immich-init:
|
||||
image: alpine:3.20
|
||||
container_name: immich-init
|
||||
env_file: .env
|
||||
environment:
|
||||
IMMICH_BASE_URL: http://immich-server:2283/api
|
||||
depends_on:
|
||||
- immich-server
|
||||
#condition: service_healthy
|
||||
volumes:
|
||||
- ./immich/init-immich.sh:/init-immich.sh:ro
|
||||
entrypoint: ["/bin/sh", "/init-immich.sh"]
|
||||
restart: "no"
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
jellyfin:
|
||||
image: jellyfin/jellyfin:latest
|
||||
container_name: jellyfin
|
||||
restart: unless-stopped
|
||||
user: 1000:1000
|
||||
environment:
|
||||
- TZ=America/Los_Angeles
|
||||
- PUID=1000 # Replace with your user ID
|
||||
- PGID=1000 # Replace with your group ID
|
||||
volumes:
|
||||
- /mnt/Nextcloud/jellyfin/config:/config
|
||||
- /mnt/Nextcloud/jellyfin/cache:/cache
|
||||
- /mnt/Nextcloud/movies:/media/movies
|
||||
- /mnt/Nextcloud/tvshows:/media/tvshows
|
||||
- /mnt/Nextcloud/music:/media/music
|
||||
- /mnt/Nextcloud/photos:/media/photos
|
||||
ports:
|
||||
- "8096:8096" # Web UI
|
||||
- "7359:7359/udp" # Client discovery
|
||||
- "1900:1900/udp" # DLNA
|
||||
devices:
|
||||
- /dev/dri:/dev/dri # For Intel Quick Sync or AMD VA-API hardware acceleration
|
||||
networks:
|
||||
- nextcloud-net
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8096/Users/Public"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 40
|
||||
start_period: 30s
|
||||
|
||||
jellyfin-init:
|
||||
image: alpine:3.20
|
||||
container_name: jellyfin-init
|
||||
env_file: .env
|
||||
environment:
|
||||
JELLYFIN_BASE_URL: ${JELLYFIN_BASE_URL}
|
||||
JELLYFIN_INIT_ADMIN_USER: ${JELLYFIN_INIT_ADMIN_USER}
|
||||
JELLYFIN_INIT_ADMIN_PASSWORD: ${JELLYFIN_INIT_ADMIN_PASSWORD}
|
||||
JELLYFIN_INIT_SERVERNAME: ${JELLYFIN_INIT_SERVERNAME}
|
||||
depends_on:
|
||||
jellyfin:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./jellyfin/init-jellyfin.sh:/init-jellyfin.sh:ro
|
||||
- /mnt/Nextcloud/movies:/media/movies
|
||||
- /mnt/Nextcloud/tvshows:/media/tvshows
|
||||
- /mnt/Nextcloud/music:/media/music
|
||||
- /mnt/Nextcloud/photos:/media/photos
|
||||
entrypoint: ["/bin/sh", "/init-jellyfin.sh"]
|
||||
restart: "no"
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
#sunshine:
|
||||
# image: ghcr.io/lizardbyte/sunshine:v2025.628.4510-archlinux
|
||||
# container_name: sunshine
|
||||
# restart: unless-stopped
|
||||
# privileged: true
|
||||
# shm_size: "2gb"
|
||||
# user: "root"
|
||||
# group_add:
|
||||
# - "render"
|
||||
# - "video"
|
||||
# - "input"
|
||||
# environment:
|
||||
# - TZ=America/Los_Angeles
|
||||
# - PUID=1000
|
||||
# - PGID=1000
|
||||
# - LIBVA_DRIVER_NAME=radeonsi
|
||||
# - WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-wayland-0}
|
||||
# - XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/run/user/1000} # replace 1000 with your UID
|
||||
# - SUNSHINE_USERNAME=${SUNSHINE_USERNAME}
|
||||
# - SUNSHINE_PASSWORD=${SUNSHINE_PASSWORD}
|
||||
# - DISPLAY=:1
|
||||
# volumes:
|
||||
# - /mnt/Nextcloud/sunshine/config:/config
|
||||
# - /run/user/1000:/run/user/1000
|
||||
# - /tmp/.X11-unix:/tmp/.X11-unix # optional for X11
|
||||
# - /var/lib/dbus/machine-id:/var/lib/dbus/machine-id:ro
|
||||
# - /run/dbus:/run/dbus:ro
|
||||
# - /run/udev/data:/run/udev/data:ro
|
||||
# ports:
|
||||
# - "47984:47984/udp"
|
||||
# - "47990:47990/tcp"
|
||||
# - "47989:47989/tcp"
|
||||
# - "48010:48010/udp"
|
||||
# - "48010:48010/tcp"
|
||||
# - "47998-48000:47998-48000/udp"
|
||||
# devices:
|
||||
# - /dev/dri:/dev/dri
|
||||
# - /dev/input:/dev/input
|
||||
# - /dev/uinput:/dev/uinput
|
||||
# #networks:
|
||||
# # - nextcloud-net
|
||||
# networks:
|
||||
# sunshine_net:
|
||||
# ipv4_address: 192.168.0.200
|
||||
|
||||
wolf:
|
||||
image: ghcr.io/games-on-whales/wolf:stable
|
||||
container_name: wolf
|
||||
#user: "1000:1000"
|
||||
privileged: true
|
||||
environment:
|
||||
- XDG_RUNTIME_DIR=/tmp/sockets
|
||||
- HOST_APPS_STATE_FOLDER=/mnt/Nextcloud/wolf
|
||||
- WAYLAND_DISPLAY=wayland-1
|
||||
- MESA_LOADER_DRIVER_OVERRIDE=amd
|
||||
- VDPAU_DRIVER=va_gl
|
||||
- LIBVA_DRIVER_NAME=radeonsi
|
||||
- RADV_DEBUG=llvm
|
||||
- WOLF_LOG_LEVEL:DEBUG
|
||||
volumes:
|
||||
- /mnt/Nextcloud/wolf:/mnt/Nextcloud/wolf:rw
|
||||
- /mnt/Nextcloud/wolf/cfg:/etc/wolf/cfg:rw
|
||||
- /run/user/1000:/run/user/1000
|
||||
- /run/udev/data:/run/udev/data:ro
|
||||
- /tmp/sockets:/tmp/sockets:rw
|
||||
- /var/run/docker.sock:/var/run/docker.sock:rw
|
||||
- /var/lib/dbus/machine-id:/var/lib/dbus/machine-id:ro
|
||||
- /usr/share/vulkan/icd.d:/usr/share/vulkan/icd.d:ro
|
||||
- /usr/lib/libvulkan.so:/usr/lib/libvulkan.so:ro
|
||||
- /usr/lib/libvulkan.so.1:/usr/lib/libvulkan.so.1:ro
|
||||
- /usr/lib/libvulkan.so.1.4.321:/usr/lib/libvulkan.so.1.4.321:ro
|
||||
- /usr/lib/libvulkan_radeon.so:/usr/lib/libvulkan_radeon.so:ro
|
||||
- /usr/lib/libEGL_mesa.so:/usr/lib/libEGL_mesa.so:ro
|
||||
- /usr/lib/libEGL_mesa.so.0:/usr/lib/libEGL_mesa.so.0:ro
|
||||
- /usr/lib/libEGL_mesa.so.0.0.0:/usr/lib/libEGL_mesa.so.0.0.0:ro
|
||||
- /usr/lib/libGLX_mesa.so:/usr/lib/libGLX_mesa.so:ro
|
||||
- /usr/lib/libGLX_mesa.so.0:/usr/lib/libGLX_mesa.so.0:ro
|
||||
- /usr/lib/libGLX_mesa.so.0.0.0:/usr/lib/libGLX_mesa.so.0.0.0:ro
|
||||
- /usr/lib/libva.so:/usr/lib/libva.so:ro
|
||||
- /usr/lib/libva.so.2:/usr/lib/libva.so.2:ro
|
||||
- /usr/lib/libva.so.2.0.0:/usr/lib/libva.so.2.0.0:ro
|
||||
- /usr/lib/dri/radeonsi_drv_video.so:/usr/lib/dri/radeonsi_drv_video.so:ro
|
||||
- /usr/lib/x86_64-linux-gnu/dri:/usr/lib/x86_64-linux-gnu/dri:ro
|
||||
- /run/dbus:/run/dbus:ro
|
||||
- /run/udev:/run/udev:rw
|
||||
device_cgroup_rules:
|
||||
- 'c 13:* rmw'
|
||||
devices:
|
||||
- /dev/dri:/dev/dri:rw
|
||||
- /dev/dri/renderD128:/dev/dri/renderD128:rw
|
||||
- /dev/uinput:/dev/uinput
|
||||
- /dev/uhid:/dev/uhid
|
||||
group_add:
|
||||
- "989"
|
||||
- "video"
|
||||
- "994"
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
#networks:
|
||||
# sunshine_net:
|
||||
# ipv4_address: 192.168.0.200
|
||||
|
||||
technitium-dns:
|
||||
image: technitium/dns-server:latest
|
||||
container_name: technitium-dns
|
||||
hostname: technitium-dns
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/Nextcloud/technitium/config:/etc/dns
|
||||
environment:
|
||||
- DNS_SERVER_DOMAIN=home.lan
|
||||
- DNS_SERVER_ADMIN_PASSWORD=${TECHNITIUM_ADMIN_PASSWORD}
|
||||
- DNS_SERVER_FORWARDERS=1.1.1.1,8.8.8.8,2606:4700:4700::1111,2001:4860:4860::8888
|
||||
- DNS_SERVER_FORWARDER_PROTOCOL=Udp
|
||||
- DNS_SERVER_RECURSION=AllowOnlyForPrivateNetworks
|
||||
- DNS_SERVER_RECURSION_ALLOWED_NETWORKS=192.168.0.0/24,fd00::/8
|
||||
ports:
|
||||
- "5380:5380/tcp" # Web UI
|
||||
- "53:53/tcp" # DNS
|
||||
- "53:53/udp" # DNS
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
#echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
|
||||
#echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
|
||||
|
||||
vaultwarden:
|
||||
image: vaultwarden/server:latest
|
||||
container_name: vaultwarden
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- WEBSOCKET_ENABLED=true # Required for Caddy reverse proxy
|
||||
- SIGNUPS_ALLOWED=true # Set to false after you've created your account
|
||||
- ADMIN_TOKEN=${VAULTWARDEN_ADMIN_TOKEN} # Add a secure token to your .env file
|
||||
volumes:
|
||||
- /mnt/Nextcloud/vaultwarden:/data
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
wireguard:
|
||||
image: lscr.io/linuxserver/wireguard:latest
|
||||
container_name: wireguard
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=America/Los_Angeles
|
||||
- SERVERURL=vpn.poppyglen.cc # or your public domain, e.g., vpn.poppyglen.cc
|
||||
- PEERS=1 # Number of peer configs to generate
|
||||
- PEERDNS=auto
|
||||
- INTERNAL_SUBNET=10.13.13.0/24
|
||||
volumes:
|
||||
- /mnt/Nextcloud/wireguard/config:/config
|
||||
- /lib/modules:/lib/modules
|
||||
ports:
|
||||
- 51820:51820/udp
|
||||
sysctls:
|
||||
- net.ipv4.conf.all.src_valid_mark=1
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nextcloud-net
|
||||
|
||||
#ipfs:
|
||||
# image: ipfs/kubo:latest
|
||||
# container_name: ipfs_node
|
||||
# restart: unless-stopped
|
||||
# environment:
|
||||
# - IPFS_PROFILE=server
|
||||
# volumes:
|
||||
# - /mnt/Nextcloud/ipfs_data:/data/ipfs
|
||||
# ports:
|
||||
# - "4001:4001/tcp" # Swarm - TCP
|
||||
# - "4001:4001/udp" # Swarm - QUIC
|
||||
# - "8080:8080" # Gateway
|
||||
# - "5001:5001" # API
|
||||
# networks:
|
||||
# - nextcloud-net
|
||||
480
init-mail.sh
Normal file
480
init-mail.sh
Normal file
@@ -0,0 +1,480 @@
|
||||
#!/bin/bash
|
||||
#set -euo pipefail # Exit on error, unset variables are errors
|
||||
|
||||
# ---------------------------
|
||||
# Configuration
|
||||
# ---------------------------
|
||||
# Mail
|
||||
MAIL_CONFIG_DIR="/mnt/Nextcloud/mail/config"
|
||||
ACCOUNT_FILE="$MAIL_CONFIG_DIR/postfix-accounts.cf"
|
||||
ADMIN_EMAIL="admin@poppyglen.cc"
|
||||
# Caddy WAF
|
||||
CADDY_WAF_DIR="./caddy/waf"
|
||||
# Sunshine
|
||||
SUNSHINE_CONFIG_DIR="/mnt/Nextcloud/sunshine/config"
|
||||
SUNSHINE_CONFIG_FILE="$SUNSHINE_CONFIG_DIR/sunshine.conf"
|
||||
SUNSHINE_USERNAME="${SUNSHINE_USERNAME:-sunshine_admin}"
|
||||
SUNSHINE_PASSWORD="${SUNSHINE_PASSWORD:-}"
|
||||
# Authelia
|
||||
AUTHELIA_CONFIG_DIR="./authelia"
|
||||
AUTHELIA_USERS_FILE="$AUTHELIA_CONFIG_DIR/users_database.yml"
|
||||
AUTHELIA_CONFIG_FILE="$AUTHELIA_CONFIG_DIR/configuration.yml"
|
||||
AUTHELIA_REDIS_DIR="/mnt/Nextcloud/authelia-redis"
|
||||
# MinIO
|
||||
MINIO_DATA_DIR="/mnt/Nextcloud/minio-data"
|
||||
|
||||
# ---------------------------
|
||||
# General Functions
|
||||
# ---------------------------
|
||||
require_root() {
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo "❌ Error: This script must be run as root or with sudo." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
load_env() {
|
||||
if [[ -f ".env" ]]; then
|
||||
echo "📄 Loading environment variables from .env..."
|
||||
while IFS='=' read -r key value; do
|
||||
[[ -z "$key" || "$key" =~ ^# || "$key" =~ \[|\{ ]] && continue
|
||||
export "$key=$value"
|
||||
done < .env
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------
|
||||
# Mail Setup Functions
|
||||
# ---------------------------
|
||||
get_password() {
|
||||
if [[ -z "${MAIL_ADMIN_PASSWORD:-}" ]]; then
|
||||
read -sp "Enter a password for the mail admin ($ADMIN_EMAIL): " MAIL_ADMIN_PASSWORD
|
||||
echo
|
||||
else
|
||||
echo "✅ Loaded MAIL_ADMIN_PASSWORD from .env"
|
||||
fi
|
||||
|
||||
if [[ -z "$MAIL_ADMIN_PASSWORD" ]]; then
|
||||
echo "❌ Error: Password cannot be empty." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
generate_hash() {
|
||||
local password="$1"
|
||||
docker run --rm docker.io/mailserver/docker-mailserver:latest \
|
||||
doveadm pw -s SHA512-CRYPT -p "$password"
|
||||
}
|
||||
|
||||
create_account_file() {
|
||||
echo "📝 Creating account file: $ACCOUNT_FILE"
|
||||
mkdir -p "$MAIL_CONFIG_DIR"
|
||||
echo "$ADMIN_EMAIL|$HASH" > "$ACCOUNT_FILE"
|
||||
|
||||
# Add additional users from USERS_JSON
|
||||
if [[ -n "${USERS_JSON:-}" ]]; then
|
||||
echo "📋 Adding additional users from USERS_JSON..."
|
||||
echo "$USERS_JSON" | jq -c '.[]' | while read -r user; do
|
||||
username=$(echo "$user" | jq -r '.username')
|
||||
password=$(echo "$user" | jq -r '.password')
|
||||
if [[ -z "$username" || -z "$password" ]]; then
|
||||
echo "⚠️ Skipping invalid user entry: $user"
|
||||
continue
|
||||
fi
|
||||
user_hash=$(generate_hash "$password")
|
||||
echo "$username@$MAIL_DOMAIN|$user_hash" >> "$ACCOUNT_FILE"
|
||||
echo "➕ Added user: $username@$MAIL_DOMAIN"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
setup_mail() {
|
||||
echo "📧 Mail Server First-Time Setup..."
|
||||
if [[ -f "$ACCOUNT_FILE" ]]; then
|
||||
read -p "⚠️ A mail account file already exists. Overwrite? (y/N) " -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
echo "Skipping mail setup."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
get_password
|
||||
HASH=$(generate_hash "$MAIL_ADMIN_PASSWORD")
|
||||
create_account_file
|
||||
echo "✅ Mail setup complete."
|
||||
}
|
||||
|
||||
# ---------------------------
|
||||
# Sunshine Config Setup
|
||||
# ---------------------------
|
||||
setup_sunshine_config() {
|
||||
echo "☀️ Setting up Sunshine config..."
|
||||
mkdir -p "$SUNSHINE_CONFIG_DIR"
|
||||
|
||||
if [[ -f "$SUNSHINE_CONFIG_FILE" ]]; then
|
||||
read -p "⚠️ Sunshine config already exists. Overwrite? (y/N) " -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
echo "Skipping Sunshine config setup."
|
||||
return
|
||||
fi
|
||||
rm "$SUNSHINE_CONFIG_FILE"
|
||||
fi
|
||||
|
||||
local password="$SUNSHINE_PASSWORD"
|
||||
if [[ -z "$password" ]]; then
|
||||
read -sp "Enter a password for Sunshine user ($SUNSHINE_USERNAME): " password
|
||||
echo
|
||||
fi
|
||||
|
||||
if [[ -z "$password" ]]; then
|
||||
echo "❌ Error: Sunshine password cannot be empty." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔐 Creating Sunshine credentials..."
|
||||
docker compose run --rm sunshine --creds "$SUNSHINE_USERNAME" "$password"
|
||||
sed -i '1i username = "'"$SUNSHINE_USERNAME"'"' "$SUNSHINE_CONFIG_FILE"
|
||||
echo "🔧 Setting permissions for Sunshine config..."
|
||||
chown -R 1000:1000 "$SUNSHINE_CONFIG_DIR"
|
||||
echo "✅ Sunshine config created successfully!"
|
||||
}
|
||||
|
||||
# ---------------------------
|
||||
# Caddy WAF Setup
|
||||
# ---------------------------
|
||||
setup_caddy_waf() {
|
||||
echo "🛡️ Setting up Caddy WAF..."
|
||||
if ! command -v git &> /dev/null; then
|
||||
echo "❌ Error: git is not installed. Please install git to continue." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$CADDY_WAF_DIR"
|
||||
local owasp_crs_dir="$CADDY_WAF_DIR/owasp-crs"
|
||||
if [[ -d "$owasp_crs_dir" ]]; then
|
||||
echo "✅ OWASP Core Rule Set already exists. Skipping clone."
|
||||
else
|
||||
echo "⏳ Cloning OWASP Core Rule Set..."
|
||||
git clone https://github.com/coreruleset/coreruleset.git "$owasp_crs_dir"
|
||||
fi
|
||||
|
||||
local crs_setup_conf="$owasp_crs_dir/crs-setup.conf"
|
||||
if [[ ! -f "$crs_setup_conf" ]]; then
|
||||
echo "📝 Creating crs-setup.conf from example..."
|
||||
cp "$owasp_crs_dir/crs-setup.conf.example" "$crs_setup_conf"
|
||||
fi
|
||||
|
||||
local coraza_conf="$CADDY_WAF_DIR/coraza.conf"
|
||||
if [[ -f "$coraza_conf" ]]; then
|
||||
echo "✅ coraza.conf already exists. Skipping."
|
||||
else
|
||||
echo "📝 Creating main WAF configuration (coraza.conf)..."
|
||||
cat <<EOF > "$coraza_conf"
|
||||
# This file tells Coraza where to find the OWASP Core Rule Set.
|
||||
Include @owasp_crs/crs-setup.conf
|
||||
Include @owasp_crs/rules/*.conf
|
||||
EOF
|
||||
fi
|
||||
echo "✅ Caddy WAF setup is complete!"
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Authelia Setup
|
||||
# ---------------------------
|
||||
|
||||
generate_authelia_hash() {
|
||||
local pass="$1"
|
||||
local output
|
||||
output=$(docker run --rm authelia/authelia authelia crypto hash generate argon2 --password "$pass" 2>&1)
|
||||
echo "$output" | grep -o '\$argon2id\$.*'
|
||||
}
|
||||
|
||||
setup_authelia() {
|
||||
echo "🔐 Setting up Authelia..."
|
||||
if ! command -v openssl &> /dev/null; then
|
||||
echo "❌ Error: openssl is not installed. Please install it to generate secure secrets." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$AUTHELIA_CONFIG_DIR"
|
||||
if [[ -f "$AUTHELIA_USERS_FILE" ]]; then
|
||||
read -p "⚠️ Authelia user database already exists. Overwrite? (y/N) " -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
echo "Skipping Authelia setup."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Admin User Setup ---
|
||||
local admin_username="${AUTHELIA_ADMIN_USER:-}"
|
||||
local admin_password="${AUTHELIA_ADMIN_PASSWORD:-}"
|
||||
|
||||
if [[ -z "$admin_username" ]]; then
|
||||
read -p "Enter a username for the Authelia admin: " admin_username
|
||||
else
|
||||
echo "✅ Loaded AUTHELIA_ADMIN_USER from .env"
|
||||
fi
|
||||
|
||||
if [[ -z "$admin_password" ]]; then
|
||||
read -sp "Enter a password for Authelia user '$admin_username': " admin_password
|
||||
echo
|
||||
else
|
||||
echo "✅ Loaded AUTHELIA_ADMIN_PASSWORD from .env"
|
||||
fi
|
||||
|
||||
if [[ -z "$admin_username" || -z "$admin_password" ]]; then
|
||||
echo "❌ Error: Authelia admin username and password cannot be empty." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "⏳ Generating password hash for admin user..."
|
||||
local admin_hash
|
||||
admin_hash=$(generate_authelia_hash "$admin_password")
|
||||
|
||||
if [[ -z "$admin_hash" ]]; then
|
||||
echo "❌ Error: Could not generate a valid hash for the admin user." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Authelia Configuration File Generation ---
|
||||
echo "⏳ Generating secrets..."
|
||||
local session_secret
|
||||
session_secret=$(openssl rand -hex 32)
|
||||
local encryption_key
|
||||
encryption_key=$(openssl rand -hex 32)
|
||||
local jwt_secret
|
||||
jwt_secret=$(openssl rand -hex 32)
|
||||
|
||||
echo "📝 Creating Authelia configuration file..."
|
||||
cat <<EOF > "$AUTHELIA_CONFIG_FILE"
|
||||
# Main server configuration
|
||||
server:
|
||||
address: tcp://0.0.0.0:9091
|
||||
|
||||
# Session configuration using modern format
|
||||
session:
|
||||
secret: "$session_secret"
|
||||
cookies:
|
||||
- name: poppy_session
|
||||
domain: poppyglen.cc
|
||||
authelia_url: https://auth.poppyglen.cc
|
||||
# Use Redis for session storage for better performance
|
||||
redis:
|
||||
host: authelia-redis
|
||||
port: 6379
|
||||
|
||||
# Storage for user data, password reset tokens, etc.
|
||||
storage:
|
||||
encryption_key: "$encryption_key"
|
||||
# Use a local SQLite database for persistent storage
|
||||
local:
|
||||
path: /config/db.sqlite3
|
||||
|
||||
# User authentication backend
|
||||
authentication_backend:
|
||||
file:
|
||||
path: /config/users_database.yml
|
||||
|
||||
# Access control rules
|
||||
access_control:
|
||||
default_policy: deny
|
||||
rules:
|
||||
- domain: "cloud.poppyglen.cc"
|
||||
policy: bypass
|
||||
resources:
|
||||
- '^/index\.php/apps/memories/static/go-vod\?arch=.+$'
|
||||
methods: [GET]
|
||||
- domain: "cloud.poppyglen.cc"
|
||||
policy: bypass
|
||||
resources:
|
||||
- '^/apps/circles/async/.*$'
|
||||
methods: [GET]
|
||||
- domain: "cloud.poppyglen.cc"
|
||||
policy: bypass
|
||||
resources:
|
||||
- '^/index\.php/apps/richdocuments/wopi/.*$'
|
||||
- domain: "cloud.poppyglen.cc"
|
||||
policy: bypass
|
||||
resources:
|
||||
- '^/\.well-known/(card|cal)dav$'
|
||||
- domain: "cloud.poppyglen.cc"
|
||||
policy: bypass
|
||||
resources:
|
||||
- '^/remote\.php/(dav|webdav)/.*$'
|
||||
- domain: "cloud.poppyglen.cc"
|
||||
policy: bypass
|
||||
resources:
|
||||
- '^/ocs/v[12]\.php/.*$'
|
||||
- '^/remote\.php/status\.php$'
|
||||
- domain:
|
||||
- "cloud.poppyglen.cc"
|
||||
- "immich.poppyglen.cc"
|
||||
- "jellyfin.poppyglen.cc"
|
||||
policy: two_factor
|
||||
subject:
|
||||
- "group:admins"
|
||||
- "group:users"
|
||||
- "group:family"
|
||||
- domain: "auth.poppyglen.cc"
|
||||
policy: bypass
|
||||
|
||||
# Required for password reset functionality
|
||||
identity_validation:
|
||||
reset_password:
|
||||
jwt_secret: "$jwt_secret"
|
||||
|
||||
# Notification settings
|
||||
notifier:
|
||||
disable_startup_check: true
|
||||
smtp:
|
||||
address: "submission://mail:587"
|
||||
username: $MAIL_ADMIN_EMAIL
|
||||
password: $MAIL_ADMIN_PASSWORD
|
||||
sender: "Authelia <admin@poppyglen.cc>"
|
||||
identifier: "authelia.poppyglen.cc"
|
||||
tls:
|
||||
skip_verify: true
|
||||
# disable_require_tls: false
|
||||
# filesystem:
|
||||
# filename: /config/notification.log
|
||||
EOF
|
||||
|
||||
# --- Authelia User Database File Generation ---
|
||||
echo "📝 Creating Authelia user database for admin..."
|
||||
{
|
||||
echo "users:"
|
||||
echo " $admin_username:"
|
||||
echo " displayname: \"Admin User\""
|
||||
echo " password: '$admin_hash'"
|
||||
echo " email: \"admin@poppyglen.cc\""
|
||||
echo " groups:"
|
||||
echo " - admins"
|
||||
} > "$AUTHELIA_USERS_FILE"
|
||||
|
||||
|
||||
# --- START: New section to add users from JSON ---
|
||||
if [[ -n "${USERS_JSON:-}" ]]; then
|
||||
echo "📋 Adding additional Authelia users from USERS_JSON..."
|
||||
echo "$USERS_JSON" | jq -c '.[]' | while read -r user_json; do
|
||||
local username=$(echo "$user_json" | jq -r '.username')
|
||||
local password=$(echo "$user_json" | jq -r '.password')
|
||||
local name=$(echo "$user_json" | jq -r '.name')
|
||||
local email=$(echo "$user_json" | jq -r '.email')
|
||||
|
||||
if [[ -z "$username" || -z "$password" ]]; then
|
||||
echo "⚠️ Skipping invalid user entry: $user_json"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "⏳ Generating password hash for user '$username'..."
|
||||
local user_hash
|
||||
user_hash=$(generate_authelia_hash "$password")
|
||||
|
||||
if [[ -z "$user_hash" ]]; then
|
||||
echo "❌ Error: Could not generate a valid hash for user '$username'." >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
# Append the new user to the users_database.yml file
|
||||
{
|
||||
echo " $username:"
|
||||
echo " displayname: \"$name\""
|
||||
echo " password: '$user_hash'"
|
||||
echo " email: \"$email\""
|
||||
echo " groups:"
|
||||
echo " - users"
|
||||
echo " - family"
|
||||
} >> "$AUTHELIA_USERS_FILE"
|
||||
echo "➕ Added Authelia user: $username"
|
||||
done
|
||||
fi
|
||||
# --- END: New section ---
|
||||
|
||||
echo "✅ Authelia setup is complete!"
|
||||
}
|
||||
|
||||
# ---------------------------
|
||||
# Cleanup Function
|
||||
# ---------------------------
|
||||
clean_services() {
|
||||
local COMPOSE_FILE="docker-compose.yml"
|
||||
if [[ ! -f "$COMPOSE_FILE" ]]; then
|
||||
echo "❌ Error: $COMPOSE_FILE not found in the current directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "This script will PERMANENTLY DELETE data for the selected services."
|
||||
read -p "Are you sure you want to proceed with cleanup? (y/N) " -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
echo "Operation cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "⬇️ Bringing down Docker containers and networks..."
|
||||
docker compose down --remove-orphans
|
||||
|
||||
# Helper function to clean directories
|
||||
_clean_dir_contents() {
|
||||
local service_name="$1"
|
||||
shift
|
||||
local dirs_to_clean=("$@")
|
||||
|
||||
read -p "Do you want to delete ALL data for $service_name? (y/N) " -r clean_response
|
||||
if [[ "$clean_response" =~ ^[Yy]$ ]]; then
|
||||
echo "🗑️ Cleaning $service_name directories..."
|
||||
for vol in "${dirs_to_clean[@]}"; do
|
||||
if [[ -d "$vol" ]]; then
|
||||
echo "Cleaning host directory: $vol"
|
||||
find "$vol" -mindepth 1 -delete
|
||||
else
|
||||
echo " -> Directory not found, skipping."
|
||||
fi
|
||||
done
|
||||
echo "✅ $service_name data has been cleared."
|
||||
else
|
||||
echo "Skipping $service_name data cleanup."
|
||||
fi
|
||||
}
|
||||
|
||||
_clean_dir_contents "Authelia" "$AUTHELIA_CONFIG_DIR" "$AUTHELIA_REDIS_DIR"
|
||||
_clean_dir_contents "Nextcloud Core" "/mnt/Nextcloud/Nextcloud" "/mnt/Nextcloud/nextcloud_db" "/mnt/Nextcloud/redis" "/mnt/Nextcloud/clamav_data" "/mnt/Nextcloud/es_data"
|
||||
_clean_dir_contents "MinIO S3 Storage" "$MINIO_DATA_DIR"
|
||||
_clean_dir_contents "Immich" "/mnt/Nextcloud/immich/immich_db" "/mnt/Nextcloud/immich/immich_upload" "/mnt/Nextcloud/immich/immich_models"
|
||||
_clean_dir_contents "Jellyfin" "/mnt/Nextcloud/jellyfin/config" "/mnt/Nextcloud/jellyfin/cache"
|
||||
_clean_dir_contents "Mail Server" "/mnt/Nextcloud/mail/maildata" "/mnt/Nextcloud/mail/mailstate" "/mnt/Nextcloud/mail/mail-logs" "/mnt/Nextcloud/mail/config"
|
||||
_clean_dir_contents "Sunshine" "/mnt/Nextcloud/sunshine"
|
||||
_clean_dir_contents "Technitium DNS" "/mnt/Nextcloud/technitium/config"
|
||||
_clean_dir_contents "Ollama" "/mnt/Nextcloud/ollama"
|
||||
_clean_dir_contents "Caddy (Redis Data)" "/mnt/Nextcloud/caddy_redis"
|
||||
|
||||
echo "✅ Cleanup complete."
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Main Execution
|
||||
# ---------------------------
|
||||
main() {
|
||||
require_root
|
||||
|
||||
if [[ "${1:-}" == "clean" ]]; then
|
||||
clean_services
|
||||
else
|
||||
echo "🚀 Services First-Time Setup Script"
|
||||
echo "------------------------------------"
|
||||
load_env
|
||||
|
||||
# --- Run Setup Functions ---
|
||||
setup_mail
|
||||
setup_caddy_waf
|
||||
setup_authelia
|
||||
setup_sunshine_config
|
||||
|
||||
echo "------------------------------------"
|
||||
echo "✅ All setups complete! You can now run: docker compose up -d"
|
||||
echo "To clean up this setup, run: sudo ./init-services.sh clean"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user