diff --git a/consul/config/service-defaults/unifi.hcl b/consul/config/service-defaults/unifi.hcl index 587d5ee..f3ad32f 100644 --- a/consul/config/service-defaults/unifi.hcl +++ b/consul/config/service-defaults/unifi.hcl @@ -1,3 +1,3 @@ Kind = "service-defaults" -Name = "[[ .unifi.instance ]][[ .consul.suffix ]]" +Name = "[[ .instance ]][[ .consul.suffix ]]" Protocol = "http" diff --git a/consul/config/service-intentions/unifi.hcl b/consul/config/service-intentions/unifi.hcl index 52381e1..17e983d 100644 --- a/consul/config/service-intentions/unifi.hcl +++ b/consul/config/service-intentions/unifi.hcl @@ -1,8 +1,8 @@ Kind = "service-intentions" -Name = "[[ .unifi.instance ]][[ .consul.suffix ]]" +Name = "[[ .instance ]][[ .consul.suffix ]]" Sources = [ { - Name = "[[ (merge .unifi.controller .traefik).instance ]]" + Name = "[[ (merge .unifi.controller .).traefik.instance ]]" Permissions = [ { Action = "allow" diff --git a/example/images/ubnt-firmware-downloader/Dockerfile b/example/images/ubnt-firmware-downloader/Dockerfile index 7c80d49..1cf620b 100644 --- a/example/images/ubnt-firmware-downloader/Dockerfile +++ b/example/images/ubnt-firmware-downloader/Dockerfile @@ -1,4 +1,4 @@ -FROM danielberteaud/alpine:24.1-7 +FROM danielberteaud/alpine:24.1-8 MAINTAINER Daniel Berteaud ENV UBNT_UPDATE_API="https://fw-update.ubnt.com/api/firmware-latest?filter=eq~~product~~unifi-firmware&filter=eq~~channel~~release" \ diff --git a/example/images/unifi/Dockerfile b/example/images/unifi/Dockerfile index 0928117..0c6df88 100644 --- a/example/images/unifi/Dockerfile +++ b/example/images/unifi/Dockerfile @@ -1,4 +1,4 @@ -FROM danielberteaud/java:17.24.1-3 AS builder +FROM danielberteaud/java:17.24.1-4 AS builder ARG UNIFI_VERSION=8.0.26 @@ -12,7 +12,7 @@ RUN set -euxo pipefail &&\ rm -f UniFi/bin/mongod &&\ chown -R root:root UniFi -FROM danielberteaud/java:17.24.1-3 +FROM danielberteaud/java:17.24.1-4 MAINTAINER Daniel Berteaud ENV JAVA_OPTS="-Djava.awt.headless=true -Dlogback.configurationFile=/opt/unifi/logback.xml --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED" \ diff --git a/example/unifi.nomad.hcl b/example/unifi.nomad.hcl index 841e14f..3c720b9 100644 --- a/example/unifi.nomad.hcl +++ b/example/unifi.nomad.hcl @@ -19,6 +19,7 @@ job "unifi" { sidecar_service { } sidecar_task { + resources { cpu = 50 memory = 64 @@ -138,6 +139,7 @@ job "unifi" { task "nginx" { + driver = "docker" user = 8306 @@ -147,8 +149,17 @@ job "unifi" { } config { - image = "nginxinc/nginx-unprivileged:alpine" - volumes = ["local/nginx.conf:/etc/nginx/conf.d/default.conf"] + image = "nginxinc/nginx-unprivileged:alpine" + volumes = ["local/nginx.conf:/etc/nginx/conf.d/default.conf"] + readonly_rootfs = true + mount { + type = "tmpfs" + target = "/tmp" + tmpfs_options { + size = 1000000 + } + } + } template { @@ -207,6 +218,7 @@ _EOF destination = "local/nginx.conf" } + resources { cpu = 10 memory = 15 @@ -216,36 +228,60 @@ _EOF task "controller" { + + leader = true driver = "docker" config { - image = "danielberteaud/unifi:8.0.26-2" + image = "danielberteaud/unifi:8.0.26-3" volumes = [ "local/init-system.properties.sh:/entrypoint.d/10-init-system.properties.sh" ] + readonly_rootfs = true mount { type = "tmpfs" target = "/opt/unifi/run" + tmpfs_options { + size = 1000000 + } + } + + mount { + type = "tmpfs" + target = "/tmp" + tmpfs_options { + size = 1000000 + } } } + vault { policies = ["unifi"] env = false disable_file = true } + env { - - LANG = "fr_FR.utf8" - TZ = "Europe/Paris" - } + # 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 @@ -308,6 +344,7 @@ _EOF destination = "/data" } + resources { cpu = 200 memory = 1024 @@ -317,6 +354,7 @@ _EOF } task "mongo" { + driver = "docker" lifecycle { @@ -325,12 +363,18 @@ _EOF } config { - image = "danielberteaud/mongo:5.0.24.1-1" - command = "mongod" - args = [ - "--config", - "/local/mongod.conf" - ] + image = "danielberteaud/mongo:5.0.24.1-1" + command = "mongod" + readonly_rootfs = true + args = ["--config", "/local/mongod.conf"] + mount { + type = "tmpfs" + target = "/tmp" + tmpfs_options { + size = 1000000 + } + } + } template { @@ -357,6 +401,7 @@ _EOF destination = "/data/db" } + resources { cpu = 100 memory = 256 @@ -366,4 +411,3 @@ _EOF } } } - diff --git a/example/vault/policies/unifi.hcl b/example/vault/policies/unifi.hcl index 8e0c828..ad3e6cc 100644 --- a/example/vault/policies/unifi.hcl +++ b/example/vault/policies/unifi.hcl @@ -1,3 +1,5 @@ +# Access the vault KV (v2) store path "kv/data/service/unifi" { capabilities = ["read"] } + diff --git a/prep.d/mv_conf.sh b/prep.d/mv_conf.sh index 036c5fd..7ef4362 100755 --- a/prep.d/mv_conf.sh +++ b/prep.d/mv_conf.sh @@ -1 +1 @@ -[[ template "common/mv_conf.sh.tpl" dict "ctx" . "services" (dict "unifi" .unifi.instance) ]] +[[ template "common/mv_conf.sh.tpl" dict "ctx" . "services" (dict "unifi" .instance) ]] diff --git a/unifi.nomad.hcl b/unifi.nomad.hcl index b664e11..60ab839 100644 --- a/unifi.nomad.hcl +++ b/unifi.nomad.hcl @@ -1,10 +1,10 @@ -job [[ .unifi.instance | toJSON ]] { +job "[[ .instance ]]" { [[- template "common/job_start" . ]] group "unifi" { -[[- $c := merge .unifi.controller . ]] +[[- $c := merge .unifi.controller .unifi . ]] network { mode = "bridge" @@ -14,7 +14,7 @@ job [[ .unifi.instance | toJSON ]] { } service { - name = "[[ .unifi.instance ]][[ .consul.suffix ]]" + name = "[[ .instance ]][[ .consul.suffix ]]" port = 8888 [[ template "common/connect" $c ]] @@ -35,41 +35,41 @@ job [[ .unifi.instance | toJSON ]] { "[[ $c.traefik.instance ]].enable=true", # Note : no Host as inform requests are sent without. But it's binded to the dedicated entrypoint anyway - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-inform[[ .consul.suffix ]].rule=(Path(`/inform`) && Method(`POST`)) || (PathPrefix(`/dl/firmware-cached`) && (Method(`GET`) || Method(`HEAD`)))", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-inform[[ .consul.suffix ]].entrypoints=[[ join (merge .unifi.inform.traefik .traefik).entrypoints "," ]]", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-inform[[ .consul.suffix ]].middlewares=[[ template "common/traefik_middlewares.tpl" merge .unifi.inform.traefik .traefik ]]", + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-inform[[ .consul.suffix ]].rule=(Path(`/inform`) && Method(`POST`)) || (PathPrefix(`/dl/firmware-cached`) && (Method(`GET`) || Method(`HEAD`)))", + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-inform[[ .consul.suffix ]].entrypoints=[[ join (merge .unifi.inform.traefik .traefik).entrypoints "," ]]", + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-inform[[ .consul.suffix ]].middlewares=[[ template "common/traefik_middlewares.tpl" merge .unifi.inform.traefik .traefik ]]", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-controller[[ .consul.suffix ]].rule=Host(` + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-controller[[ .consul.suffix ]].rule=Host(` [[- (urlParse .unifi.controller.public_url).Hostname -]]`) [[- if ne "" (urlParse .unifi.controller.public_url).Path ]] && PathPrefix(`[[ (urlParse .unifi.controller.public_url).Path ]]`)[[ end ]]", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-controller[[ .consul.suffix ]].entrypoints=[[ join (merge .unifi.controller.traefik .traefik).entrypoints "," ]]", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-controller[[ .consul.suffix ]].tls=true", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-controller[[ .consul.suffix ]].middlewares=[[ template "common/traefik_middlewares.tpl" merge .unifi.controller.traefik .traefik ]]", + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-controller[[ .consul.suffix ]].entrypoints=[[ join (merge .unifi.controller.traefik .traefik).entrypoints "," ]]", + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-controller[[ .consul.suffix ]].tls=true", + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-controller[[ .consul.suffix ]].middlewares=[[ template "common/traefik_middlewares.tpl" merge .unifi.controller.traefik .traefik ]]", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-portal[[ .consul.suffix ]].rule=Host(` + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-portal[[ .consul.suffix ]].rule=Host(` [[- (urlParse .unifi.guest_portal.public_url).Hostname -]] `) && PathPrefix(`/guest`)", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-portal[[ .consul.suffix ]].entrypoints=[[ join (merge .unifi.guest_portal.traefik .traefik).entrypoints "," ]]", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-portal[[ .consul.suffix ]].tls=true", - "[[ $c.traefik.instance ]].http.routers.[[ .unifi.instance ]]-portal[[ .consul.suffix ]].middlewares=[[ template "common/traefik_middlewares.tpl" merge .unifi.guest_portal.traefik .traefik ]]" + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-portal[[ .consul.suffix ]].entrypoints=[[ join (merge .unifi.guest_portal.traefik .traefik).entrypoints "," ]]", + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-portal[[ .consul.suffix ]].tls=true", + "[[ $c.traefik.instance ]].http.routers.[[ .instance ]]-portal[[ .consul.suffix ]].middlewares=[[ template "common/traefik_middlewares.tpl" merge .unifi.guest_portal.traefik .traefik ]]" ] } service { - name = "[[ .unifi.instance ]]-stun[[ .consul.suffix ]]" + name = "[[ .instance ]]-stun[[ .consul.suffix ]]" port = "stun" tags = [ "[[ $c.traefik.instance ]].enable=true", - "[[ $c.traefik.instance ]].udp.routers.[[ .unifi.instance ]]-stun[[ .consul.suffix ]].entrypoints=[[ join .unifi.stun.traefik.entrypoints "," ]]", + "[[ $c.traefik.instance ]].udp.routers.[[ .instance ]]-stun[[ .consul.suffix ]].entrypoints=[[ join .unifi.stun.traefik.entrypoints "," ]]", "[[ $c.traefik.instance ]].consulcatalog.connect=false" ] } -[[- if not .unifi.controller.mongo.is_external ]] +[[- if not $c.mongo.is_external ]] service { - name = "[[ .unifi.instance ]]-mongo[[ .consul.suffix ]]" + name = "[[ .instance ]]-mongo[[ .consul.suffix ]]" port = 27017 check { @@ -91,13 +91,16 @@ job [[ .unifi.instance | toJSON ]] { [[- end ]] -[[ template "common/volumes" .unifi.controller.volumes ]] +[[ template "common/volumes" $c ]] [[ template "common/task.wait_for" $c ]] task "nginx" { - driver = [[ .nomad.driver | toJSON ]] - user = 8306 + +[[- $c := merge .unifi.nginx .unifi . ]] + + driver = "[[ .nomad.driver ]]" + user = 8306 lifecycle { hook = "poststart" @@ -105,8 +108,10 @@ job [[ .unifi.instance | toJSON ]] { } config { - image = [[ .unifi.nginx.image | toJSON ]] - volumes = ["local/nginx.conf:/etc/nginx/conf.d/default.conf"] + image = "[[ .unifi.nginx.image ]]" + volumes = ["local/nginx.conf:/etc/nginx/conf.d/default.conf"] + readonly_rootfs = true +[[ template "common/tmpfs" dict "size" "1000000" "target" "/tmp" ]] } template { @@ -116,33 +121,34 @@ _EOF destination = "local/nginx.conf" } -[[ template "common/resources" .unifi.nginx.resources ]] +[[ template "common/resources" $c ]] } task "controller" { +[[ $c := merge .unifi.controller .unifi . ]] + leader = true - driver = [[ .nomad.driver | toJSON ]] + driver = "[[ $c.nomad.driver ]]" config { - image = [[ .unifi.controller.image | toJSON ]] + image = "[[ $c.image ]]" volumes = [ "local/init-system.properties.sh:/entrypoint.d/10-init-system.properties.sh" ] -[[ template "common/tmpfs" "/opt/unifi/run" ]] + readonly_rootfs = true +[[ template "common/tmpfs" dict "size" "1000000" "target" "/opt/unifi/run" ]] +[[ template "common/tmpfs" dict "size" "1000000" "target" "/tmp" ]] } - vault { - policies = ["[[ .unifi.instance ]][[ .consul.suffix ]]"] - env = false - disable_file = true - } +[[ template "common/vault.policies" $c ]] env { [[ template "common/proxy_env" . ]] - [[ template "common/env" $c.env ]] } +[[ template "common/file_env" $c ]] + template { data =<<_EOF [[ template "unifi/controller/system.properties.tpl" . ]] @@ -163,14 +169,17 @@ _EOF destination = "/data" } - [[ template "common/resources" .unifi.controller.resources ]] +[[ template "common/resources" $c ]] } [[- if not .unifi.controller.mongo.is_external ]] task "mongo" { - driver = [[ .nomad.driver | toJSON ]] + +[[- $c := merge .unifi.mongo .unifi . ]] + + driver = "[[ $c.nomad.driver ]]" lifecycle { hook = "prestart" @@ -178,12 +187,11 @@ _EOF } config { - image = [[ .unifi.mongo.image | toJSON ]] - command = "mongod" - args = [ - "--config", - "/local/mongod.conf" - ] + image = "[[ $c.image ]]" + command = "mongod" + readonly_rootfs = true + args = ["--config", "/local/mongod.conf" ] +[[ template "common/tmpfs" dict "size" "1000000" "target" "/tmp" ]] } template { @@ -198,10 +206,9 @@ _EOF destination = "/data/db" } -[[ template "common/resources" .unifi.mongo.resources ]] +[[ template "common/resources" $c ]] } } [[- end ]] } - diff --git a/variables.yml b/variables.yml index 3969c84..f032693 100644 --- a/variables.yml +++ b/variables.yml @@ -6,9 +6,10 @@ # - A mongodb server (optional, can use an external mongodb) # - A firmware downloader helper : this is because the Unifi Controller can't use an outbound web proxy +# The name of the nomad job +instance: unifi + unifi: - # The name of the nomad job - instance: unifi # This is for the web management console controller: @@ -17,7 +18,11 @@ unifi: driver: docker # The image for the controller - image: '[[ .docker.repo ]]unifi:8.0.26-2' + image: '[[ .docker.repo ]]unifi:8.0.26-3' + + vault: + policies: + - '[[ .instance ]][[ .consul.suffix ]]' # Resource allocation for the controller resources: diff --git a/vault/policies/unifi.hcl b/vault/policies/unifi.hcl index 379aa09..c73660b 100644 --- a/vault/policies/unifi.hcl +++ b/vault/policies/unifi.hcl @@ -1,3 +1 @@ -path "[[ .vault.prefix ]]kv/data/service/[[ .unifi.instance ]]" { - capabilities = ["read"] -} +[[- template "common/vault.kv_policy" . ]]