Add rendered example
This commit is contained in:
parent
fa90b04da7
commit
a88e2188cf
|
@ -0,0 +1,3 @@
|
|||
Kind = "service-defaults"
|
||||
Name = "unifi"
|
||||
Protocol = "http"
|
|
@ -0,0 +1,16 @@
|
|||
Kind = "service-intentions"
|
||||
Name = "unifi"
|
||||
Sources = [
|
||||
{
|
||||
Name = "traefik"
|
||||
Permissions = [
|
||||
{
|
||||
Action = "allow"
|
||||
HTTP {
|
||||
PathPrefix = "/"
|
||||
Methods = ["GET","HEAD","POST","PUT","DELETE"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,16 @@
|
|||
FROM danielberteaud/alpine:24.1-1
|
||||
MAINTAINER Daniel Berteaud <dbd@ehtrace.com>
|
||||
|
||||
ENV UBNT_UPDATE_API="https://fw-update.ubnt.com/api/firmware-latest?filter=eq~~product~~unifi-firmware&filter=eq~~channel~~release" \
|
||||
UBNT_FIRMWARE_DIR="/opt/unifi/app/data/firmware/" \
|
||||
UBNT_PLATFORMS=
|
||||
|
||||
RUN set -eux &&\
|
||||
apk --no-cache upgrade &&\
|
||||
apk --no-cache add perl-libwww \
|
||||
perl-lwp-protocol-https \
|
||||
perl-json \
|
||||
supercronic
|
||||
|
||||
COPY root/ /
|
||||
CMD ["ubnt-firmware-downloader.sh"]
|
|
@ -0,0 +1,141 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use JSON;
|
||||
use LWP::UserAgent;
|
||||
use File::Basename;
|
||||
use File::Path qw(make_path);
|
||||
use Digest::SHA;
|
||||
use Data::Dumper;
|
||||
|
||||
my $ubnt_api = $ENV{UBNT_UPDATE_API} || 'https://fw-update.ubnt.com/api/firmware-latest?filter=eq~~product~~unifi-firmware&filter=eq~~channel~~release';
|
||||
my $fw_dir = $ENV{UBNT_FIRMWARE_DIR} || '/opt/unifi/app/data/firmware/';
|
||||
my @dl_platform = split(/,/, $ENV{UBNT_PLATFORMS} || "");
|
||||
my $fw_cached = {};
|
||||
my $fw_entries = {};
|
||||
my $fw_devices = {};
|
||||
my $fw_latest = {};
|
||||
|
||||
# Ensure the list is empty if UBNT_PLATFORMS was empty
|
||||
if (scalar @dl_platform > 0 and $dl_platform[0] eq "") {
|
||||
@dl_platform = ();
|
||||
}
|
||||
|
||||
if (scalar @dl_platform > 0){
|
||||
print STDERR "Will only try to download firmware for platforms " . join(",", @dl_platform) . "\n";
|
||||
}
|
||||
|
||||
# If the firmware_meta.json file exists, open it and read
|
||||
# already present firmwares
|
||||
if (-e "$fw_dir/firmware_meta.json"){
|
||||
open CACHED_FIRMWARES, "<$fw_dir/firmware_meta.json";
|
||||
my $data = <CACHED_FIRMWARES>;
|
||||
my $json = eval {
|
||||
from_json($data);
|
||||
};
|
||||
if (defined $json and defined $json->{cached_firmwares}){
|
||||
foreach my $fw (@{$json->{cached_firmwares}}){
|
||||
print STDERR "file $fw->{path} found\n";
|
||||
if (-e "$fw_dir/$fw->{path}"){
|
||||
$fw_entries->{$fw->{md5}} = $fw;
|
||||
}
|
||||
}
|
||||
}
|
||||
close CACHED_FIRMWARES;
|
||||
}
|
||||
|
||||
my $ua = LWP::UserAgent->new(timeout => 10);
|
||||
$ua->env_proxy;
|
||||
|
||||
# Ask ubnt API for the list of firmwares
|
||||
my $resp = $ua->get($ubnt_api);
|
||||
|
||||
if (not $resp->is_success){
|
||||
die "Couldn't fetch $ubnt_api : " . $resp->status_line . "\n";
|
||||
}
|
||||
|
||||
my $firmwares = from_json($resp->decoded_content);
|
||||
|
||||
FIRMWARE: foreach my $fw (@{$firmwares->{_embedded}->{firmware}}){
|
||||
my $version = $fw->{version};
|
||||
$version =~ s/^v//;
|
||||
$version =~ s/\+/./g;
|
||||
my $file = basename($fw->{_links}->{data}->{href});
|
||||
my $fw_path = "$fw_dir/$fw->{platform}/$version/$file";
|
||||
my $sha256;
|
||||
|
||||
# If the firmware is already cached, we need to check its checksum
|
||||
if (-e "$fw_path"){
|
||||
print STDERR "Found firmware for $fw->{platform} version $version, checking checksum of $fw_path\n";
|
||||
$sha256 = Digest::SHA->new("sha256")->addfile($fw_path)->hexdigest;
|
||||
|
||||
if ($sha256 eq $fw->{sha256_checksum}){
|
||||
print STDERR "Checksum matched, firmware already cached\n";
|
||||
|
||||
$fw_entries->{$fw->{md5}} = {
|
||||
md5 => $fw->{md5},
|
||||
version => $version,
|
||||
size => $fw->{file_size},
|
||||
path => "$fw->{platform}/$file"
|
||||
};
|
||||
|
||||
push @{$fw_devices->{$fw->{md5}}}, $fw->{platform};
|
||||
$fw_latest->{$fw->{md5}} = 1;
|
||||
|
||||
# File is OK, just continue with the next firmware
|
||||
next FIRMWARE;
|
||||
} else {
|
||||
print STDERR "Checksum mismatch: got $sha256 while expecting $fw->{sha256_checksum}, downloading again\n";
|
||||
}
|
||||
}
|
||||
|
||||
# If we restrict the list of platform to download firmwares for
|
||||
# check the current firmware matches
|
||||
if (scalar @dl_platform > 0 and not grep { $_ eq $fw->{platform} } @dl_platform){
|
||||
print STDERR "Platform $fw->{platform} is not in the list of downloads, skipping download\n";
|
||||
} else {
|
||||
print STDERR "Downloading firmware from $fw->{_links}->{data}->{href}\n";
|
||||
make_path "$fw_dir/$fw->{platform}/$version/";
|
||||
#$resp = getstore($fw->{_links}->{data}->{href}, $fw_path);
|
||||
$resp = $ua->get($fw->{_links}->{data}->{href}, ":content_file" => $fw_path);
|
||||
if (not $resp->is_success){
|
||||
print STDERR "Error downloading $fw->{_links}->{data}->{href} : " . $resp->status_line ."\n";
|
||||
next FIRMWARE;
|
||||
}
|
||||
|
||||
$sha256 = Digest::SHA->new("sha256")->addfile($fw_path)->hexdigest;
|
||||
if ($sha256 ne $fw->{sha256_checksum}){
|
||||
print STDERR "Checksum mismatch : got $sha256 while expecting $fw->{sha256_checksum}\n";
|
||||
next FIRMWARE;
|
||||
} else {
|
||||
print STDERR "Checksum correctly verified\n";
|
||||
push @{$fw_devices->{$fw->{md5}}}, $fw->{platform};
|
||||
$fw_latest->{$fw->{md5}} = 1;
|
||||
$fw_entries->{$fw->{md5}} = {
|
||||
md5 => $fw->{md5},
|
||||
version => $version,
|
||||
size => $fw->{file_size},
|
||||
path => "$fw->{platform}/$file"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
# In anycase, push this platform to the fw_devices hash
|
||||
# so the list of devices will be complete
|
||||
push @{$fw_devices->{$fw->{md5}}}, $fw->{platform};
|
||||
}
|
||||
|
||||
print STDERR "Finished downloading firmwares, now building the firmware_meta.json file\n";
|
||||
foreach my $fw (sort { $fw_entries->{$a}->{version} cmp $fw_entries->{$b}->{version} } keys %{$fw_entries}){
|
||||
my $firmware = $fw_entries->{$fw};
|
||||
# Only override device list for latest firmwares (those returned by the ubnt API)
|
||||
# for the previous one, we do not have fresh info, so just trust what was in the firmware_meta.json file
|
||||
if (defined $fw_latest->{$fw_entries->{$fw}->{md5}}){
|
||||
$firmware->{devices} = $fw_devices->{$fw};
|
||||
}
|
||||
push @{$fw_cached->{cached_firmwares}}, $firmware;
|
||||
}
|
||||
|
||||
open CACHED_FIRMWARES, ">$fw_dir/firmware_meta.json";
|
||||
print CACHED_FIRMWARES to_json($fw_cached);
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
/usr/local/bin/ubnt-firmware-downloader
|
||||
# If a cron expression is defined, run a cron daemon
|
||||
if [ -n "${UBNT_CRON}" ]; then
|
||||
echo "Running using cron with expression ${UBNT_CRON}"
|
||||
cat <<_EOF > /tmp/crontab
|
||||
${UBNT_CRON} /usr/local/bin/ubnt-firmware-downloader
|
||||
_EOF
|
||||
supercronic /tmp/crontab
|
||||
fi
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
FROM danielberteaud/java:17.24.1-1 AS builder
|
||||
|
||||
ARG UNIFI_VERSION=8.0.26
|
||||
|
||||
RUN set -euxo pipefail &&\
|
||||
apk --no-cache add curl ca-certificates unzip &&\
|
||||
cd /tmp &&\
|
||||
curl -sSLO https://www.ubnt.com/downloads/unifi/${UNIFI_VERSION}/UniFi.unix.zip &&\
|
||||
unzip UniFi.unix.zip &&\
|
||||
rm -f UniFi.unix.zip &&\
|
||||
ls -l &&\
|
||||
rm -f UniFi/bin/mongod &&\
|
||||
chown -R root:root UniFi
|
||||
|
||||
FROM danielberteaud/java:17.24.1-1
|
||||
MAINTAINER Daniel Berteaud <dbd@ehtrace.com>
|
||||
|
||||
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" \
|
||||
TZ=Europe/Paris \
|
||||
LANG=fr_FR.utf8
|
||||
|
||||
COPY --from=builder /tmp/UniFi /opt/unifi
|
||||
COPY root/ /
|
||||
RUN set -euxo pipefail &&\
|
||||
apk --no-cache upgrade &&\
|
||||
addgroup -g 8443 unifi &&\
|
||||
adduser --system --ingroup unifi --disabled-password --uid 8443 --home /opt/unifi --shell /sbin/nologin unifi &&\
|
||||
mkdir -p /data/unifi &&\
|
||||
mkdir -p /data/logs &&\
|
||||
chown unifi:unifi /data &&\
|
||||
chmod 700 /data &&\
|
||||
ln -s /data/unifi /opt/unifi/data &&\
|
||||
ln -s /data/logs /opt/unifi/logs
|
||||
|
||||
EXPOSE 8443 8080 8843 3778
|
||||
USER unifi
|
||||
VOLUME /data
|
||||
WORKDIR /opt/unifi
|
||||
|
||||
CMD ["sh", "-c", "java ${JAVA_OPTS} -jar /opt/unifi/lib/ace.jar start"]
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight([%-5level]) %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
</configuration>
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
|
||||
if [ "unifi" != "unifi" ]; then
|
||||
for DIR in vault consul nomad; do
|
||||
if [ -d output/${DIR} ]; then
|
||||
for FILE in $(find output/${DIR} -name "*unifi*.hcl" -type f); do
|
||||
NEW_FILE=$(echo "${FILE}" | sed -E "s/unifi/unifi/g")
|
||||
mv "${FILE}" "${NEW_FILE}"
|
||||
done
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
job "unifi" {
|
||||
datacenters = ["dc1"]
|
||||
|
||||
|
||||
group "unifi" {
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
port "stun" {
|
||||
to = 3478
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
name = "unifi"
|
||||
port = 8888
|
||||
|
||||
connect {
|
||||
sidecar_service {
|
||||
}
|
||||
sidecar_task {
|
||||
resources {
|
||||
cpu = 50
|
||||
memory = 64
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
|
||||
# Note : no Host as inform requests are sent without. But it's binded to the dedicated entrypoint anyway
|
||||
"traefik.http.routers.unifi-inform.rule=(Path(`/inform`) && Method(`POST`)) || (PathPrefix(`/dl/firmware-cached`) && (Method(`GET`) || Method(`HEAD`)))",
|
||||
"traefik.http.routers.unifi-inform.entrypoints=unifi-inform",
|
||||
"traefik.http.routers.unifi-inform.middlewares=rate-limit-std@file,inflight-std@file",
|
||||
|
||||
"traefik.http.routers.unifi-controller.rule=Host(`unifi.example.org`)",
|
||||
"traefik.http.routers.unifi-controller.entrypoints=https",
|
||||
"traefik.http.routers.unifi-controller.tls=true",
|
||||
"traefik.http.routers.unifi-controller.middlewares=rate-limit-std@file,security-headers@file,compression@file,csp-relaxed@file",
|
||||
|
||||
"traefik.http.routers.unifi-portal.rule=Host(`unifi-portal.example.org`) && PathPrefix(`/guest`)",
|
||||
"traefik.http.routers.unifi-portal.entrypoints=unifi-portal",
|
||||
"traefik.http.routers.unifi-portal.tls=true",
|
||||
"traefik.http.routers.unifi-portal.middlewares=rate-limit-std@file,inflight-std@file,security-headers@file,hsts@file,compression@file,csp-relaxed@file"
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
service {
|
||||
name = "unifi-stun"
|
||||
port = "stun"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.udp.routers.unifi-stun.entrypoints=unifi-stun",
|
||||
"traefik.consulcatalog.connect=false"
|
||||
]
|
||||
}
|
||||
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.1-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"]
|
||||
}
|
||||
|
||||
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.0.26-1"
|
||||
volumes = [
|
||||
"local/init-system.properties.sh:/entrypoint.d/10-init-system.properties.sh"
|
||||
]
|
||||
|
||||
mount {
|
||||
type = "tmpfs"
|
||||
target = "/opt/unifi/run"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
vault {
|
||||
policies = ["unifi"]
|
||||
env = false
|
||||
disable_file = true
|
||||
}
|
||||
|
||||
env {
|
||||
|
||||
|
||||
|
||||
LANG = "fr_FR.utf8"
|
||||
TZ = "Europe/Paris"
|
||||
|
||||
}
|
||||
|
||||
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.1-1"
|
||||
command = "mongod"
|
||||
args = [
|
||||
"--config",
|
||||
"/local/mongod.conf"
|
||||
]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
path "kv/data/service/unifi" {
|
||||
capabilities = ["read"]
|
||||
}
|
Loading…
Reference in New Issue