unifi/example/unifi.nomad.hcl

442 lines
9.6 KiB
HCL

job "unifi" {
datacenters = ["dc1"]
region = "global"
group "unifi" {
network {
mode = "bridge"
port "stun" {
to = 3478
}
}
service {
name = "unifi"
port = 8888
connect {
sidecar_service {
}
sidecar_task {
config {
args = [
"-c",
"${NOMAD_SECRETS_DIR}/envoy_bootstrap.json",
"-l",
"${meta.connect.log_level}",
"--concurrency",
"${meta.connect.proxy_concurrency}",
"--disable-hot-restart"
]
}
resources {
cpu = 50
memory = 64
}
}
}
check {
type = "http"
path = "/status"
expose = true
interval = "10s"
timeout = "5s"
check_restart {
limit = 12
grace = "2m"
}
}
tags = [
"traefik.enable=true",
"traefik.http.routers.unifi.entrypoints=https",
"traefik.http.routers.unifi.rule=Host(`unifi.example.org`)",
"traefik.http.middlewares.csp-unifi.headers.contentsecuritypolicy=default-src 'self';font-src 'self' data:;img-src 'self' data:;script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';",
"traefik.http.middlewares.unifi-inflight.inflightreq.amount=300",
"traefik.http.middlewares.unifi-rate-limit.ratelimit.average=100",
"traefik.http.middlewares.unifi-rate-limit.ratelimit.burst=200",
"traefik.http.routers.unifi.middlewares=security-headers@file,unifi-rate-limit,forward-proto@file,inflight-std@file,unifi-inflight,hsts@file,compression@file,csp-unifi",
"traefik.enable=true",
"traefik.http.routers.unifi-inform.entrypoints=unifi-inform",
"traefik.http.routers.unifi-inform.rule=(Path(`/inform`) && Method(`POST`)) || (PathPrefix(`/dl/firmware-cached`) && (Method(`GET`) || Method(`HEAD`)))",
"traefik.http.routers.unifi-inform.middlewares=rate-limit-std@file,inflight-std@file",
"traefik.enable=true",
"traefik.http.routers.unifi-portal.entrypoints=unifi-portal",
"traefik.http.routers.unifi-portal.rule=Host(`unifi-portal.example.org`) && PathPrefix(`/guest`)",
"traefik.http.middlewares.csp-unifi-portal.headers.contentsecuritypolicy=default-src 'self';font-src 'self' data:;img-src 'self' data:;script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';",
"traefik.http.routers.unifi-portal.middlewares=security-headers@file,rate-limit-std@file,forward-proto@file,inflight-std@file,hsts@file,compression@file,csp-unifi-portal",
]
}
service {
name = "unifi-stun"
port = "stun"
tags = [
"traefik.enable=true",
"traefik.consulcatalog.connect=false",
"traefik.udp.routers.unifi-stun.entrypoints=unifi-stun",
]
}
service {
name = "unifi-mongo"
port = 27017
check {
type = "script"
command = "sh"
args = ["-c", "mongo --quiet --eval 'db.runCommand(\"ping\").ok'"]
interval = "30s"
timeout = "5s"
task = "mongo"
check_restart {
limit = 4
grace = "3m"
}
}
}
volume "mongo" {
source = "unifi-mongo"
type = "csi"
access_mode = "single-node-writer"
attachment_mode = "file-system"
}
volume "data" {
source = "unifi-data"
type = "csi"
access_mode = "single-node-writer"
attachment_mode = "file-system"
}
# wait for required services tp be ready before starting the main task
task "wait-for" {
driver = "docker"
user = 1053
config {
image = "danielberteaud/wait-for:24.3-1"
readonly_rootfs = true
pids_limit = 20
}
lifecycle {
hook = "prestart"
}
env {
SERVICE_0 = "unifi-mongo.service.consul"
}
resources {
cpu = 10
memory = 10
memory_max = 30
}
}
task "nginx" {
driver = "docker"
user = 8306
lifecycle {
hook = "poststart"
sidecar = "true"
}
config {
image = "nginxinc/nginx-unprivileged:alpine"
volumes = ["local/nginx.conf:/etc/nginx/conf.d/default.conf"]
readonly_rootfs = true
pids_limit = 20
mount {
type = "tmpfs"
target = "/tmp"
tmpfs_options {
size = 3000000
}
}
}
template {
data = <<_EOF
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 127.0.0.1:8888;
server_name _;
server_tokens off;
root /usr/share/html;
proxy_set_header Origin "";
proxy_set_header Authorization "";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_socket_keepalive on;
client_max_body_size 100m;
set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# Inform endpoint
location ~ ^/(inform|dl/firmware-cached).* {
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 405;
}
proxy_pass http://localhost:8080;
}
# Guest portal
location /guest/ {
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 405;
}
proxy_pass https://localhost:8843;
}
# Main console
location / {
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE)$ ) {
return 405;
}
proxy_pass https://localhost:8443;
}
}
_EOF
destination = "local/nginx.conf"
}
resources {
cpu = 10
memory = 15
}
}
task "controller" {
leader = true
driver = "docker"
config {
image = "danielberteaud/unifi:8.1.113-1"
volumes = [
"local/init-system.properties.sh:/entrypoint.d/10-init-system.properties.sh"
]
readonly_rootfs = true
pids_limit = 200
mount {
type = "tmpfs"
target = "/opt/unifi/run"
tmpfs_options {
size = 3000000
}
}
mount {
type = "tmpfs"
target = "/tmp"
tmpfs_options {
size = 3000000
}
}
}
vault {
policies = ["unifi"]
env = false
disable_file = true
change_mode = "noop"
}
env {
TMPDIR = "/local/tmp"
}
# Use a template block instead of env {} so we can fetch values from vault
template {
data = <<_EOT
LANG=fr_FR.utf8
TZ=Europe/Paris
_EOT
destination = "secrets/.env"
perms = 400
env = true
}
template {
data = <<_EOF
unifi.http.port=8080
unifi.https.port=8443
portal.http.port=8880
portal.https.port=8843
unifi.stun.port={{ env "NOMAD_PORT_stun" }}
unifi.db.nojournal=true
db.mongo.local=false
db.mongo.uri=mongodb://127.0.0.1:27017/unifi?
statdb.mongo.uri=mongodb://127.0.0.1:27017/unifi_stats?
debug.device=info
debug.mgmt=info
debug.system=info
debug.sdn=warn
autobackup.dir=/data/backup
_EOF
destination = "secrets/system.properties"
}
template {
data = <<_EOF
#!/bin/sh
# vim: syntax=sh
set -euo pipefail
mkdir -p /data/unifi
mkdir -p /data/logs
if [ \! -f "/opt/unifi/data/system.properties" ]; then
echo "System initialization, copy the default system.properties"
cp /secrets/system.properties /opt/unifi/data/system.properties
else
for PROP in $(grep -vE '^(\s*$|#)' /secrets/system.properties); do
KEY=$(echo ${PROP} | cut -d= -f1)
VALUE=$(echo ${PROP} | cut -d= -f2)
if grep -q -E "^${KEY}=" /opt/unifi/data/system.properties; then
if grep -q -E "^${PROP}" /opt/unifi/data/system.properties; then
echo "${PROP} already set in system.properties"
else
echo "Updating ${PROP} in system.properties"
sed -i -E "s|^${KEY}=.*|${PROP}|" /opt/unifi/data/system.properties
fi
else
echo "Adding ${PROP} in system.properties"
echo ${PROP} >> /opt/unifi/data/system.properties
fi
done
fi
_EOF
destination = "local/init-system.properties.sh"
perms = "755"
}
volume_mount {
volume = "data"
destination = "/data"
}
resources {
cpu = 200
memory = 1024
}
}
task "mongo" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = true
}
config {
image = "danielberteaud/mongo:5.0.24.3-1"
command = "mongod"
readonly_rootfs = true
pids_limit = 200
args = ["--config", "/local/mongod.conf"]
mount {
type = "tmpfs"
target = "/tmp"
tmpfs_options {
size = 3000000
}
}
}
template {
data = <<_EOF
net:
bindIp: 127.0.0.1
storage:
dbPath: /data/db
directoryPerDB: true
wiredTiger:
engineConfig:
directoryForIndexes: true
journalCompressor: snappy
collectionConfig:
blockCompressor: snappy
_EOF
destination = "local/mongod.conf"
}
volume_mount {
volume = "mongo"
destination = "/data/db"
}
resources {
cpu = 100
memory = 256
}
}
}
}