feat: create net stack consolidating proxy, netdata, watchtower, and umami
- Create net/compose.yaml with 4 services (traefik, netdata, watchtower, umami) - Update arty.yml with NET_* environment variables - Update compose.yaml to include net instead of individual stacks - Update restic volume references to net_letsencrypt_data and net_netdata_config - Copy configuration files to net/ directory (Dockerfile, dynamic/, go.d/, etc.) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
11
net/Dockerfile
Normal file
11
net/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
# Dockerfile for Netdata with msmtp support for email alerts
|
||||
FROM netdata/netdata:latest
|
||||
|
||||
# Install msmtp for sending emails
|
||||
RUN apt-get update && \
|
||||
apt-get install -y msmtp msmtp-mta ca-certificates && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set proper permissions for msmtp config
|
||||
RUN chmod 600 /etc/msmtprc || true
|
||||
238
net/compose.yaml
Normal file
238
net/compose.yaml
Normal file
@@ -0,0 +1,238 @@
|
||||
services:
|
||||
# Traefik - Reverse proxy and load balancer
|
||||
traefik:
|
||||
image: ${NET_PROXY_DOCKER_IMAGE}
|
||||
container_name: ${NET_COMPOSE_PROJECT_NAME}_traefik
|
||||
restart: unless-stopped
|
||||
command:
|
||||
# API & Dashboard
|
||||
- '--api.dashboard=true'
|
||||
- '--api.insecure=false'
|
||||
|
||||
# Ping endpoint for healthcheck
|
||||
- '--ping=true'
|
||||
|
||||
# Experimental plugins
|
||||
- '--experimental.plugins.sablier.modulename=github.com/acouvreur/sablier'
|
||||
- '--experimental.plugins.sablier.version=v1.8.0'
|
||||
|
||||
# Logging
|
||||
- '--log.level=${NET_PROXY_LOG_LEVEL:-INFO}'
|
||||
- '--accesslog=true'
|
||||
|
||||
# Global
|
||||
- '--global.sendAnonymousUsage=false'
|
||||
- '--global.checkNewVersion=true'
|
||||
|
||||
# Docker Provider
|
||||
- '--providers.docker=true'
|
||||
- '--providers.docker.exposedbydefault=false'
|
||||
- '--providers.docker.network=${NETWORK_NAME}'
|
||||
|
||||
# File Provider for dynamic configuration
|
||||
- '--providers.file.directory=/etc/traefik/dynamic'
|
||||
- '--providers.file.watch=true'
|
||||
|
||||
# Entrypoints
|
||||
- '--entrypoints.web.address=:${NET_PROXY_PORT_HTTP:-80}'
|
||||
- '--entrypoints.web-secure.address=:${NET_PROXY_PORT_HTTPS:-443}'
|
||||
|
||||
# Global HTTP to HTTPS redirect
|
||||
- '--entrypoints.web.http.redirections.entryPoint.to=web-secure'
|
||||
- '--entrypoints.web.http.redirections.entryPoint.scheme=https'
|
||||
- '--entrypoints.web.http.redirections.entryPoint.permanent=true'
|
||||
|
||||
# Security Headers (applied globally)
|
||||
- '--entrypoints.web-secure.http.middlewares=security-headers@file'
|
||||
|
||||
# Let's Encrypt
|
||||
- '--certificatesresolvers.resolver.acme.tlschallenge=true'
|
||||
- '--certificatesresolvers.resolver.acme.email=${ADMIN_EMAIL}'
|
||||
- '--certificatesresolvers.resolver.acme.storage=/letsencrypt/acme.json'
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "traefik", "healthcheck", "--ping"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
environment:
|
||||
AUTH_USERS: ${AUTH_USERS}
|
||||
|
||||
networks:
|
||||
- compose_network
|
||||
|
||||
ports:
|
||||
- "${NET_PROXY_PORT_HTTP:-80}:80"
|
||||
- "${NET_PROXY_PORT_HTTPS:-443}:443"
|
||||
|
||||
volumes:
|
||||
- letsencrypt_data:/letsencrypt
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./dynamic:/etc/traefik/dynamic:ro
|
||||
|
||||
labels:
|
||||
- 'traefik.enable=true'
|
||||
# HTTP to HTTPS redirect
|
||||
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-traefik-redirect-web-secure.redirectscheme.scheme=https'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-traefik-redirect-web-secure'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.rule=Host(`${NET_PROXY_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web.entrypoints=web'
|
||||
# HTTPS router with auth
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.rule=Host(`${NET_PROXY_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.tls.certresolver=resolver'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.entrypoints=web-secure'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.service=api@internal'
|
||||
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-traefik-auth.basicauth.users=${AUTH_USERS}'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.middlewares=${NET_COMPOSE_PROJECT_NAME}-traefik-auth'
|
||||
- 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-traefik-web-secure.loadbalancer.server.port=8080'
|
||||
- 'traefik.docker.network=${NETWORK_NAME}'
|
||||
|
||||
# Netdata - Real-time monitoring
|
||||
netdata:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: ${NET_NETDATA_IMAGE:-netdata/netdata:latest}
|
||||
container_name: ${NET_COMPOSE_PROJECT_NAME}_netdata
|
||||
restart: unless-stopped
|
||||
hostname: ${NET_NETDATA_HOSTNAME:-netdata.pivoine.art}
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
- SYS_ADMIN
|
||||
security_opt:
|
||||
- apparmor:unconfined
|
||||
volumes:
|
||||
- netdata_config:/etc/netdata
|
||||
- netdata_lib:/var/lib/netdata
|
||||
- netdata_cache:/var/cache/netdata
|
||||
- ./go.d/postgres.conf:/etc/netdata/go.d/postgres.conf:ro
|
||||
- ./go.d/filecheck.conf:/etc/netdata/go.d/filecheck.conf:ro
|
||||
- ./health_alarm_notify.conf:/etc/netdata/health_alarm_notify.conf:ro
|
||||
- ./msmtprc:/etc/netdata/msmtprc:ro
|
||||
- /mnt/hidrive/users/valknar/Backup:/mnt/hidrive/users/valknar/Backup:ro
|
||||
- /etc/passwd:/host/etc/passwd:ro
|
||||
- /etc/group:/host/etc/group:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /etc/os-release:/host/etc/os-release:ro
|
||||
- /var/log:/host/var/log:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
- NETDATA_CLAIM_TOKEN=${NETDATA_CLAIM_TOKEN:-}
|
||||
- NETDATA_CLAIM_URL=${NETDATA_CLAIM_URL:-}
|
||||
- NETDATA_CLAIM_ROOMS=${NETDATA_CLAIM_ROOMS:-}
|
||||
- MATTERMOST_WEBHOOK_URL=${MATTERMOST_WEBHOOK_URL:-}
|
||||
networks:
|
||||
- compose_network
|
||||
labels:
|
||||
- 'traefik.enable=${NET_TRAEFIK_ENABLED}'
|
||||
# HTTP to HTTPS redirect
|
||||
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-netdata-redirect-web-secure.redirectscheme.scheme=https'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-netdata-redirect-web-secure'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.rule=Host(`${NET_NETDATA_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web.entrypoints=web'
|
||||
# HTTPS router
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.rule=Host(`${NET_NETDATA_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.tls.certresolver=resolver'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.entrypoints=web-secure'
|
||||
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-netdata-compress.compress=true'
|
||||
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-netdata-auth.basicauth.users=${AUTH_USERS}'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-netdata-web-secure.middlewares=${NET_COMPOSE_PROJECT_NAME}-netdata-auth,${NET_COMPOSE_PROJECT_NAME}-netdata-compress,security-headers@file'
|
||||
# Service
|
||||
- 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-netdata.loadbalancer.server.port=19999'
|
||||
- 'traefik.docker.network=${NETWORK_NAME}'
|
||||
# Watchtower
|
||||
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
|
||||
|
||||
# Watchtower - Automatic container updates
|
||||
watchtower:
|
||||
image: containrrr/watchtower:latest
|
||||
container_name: ${NET_COMPOSE_PROJECT_NAME}_watchtower
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
# Check for updates every 5 minutes (300 seconds)
|
||||
WATCHTOWER_POLL_INTERVAL: ${WATCHTOWER_POLL_INTERVAL:-300}
|
||||
# Only update containers with the watchtower label
|
||||
WATCHTOWER_LABEL_ENABLE: ${WATCHTOWER_LABEL_ENABLE:-true}
|
||||
# Clean up old images after update
|
||||
WATCHTOWER_CLEANUP: ${WATCHTOWER_CLEANUP:-true}
|
||||
# Include stopped containers
|
||||
WATCHTOWER_INCLUDE_STOPPED: ${WATCHTOWER_INCLUDE_STOPPED:-false}
|
||||
# Include restarting containers
|
||||
WATCHTOWER_INCLUDE_RESTARTING: ${WATCHTOWER_INCLUDE_RESTARTING:-true}
|
||||
# Run once and exit (set to false for continuous monitoring)
|
||||
WATCHTOWER_RUN_ONCE: ${WATCHTOWER_RUN_ONCE:-false}
|
||||
# Notifications via Shoutrrr
|
||||
WATCHTOWER_NOTIFICATIONS: ${WATCHTOWER_NOTIFICATIONS:-}
|
||||
WATCHTOWER_NOTIFICATION_URL: ${WATCHTOWER_NOTIFICATION_URL:-}
|
||||
# Log level (trace, debug, info, warn, error, fatal, panic)
|
||||
WATCHTOWER_LOG_LEVEL: ${WATCHTOWER_LOG_LEVEL:-info}
|
||||
# Rolling restart (update one container at a time)
|
||||
WATCHTOWER_ROLLING_RESTART: ${WATCHTOWER_ROLLING_RESTART:-false}
|
||||
labels:
|
||||
# Allow watchtower to update itself
|
||||
- com.centurylinklabs.watchtower.enable=true
|
||||
|
||||
# Umami - Web analytics
|
||||
umami:
|
||||
image: ${NET_TRACK_DOCKER_IMAGE}
|
||||
container_name: ${NET_COMPOSE_PROJECT_NAME}_umami
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
TZ: ${TIMEZONE:-Europe/Amsterdam}
|
||||
|
||||
# Database Configuration
|
||||
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@${CORE_DB_HOST}:${CORE_DB_PORT}/${NET_TRACK_DB_NAME}
|
||||
DATABASE_TYPE: postgresql
|
||||
|
||||
# Application Secret
|
||||
APP_SECRET: ${TRACK_APP_SECRET}
|
||||
|
||||
# Redis Cache Integration
|
||||
REDIS_URL: redis://${CORE_REDIS_HOST}:${CORE_REDIS_PORT}
|
||||
CACHE_ENABLED: true
|
||||
|
||||
networks:
|
||||
- compose_network
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/heartbeat || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 40s
|
||||
|
||||
labels:
|
||||
# Traefik Configuration
|
||||
- 'traefik.enable=${NET_TRAEFIK_ENABLED}'
|
||||
|
||||
# HTTP to HTTPS redirect
|
||||
- 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-umami-redirect-web-secure.redirectscheme.scheme=https'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-umami-redirect-web-secure'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.rule=Host(`${NET_TRACK_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web.entrypoints=web'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.rule=Host(`${NET_TRACK_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.tls.certresolver=resolver'
|
||||
- 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.entrypoints=web-secure'
|
||||
- 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-umami-web-secure.loadbalancer.server.port=3000'
|
||||
- 'traefik.docker.network=${NETWORK_NAME}'
|
||||
|
||||
volumes:
|
||||
letsencrypt_data:
|
||||
name: ${NET_COMPOSE_PROJECT_NAME}_letsencrypt_data
|
||||
netdata_config:
|
||||
name: ${NET_COMPOSE_PROJECT_NAME}_netdata_config
|
||||
netdata_lib:
|
||||
name: ${NET_COMPOSE_PROJECT_NAME}_netdata_lib
|
||||
netdata_cache:
|
||||
name: ${NET_COMPOSE_PROJECT_NAME}_netdata_cache
|
||||
|
||||
networks:
|
||||
compose_network:
|
||||
name: ${NETWORK_NAME}
|
||||
external: true
|
||||
61
net/dynamic/security.yaml
Normal file
61
net/dynamic/security.yaml
Normal file
@@ -0,0 +1,61 @@
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
minVersion: VersionTLS12
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
|
||||
- TLS_AES_128_GCM_SHA256
|
||||
- TLS_AES_256_GCM_SHA384
|
||||
- TLS_CHACHA20_POLY1305_SHA256
|
||||
curvePreferences:
|
||||
- CurveP521
|
||||
- CurveP384
|
||||
sniStrict: true
|
||||
|
||||
http:
|
||||
middlewares:
|
||||
# Security Headers Middleware
|
||||
security-headers:
|
||||
headers:
|
||||
# HSTS (HTTP Strict Transport Security)
|
||||
stsSeconds: 31536000
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
|
||||
# Force HTTPS
|
||||
forceSTSHeader: true
|
||||
|
||||
# Clickjacking protection
|
||||
customFrameOptionsValue: "SAMEORIGIN"
|
||||
|
||||
# XSS Protection
|
||||
browserXssFilter: true
|
||||
|
||||
# Content Type sniffing protection
|
||||
contentTypeNosniff: true
|
||||
|
||||
# Referrer Policy
|
||||
referrerPolicy: "strict-origin-when-cross-origin"
|
||||
|
||||
# Permissions Policy (formerly Feature Policy)
|
||||
customResponseHeaders:
|
||||
X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
|
||||
Permissions-Policy: "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), accelerometer=(), gyroscope=()"
|
||||
X-Content-Type-Options: "nosniff"
|
||||
X-Frame-Options: "SAMEORIGIN"
|
||||
|
||||
# Rate Limiting Middleware (optional, can be applied per service)
|
||||
rate-limit:
|
||||
rateLimit:
|
||||
average: 100
|
||||
burst: 50
|
||||
period: 1s
|
||||
|
||||
# Rate Limiting for API endpoints (stricter)
|
||||
api-rate-limit:
|
||||
rateLimit:
|
||||
average: 30
|
||||
burst: 15
|
||||
period: 1s
|
||||
5
net/go.d/filecheck.conf
Normal file
5
net/go.d/filecheck.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
jobs:
|
||||
- name: restic_repository
|
||||
dirs:
|
||||
include:
|
||||
- '/mnt/hidrive/users/valknar/Backup'
|
||||
3
net/go.d/postgres.conf
Normal file
3
net/go.d/postgres.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
jobs:
|
||||
- name: docker_core_postgres
|
||||
dsn: 'postgres://netdata:netdata_monitor_password@172.18.0.5:5432/postgres'
|
||||
47
net/health_alarm_notify.conf
Normal file
47
net/health_alarm_notify.conf
Normal file
@@ -0,0 +1,47 @@
|
||||
# Netdata health alarm notification configuration
|
||||
# This file configures where to send alarm notifications
|
||||
|
||||
# Enable/disable sending email notifications
|
||||
SEND_EMAIL="YES"
|
||||
|
||||
# Recipient email address for all alarms
|
||||
DEFAULT_RECIPIENT_EMAIL="${ADMIN_EMAIL}"
|
||||
|
||||
# Email sender address
|
||||
EMAIL_SENDER="${EMAIL_FROM}"
|
||||
|
||||
# SMTP configuration
|
||||
SENDMAIL=""
|
||||
EMAIL_SENDER="${EMAIL_FROM}"
|
||||
|
||||
# Custom send email command using msmtp
|
||||
EMAIL_COMMAND="msmtp -t"
|
||||
|
||||
# Enable specific notification types
|
||||
role_recipients_email[sysadmin]="${ADMIN_EMAIL}"
|
||||
role_recipients_email[domainadmin]="${ADMIN_EMAIL}"
|
||||
role_recipients_email[dba]="${ADMIN_EMAIL}"
|
||||
role_recipients_email[webmaster]="${ADMIN_EMAIL}"
|
||||
role_recipients_email[proxyadmin]="${ADMIN_EMAIL}"
|
||||
role_recipients_email[sitemgr]="${ADMIN_EMAIL}"
|
||||
|
||||
###############################################################################
|
||||
# Mattermost notifications via Slack-compatible webhook
|
||||
###############################################################################
|
||||
|
||||
# Enable Slack notifications (Mattermost supports Slack-compatible webhooks)
|
||||
SEND_SLACK="YES"
|
||||
|
||||
# Mattermost incoming webhook URL (Slack-compatible)
|
||||
SLACK_WEBHOOK_URL="${MATTERMOST_WEBHOOK_URL}"
|
||||
|
||||
# Slack channel (optional, webhook default channel will be used if empty)
|
||||
DEFAULT_RECIPIENT_SLACK=""
|
||||
|
||||
# Slack notification recipients per role
|
||||
role_recipients_slack[sysadmin]="notifications"
|
||||
role_recipients_slack[domainadmin]="notifications"
|
||||
role_recipients_slack[dba]="notifications"
|
||||
role_recipients_slack[webmaster]="notifications"
|
||||
role_recipients_slack[proxyadmin]="notifications"
|
||||
role_recipients_slack[sitemgr]="notifications"
|
||||
20
net/msmtprc
Normal file
20
net/msmtprc
Normal file
@@ -0,0 +1,20 @@
|
||||
# MSMTP configuration for Netdata email alerts
|
||||
|
||||
# Set default values for all accounts
|
||||
defaults
|
||||
auth on
|
||||
tls on
|
||||
tls_trust_file /etc/ssl/certs/ca-certificates.crt
|
||||
logfile /var/log/msmtp.log
|
||||
|
||||
# IONOS SMTP account
|
||||
account ionos
|
||||
host smtp.ionos.de
|
||||
port 465
|
||||
tls_starttls off
|
||||
from hi@pivoine.art
|
||||
user hi@pivoine.art
|
||||
password jaquoment
|
||||
|
||||
# Set default account
|
||||
account default : ionos
|
||||
Reference in New Issue
Block a user