unifi: Deploy Unifi controller

The Unifi controller consists of three containerized processes:

* Unifi Network itself
* unifi_exporter for monitoring and metrics via Prometheus
* Caddy for HTTPS

_unifi_exporter_ is really the only component with any configuration.
Unfortunately, it mixes secret and non-secret data in a single YAML
file, which makes it impossible to use `yaml.Marshal` to render the
configuration directly from the CUE source.
master
Dustin 2024-05-25 12:55:51 -05:00
parent ad6f8723da
commit e75204be53
12 changed files with 229 additions and 0 deletions

61
app/unifi/templates.cue Normal file
View File

@ -0,0 +1,61 @@
package unifi
import "du5t1n.me/cfg/base/schema/instructions"
import "du5t1n.me/cfg/app/caddy"
templates: [...instructions.#RenderInstruction] & [
{
template: "unifi/unifi.sysusers"
dest: "/etc/sysusers.d/unifi.conf"
hooks: changed: [
{
run: "systemd-sysusers /etc/sysusers.d/unifi.conf"
immediate: true
},
]
},
{
template: "unifi/unifi_exporter.yml"
dest: "/etc/unifi_exporter.yml"
hooks: {
changed: [{run: "systemctl restart unifi_exporter"}]
}
},
{
template: "unifi/unifi.pod"
dest: "/etc/containers/systemd/unifi.pod"
hooks: {
changed: [
{run: "systemctl daemon-reload", immediate: true},
]
}
},
{
template: "unifi/unifi.container"
dest: "/etc/containers/systemd/unifi.container"
hooks: {
changed: [
{run: "systemctl daemon-reload", immediate: true},
{run: "systemctl restart unifi"},
]
}
},
{
template: "unifi/unifi_exporter.container"
dest: "/etc/containers/systemd/unifi_exporter.container"
hooks: {
changed: [
{run: "systemctl daemon-reload", immediate: true},
{run: "systemctl restart unifi_exporter"},
]
}
},
{
template: "unifi/Caddyfile"
dest: "/etc/caddy/Caddyfile"
hooks: {
changed: [{run: "systemctl restart caddy"}]
}
},
]+caddy.templates

29
env/prod/unifi.cue vendored Normal file
View File

@ -0,0 +1,29 @@
package prod
unifi: caddy: {
server_name: "unifi.pyrocufflink.blue"
acme: email: "unifi@pyrocufflink.blue"
}
unifi: exporter: {
listen: {
address: ":9130"
metricspath: "/metrics"
}
unifi: {
address: "https://localhost:8443/"
insecure: true
password: """
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvRVBncFB6YnJsYmlQSnJN
YXVqclJVTGV4eTRkd3JWbFhZOXN4WWJYc1NVCnlXdElESWJNdVcxRnZodnVUTlF5
d2ovRjNNeE9jV25IOW01MERIM1ZKZnMKLS0tIEFneVBWdHVobzZBTlBvZjZaMHJp
T3A4WTRYaDd1RFduVVBnQ3hXSkE0WmcKEFBdNfdUTZSo7ebqcIcl9qckp/zc0Mf6
LV9pZz8v3n9NO9fnF/vzXJrGyaJlzv3H
-----END AGE ENCRYPTED FILE-----
"""
site: "Pyrocufflink"
timeout: "5s"
username: "prometheus"
}
}

View File

@ -0,0 +1,16 @@
package unifi2
import (
"du5t1n.me/cfg/env/prod"
)
ssh: prod.ssh
sudo: prod.sudo
promtail: prod.#promtail
collectd: prod.collectd
unifi: prod.unifi
caddy: prod.caddy

View File

@ -0,0 +1,3 @@
. scripts/no-coreos-default-sudo.sh
systemctl start unifi

View File

@ -0,0 +1,5 @@
#!/bin/sh
. scripts/pam-ssh-agent-auth.sh
install_packages

View File

@ -0,0 +1,17 @@
package unifi2
import (
"list"
"du5t1n.me/cfg/app/collectd"
"du5t1n.me/cfg/app/promtail"
"du5t1n.me/cfg/app/unifi"
"du5t1n.me/cfg/env/prod"
)
render: list.Concat([
prod.templates,
collectd.templates,
promtail.templates,
unifi.templates,
])

13
templates/unifi/Caddyfile Normal file
View File

@ -0,0 +1,13 @@
{# vim: set ft=jinja : -#}
{{ unifi.caddy.server_name }} {
reverse_proxy https://localhost:8443 {
transport http {
tls_insecure_skip_verify
}
}
tls {{ unifi.caddy.acme.email }} {
ca {{ caddy.acme.url }}
ca_root /etc/caddy/acme-ca.crt
}
}

View File

@ -0,0 +1,32 @@
[Unit]
Description=Unifi Network
Wants=network.target
After=network.target
[Container]
Image=lscr.io/linuxserver/unifi-controller
Volume=/var/lib/unifi:/config:rw,Z
NoNewPrivileges=yes
UserNS=auto:gidmapping=911:911:1,uidmapping=911:911:1
VolatileTmp=yes
Notify=yes
Pod=unifi.pod
[Service]
StateDirectory=unifi
TimeoutStartSec=5min
Restart=always
PrivateTmp=yes
ProtectClock=yes
ProtectHome=yes
ProtectKernelModules=yes
ProtectProc=invisible
ProtectSystem=strict
ReadWritePaths=/run
ReadWritePaths=/var/lib/containers/storage
ReadWritePaths=/var/lib/unifi
RestrictRealtime=yes
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,9 @@
{# vim: set ft=systemd.jinja : -#}
[Pod]
PodName=unifi
PublishPort=6789:6789
PublishPort=8080:8080
PublishPort=8443:8443
PublishPort=8843:8843
PublishPort=8880:8880
PublishPort=9130:9130

View File

@ -0,0 +1,2 @@
g unifi 911
u unifi 911:911 "Unifi" /var/lib/unifi /sbin/nologin

View File

@ -0,0 +1,31 @@
[Unit]
Description=Prometheus metrics exporter for Unifi Controller
Wants=unifi.service
After=unifi.service
[Container]
Image=docker.io/jessestuart/unifi_exporter:v0.4.0
Volume=/etc/unifi_exporter.yml:/etc/unifi_exporter.yml:ro
NoNewPrivileges=yes
Exec=-config.file /etc/unifi_exporter.yml
User=200
Group=200
VolatileTmp=yes
Pod=unifi.pod
[Service]
Restart=always
RestartSec=2
PrivateTmp=yes
ProtectClock=yes
ProtectHome=yes
ProtectKernelModules=yes
ProtectProc=invisible
ProtectSystem=strict
ReadWritePaths=/run
ReadWritePaths=/var/lib/containers/storage
RestrictRealtime=yes
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,11 @@
{#- vim: set ft=yaml.jinja : -#}
listen:
address: {{ unifi.exporter.listen.address }}
metricspath: {{ unifi.exporter.listen.metricspath }}
unifi:
address: {{ unifi.exporter.unifi.address }}
insecure: {{ unifi.exporter.unifi.insecure }}
password: {{ unifi.exporter.unifi.password | decrypt }}
site: {{ unifi.exporter.unifi.site }}
timeout: {{ unifi.exporter.unifi.timeout }}
username: {{ unifi.exporter.unifi.username }}