mirror of
https://scm.tikali.ai/tikali/applications/monky/monky-deployd.git
synced 2026-09-18 04:36:15 +00:00
feat: monky-deployd v0.1.0 — pull agent over the mesh (ADR-0028)
Stdlib-only Python 3.12 agent for docker VMs and laptops: flock → checkin
(bearer = the agent's OpenBao token, bootstrapped from the install kit's
jwt-tenancy deploy grant) → action apply|none|down → bundle (sha256
verified) → refusal checks (unresolved ${VAR} names only, manifest paths
pinned to monky/data/<env>/see/, privileged/host-network, rollback, disk
need×1.5+headroom) → lease → POST /v1/auth/jwt-tenancy/login → KV reads →
.env 0600 → promote → compose pull/up → wait healthy → report; finally
renew-self / re-lease before max TTL, scrub. Exit 0/75/78/1. Redactor log
filter. Transports sdk (openziti) / proxy (ziti tunnel proxy 18443/18200) /
system. Laptop mode.
Packaging: hardened oneshot + 60 s timer + proxy unit, nfpm .deb with
/opt/monky-deployd/venv, install.sh for Ubuntu 26.04 (Gitea release
download, enrol, ACLs, bootstrap from stdin), ansible role skeleton for
osg1-07. CI: lint/test on every change; wheel (openziti on ubuntu:26.04) and
package (nfpm) allow_failure until runner egress is proven; GitLab release +
release:gitea on v* tags. Docs: README, PROTOCOL, OPERATIONS, CHANGELOG,
CLAUDE/AGENTS.
Divergence noted: monky-tenancy main (MR !15) still ships the AppRole lease
and kit; this agent implements the plan's Gate 1 RESULT (login_jwt, no
unwrap) and refuses an AppRole lease loudly (LEASE_SHAPE).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KLB7jieMNRkTsJ2epr4Ds1
This commit is contained in:
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
# /usr/bin/monky-deployd -> the venv shipped in the .deb
|
||||
exec /opt/monky-deployd/venv/bin/python -m monky_deployd "$@"
|
||||
Executable
+222
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env bash
|
||||
# monky-deployd installer — Ubuntu 26.04 (verified target).
|
||||
#
|
||||
# curl -fsSL https://gitea.cbs.tikali.net/mdella/monky-deployd/raw/branch/main/packaging/install.sh \
|
||||
# | sudo bash -s -- --env env-qa-02 --site cbs [--transport sdk|proxy|system] [--version 0.1.0] \
|
||||
# [--enrol-jwt /path/monky-host.env-qa-02.jwt] [--laptop] < bootstrap.jwt
|
||||
#
|
||||
# stdin (or --bootstrap-file): the ONE-TIME bootstrap deploy grant from the install kit
|
||||
# (GET /v1/backends/{id}/agent/install). The enrolment JWT is read from --enrol-jwt or
|
||||
# /etc/monky-deployd/enrol.jwt and is only needed when the host identity is not enrolled yet.
|
||||
#
|
||||
# What it does (idempotent):
|
||||
# 1. apt: ziti-edge-tunnel (OpenZiti `jammy` suite) if absent, docker-compose-plugin, acl
|
||||
# 2. downloads the pinned monky-deployd_<ver>_amd64.deb + .sha256 from the Gitea release, verifies, installs
|
||||
# 3. enrols /opt/openziti/etc/identities/monky-host.<env>.json if absent (ziti-edge-tunnel enroll),
|
||||
# chown ziti:ziti 0600, switches ziti-edge-tunnel.service to `run-host` via a drop-in
|
||||
# 4. writes /etc/monky-deployd/config.yaml, ACLs so user monky-deployd can read the identity,
|
||||
# the bootstrap grant 0600, (proxy transport: proxy.env + monky-deployd-proxy.service)
|
||||
# 5. enables monky-deployd.timer, runs one tick, deletes the enrol JWT, prints the checklist
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
|
||||
DEFAULT_VERSION="0.1.0"
|
||||
BASE_URL="${MONKY_DEPLOYD_BASE_URL:-https://gitea.cbs.tikali.net/mdella/monky-deployd}"
|
||||
OPENZITI_SUITE="${OPENZITI_SUITE:-jammy}"
|
||||
IDENTITY_DIR="/opt/openziti/etc/identities"
|
||||
ETC="/etc/monky-deployd"
|
||||
ENV_ID="" SITE="" TRANSPORT="sdk" VERSION="$DEFAULT_VERSION" ENROL_JWT="" BOOTSTRAP_FILE="" NO_RUN="" FORCE_CONFIG="" BAO_CA="" LAPTOP="false"
|
||||
|
||||
usage() { sed -n '2,20p' "$0"; exit "${1:-0}"; }
|
||||
die() { echo "install.sh: $*" >&2; exit 1; }
|
||||
log() { echo "==> $*"; }
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--env) ENV_ID="$2"; shift 2 ;;
|
||||
--site) SITE="$2"; shift 2 ;;
|
||||
--transport) TRANSPORT="$2"; shift 2 ;;
|
||||
--version) VERSION="$2"; shift 2 ;;
|
||||
--enrol-jwt) ENROL_JWT="$2"; shift 2 ;;
|
||||
--bootstrap-file) BOOTSTRAP_FILE="$2"; shift 2 ;;
|
||||
--bao-ca) BAO_CA="$2"; shift 2 ;;
|
||||
--base-url) BASE_URL="$2"; shift 2 ;;
|
||||
--laptop) LAPTOP="true"; shift ;;
|
||||
--no-run) NO_RUN=1; shift ;;
|
||||
--force-config) FORCE_CONFIG=1; shift ;;
|
||||
-h|--help) usage 0 ;;
|
||||
*) die "unknown argument $1 (see --help)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ "$(id -u)" = 0 ] || die "run as root (sudo)"
|
||||
[ -n "$ENV_ID" ] || die "--env is required"
|
||||
[ -n "$SITE" ] || die "--site is required"
|
||||
[[ "$ENV_ID" =~ ^env-(dev|qa|stage|prod)-[0-9]{2,3}$|^(dev-env-2|prod-cedar)$ ]] || die "env id $ENV_ID is not env-<tier>-<nn>"
|
||||
SITE="${SITE,,}"
|
||||
[[ "$SITE" =~ ^(cbs|pdx)$ ]] || die "site must be cbs|pdx"
|
||||
[[ "$TRANSPORT" =~ ^(sdk|proxy|system)$ ]] || die "transport must be sdk|proxy|system"
|
||||
IDENTITY="$IDENTITY_DIR/monky-host.$ENV_ID.json"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
if [ -r /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
if [ "${ID:-}" != "ubuntu" ] || [ "${VERSION_ID:-}" != "26.04" ]; then
|
||||
echo "WARNING: verified on Ubuntu 26.04; this is ${PRETTY_NAME:-unknown}. Continuing." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 1. packages -------------------------------------------------------------------------------------
|
||||
apt_updated=""
|
||||
apt_update_once() { [ -n "$apt_updated" ] || { apt-get update -qq; apt_updated=1; }; }
|
||||
need_pkgs=(acl curl ca-certificates gnupg)
|
||||
if ! command -v ziti-edge-tunnel >/dev/null 2>&1; then
|
||||
log "adding the OpenZiti apt repository ($OPENZITI_SUITE suite)"
|
||||
install -d -m 0755 /usr/share/keyrings
|
||||
curl -fsSL https://get.openziti.io/tun/package-repos.gpg | gpg --dearmor -o /usr/share/keyrings/openziti.gpg
|
||||
chmod 0644 /usr/share/keyrings/openziti.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/openziti.gpg] https://packages.openziti.org/zitipax-openziti-deb-stable $OPENZITI_SUITE main" \
|
||||
> /etc/apt/sources.list.d/openziti.list
|
||||
need_pkgs+=(ziti-edge-tunnel)
|
||||
fi
|
||||
if [ "$TRANSPORT" = "proxy" ] && ! command -v ziti >/dev/null 2>&1; then
|
||||
need_pkgs+=(openziti) # the `ziti` CLI (ziti tunnel proxy) from the same repo
|
||||
fi
|
||||
command -v docker >/dev/null 2>&1 || die "docker is not installed; install Docker Engine first (https://docs.docker.com/engine/install/ubuntu/)"
|
||||
docker compose version >/dev/null 2>&1 || need_pkgs+=(docker-compose-plugin)
|
||||
python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 12) else 1)' 2>/dev/null || die "python3 >= 3.12 is required"
|
||||
if [ "${#need_pkgs[@]}" -gt 0 ]; then
|
||||
log "apt install: ${need_pkgs[*]}"
|
||||
apt_update_once
|
||||
apt-get install -y -qq --no-install-recommends "${need_pkgs[@]}"
|
||||
fi
|
||||
|
||||
# --- 2. the pinned .deb -----------------------------------------------------------------------------
|
||||
installed="$(dpkg-query -W -f='${Version}' monky-deployd 2>/dev/null || true)"
|
||||
if [ "$installed" = "$VERSION" ]; then
|
||||
log "monky-deployd $VERSION already installed"
|
||||
else
|
||||
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
|
||||
deb="monky-deployd_${VERSION}_amd64.deb"
|
||||
url="$BASE_URL/releases/download/v${VERSION}"
|
||||
log "downloading $deb from $url"
|
||||
curl -fsSL -o "$tmp/$deb" "$url/$deb"
|
||||
curl -fsSL -o "$tmp/$deb.sha256" "$url/$deb.sha256"
|
||||
(cd "$tmp" && sha256sum -c "$deb.sha256") || die "sha256 mismatch on $deb"
|
||||
apt_update_once || true
|
||||
apt-get install -y -qq "$tmp/$deb"
|
||||
fi
|
||||
command -v monky-deployd >/dev/null || die "monky-deployd not on PATH after install"
|
||||
|
||||
# --- 3. host identity -------------------------------------------------------------------------------
|
||||
install -d -m 0750 "$IDENTITY_DIR"
|
||||
[ -n "$ENROL_JWT" ] || { [ -f "$ETC/enrol.jwt" ] && ENROL_JWT="$ETC/enrol.jwt"; } || true
|
||||
if [ -s "$IDENTITY" ]; then
|
||||
log "host identity present: $IDENTITY"
|
||||
else
|
||||
[ -n "$ENROL_JWT" ] && [ -s "$ENROL_JWT" ] || die "no identity at $IDENTITY and no enrol JWT (--enrol-jwt or $ETC/enrol.jwt)"
|
||||
log "enrolling monky-host.$ENV_ID"
|
||||
ziti-edge-tunnel enroll -j "$ENROL_JWT" -i "$IDENTITY"
|
||||
fi
|
||||
getent passwd ziti >/dev/null && chown ziti:ziti "$IDENTITY" || true
|
||||
chmod 0600 "$IDENTITY"
|
||||
# ziti-edge-tunnel as a HOST (bind side, no tun/DNS) — the agent dials with the SDK or the proxy
|
||||
install -d /etc/systemd/system/ziti-edge-tunnel.service.d
|
||||
cat > /etc/systemd/system/ziti-edge-tunnel.service.d/run-host.conf <<'DROPIN'
|
||||
# monky-deployd: run-host mode (no tun, no DNS); identities from the standard directory
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/opt/openziti/bin/ziti-edge-tunnel run-host --identity-dir=/opt/openziti/etc/identities
|
||||
DROPIN
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ziti-edge-tunnel.service
|
||||
systemctl restart ziti-edge-tunnel.service || true
|
||||
|
||||
# --- 4. config, ACLs, bootstrap grant -----------------------------------------------------------------
|
||||
install -d -m 0750 -o root -g monky-deployd "$ETC"
|
||||
install -d -m 0700 -o monky-deployd -g monky-deployd /var/lib/monky-deployd
|
||||
setfacl -m u:monky-deployd:r "$IDENTITY"
|
||||
setfacl -m u:monky-deployd:rx "$IDENTITY_DIR"
|
||||
setfacl -m u:monky-deployd:x /opt/openziti/etc 2>/dev/null || true
|
||||
if [ -n "$BAO_CA" ]; then
|
||||
install -m 0644 "$BAO_CA" "$ETC/openbao-ca.pem"
|
||||
fi
|
||||
ca_line="ca_bundle: $ETC/openbao-ca.pem"
|
||||
[ -s "$ETC/openbao-ca.pem" ] || { ca_line="ca_bundle: none # TODO: install the openbao-ca certificate (see docs/OPERATIONS.md)"; echo "WARNING: $ETC/openbao-ca.pem missing; TLS to OpenBao will use the system store" >&2; }
|
||||
if [ -s "$ETC/config.yaml" ] && [ -z "$FORCE_CONFIG" ]; then
|
||||
log "keeping existing $ETC/config.yaml (use --force-config to rewrite)"
|
||||
else
|
||||
cat > "$ETC/config.yaml" <<CFG
|
||||
# written by install.sh $(date -u +%FT%TZ) — monky-deployd $VERSION
|
||||
env_id: $ENV_ID
|
||||
site: $SITE
|
||||
transport: $TRANSPORT
|
||||
identity: $IDENTITY
|
||||
tenancy:
|
||||
service: monky.tenancy.deploy
|
||||
host: monky.tenancy.deploy
|
||||
port: 8081
|
||||
scheme: http
|
||||
proxy_addr: 127.0.0.1:18443
|
||||
bao:
|
||||
service: openbao
|
||||
addr: https://bao.cbs.tikali.net:8200
|
||||
proxy_addr: 127.0.0.1:18200
|
||||
$ca_line
|
||||
mount: jwt-tenancy
|
||||
role: see-env
|
||||
kv_mount: monky
|
||||
state_dir: /var/lib/monky-deployd
|
||||
deploy_dir: /var/lib/monky-deployd/$ENV_ID
|
||||
bootstrap_path: $ETC/bootstrap.jwt
|
||||
interval_s: 60
|
||||
volumes_on_absent: keep
|
||||
laptop_mode: $LAPTOP
|
||||
CFG
|
||||
chmod 0640 "$ETC/config.yaml"; chgrp monky-deployd "$ETC/config.yaml"
|
||||
fi
|
||||
if [ "$TRANSPORT" = "proxy" ]; then
|
||||
printf 'ZITI_IDENTITY=%s\n' "$IDENTITY" > "$ETC/proxy.env"; chmod 0644 "$ETC/proxy.env"
|
||||
systemctl enable --now monky-deployd-proxy.service
|
||||
fi
|
||||
# bootstrap grant: stdin (the kit pipes it) or --bootstrap-file; 0600, owned by the agent so it can consume it
|
||||
grant=""
|
||||
if [ -n "$BOOTSTRAP_FILE" ]; then grant="$(tr -d '[:space:]' < "$BOOTSTRAP_FILE")"
|
||||
elif [ ! -t 0 ]; then grant="$(tr -d '[:space:]' </dev/stdin || true)"; fi
|
||||
if [ -n "$grant" ]; then
|
||||
printf '%s\n' "$grant" > "$ETC/bootstrap.jwt"
|
||||
chown monky-deployd:monky-deployd "$ETC/bootstrap.jwt"; chmod 0600 "$ETC/bootstrap.jwt"
|
||||
log "bootstrap grant staged at $ETC/bootstrap.jwt (consumed on first tick)"
|
||||
elif [ -s /var/lib/monky-deployd/bao.token ]; then
|
||||
log "no bootstrap grant given; existing bao.token kept"
|
||||
else
|
||||
echo "WARNING: no bootstrap grant on stdin and no bao.token: the agent cannot check in until you provide one" >&2
|
||||
fi
|
||||
unset grant
|
||||
|
||||
# --- 5. timer + first tick ----------------------------------------------------------------------------------
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now monky-deployd.timer
|
||||
if [ -z "$NO_RUN" ]; then
|
||||
log "first tick"
|
||||
systemctl start monky-deployd.service || true
|
||||
fi
|
||||
[ -n "$ENROL_JWT" ] && rm -f "$ENROL_JWT" || true
|
||||
rm -f "$ETC/enrol.jwt"
|
||||
|
||||
cat <<CHECK
|
||||
|
||||
monky-deployd $VERSION installed for $ENV_ID ($SITE, transport $TRANSPORT). Verification checklist (Ubuntu 26.04):
|
||||
python3 --version # >= 3.12
|
||||
docker compose version # compose plugin present
|
||||
systemctl is-active ziti-edge-tunnel # run-host mode (drop-in run-host.conf)
|
||||
$( [ "$TRANSPORT" = proxy ] && echo "systemctl is-active monky-deployd-proxy # ziti tunnel proxy on 18443/18200" || echo "ziti tunnel proxy --help >/dev/null # fallback transport available" )
|
||||
/opt/monky-deployd/venv/bin/python -c 'import openziti' # SDK import (transport sdk)
|
||||
monky-deployd status # token present, applied == desired
|
||||
journalctl -u monky-deployd -n 50 # 'checkin:' and 'applied' lines
|
||||
df -h \$(docker info -f '{{.DockerRootDir}}') # free space >= bundle need x 1.5 + headroom
|
||||
docker compose -p monky-$ENV_ID ps # healthy
|
||||
# from another mesh member: curl monky.percept.$ENV_ID:47283/health -> 200
|
||||
CHECK
|
||||
CHECK_STATUS="$(monky-deployd status 2>&1 || true)"
|
||||
echo "$CHECK_STATUS" | sed 's/^/ | /'
|
||||
@@ -0,0 +1,2 @@
|
||||
# systemd-sysusers: the agent's service account (docker group membership is added by postinstall)
|
||||
u monky-deployd - "Monky backend pull agent" /var/lib/monky-deployd
|
||||
@@ -0,0 +1,2 @@
|
||||
d /var/lib/monky-deployd 0700 monky-deployd monky-deployd -
|
||||
d /etc/monky-deployd 0750 root monky-deployd -
|
||||
@@ -0,0 +1,53 @@
|
||||
# nfpm (https://nfpm.goreleaser.com) — builds monky-deployd_<ver>_amd64.deb in CI (`package` job).
|
||||
# The venv under /opt/monky-deployd/venv is created by CI on ubuntu:26.04 with the system
|
||||
# python3 (3.14 on 26.04; the agent needs >= 3.12) and the vendored openziti wheel when present.
|
||||
name: monky-deployd
|
||||
arch: amd64
|
||||
platform: linux
|
||||
version: ${VERSION}
|
||||
section: admin
|
||||
priority: optional
|
||||
maintainer: Tikali <mdella@tikali.ai>
|
||||
description: |
|
||||
Monky backend pull agent (MONKY-ADR-0028): checkin -> bundle -> lease -> OpenBao -> docker compose -> report,
|
||||
over the OpenZiti mesh with the box's own host identity.
|
||||
vendor: Tikali
|
||||
homepage: https://scm.tikali.ai/tikali/applications/monky/monky-deployd
|
||||
license: Proprietary
|
||||
depends:
|
||||
- python3 (>= 3.12)
|
||||
- acl
|
||||
- ca-certificates
|
||||
recommends:
|
||||
- docker-compose-plugin
|
||||
- ziti-edge-tunnel
|
||||
contents:
|
||||
- src: ./build/venv
|
||||
dst: /opt/monky-deployd/venv
|
||||
- src: ./packaging/bin/monky-deployd
|
||||
dst: /usr/bin/monky-deployd
|
||||
file_info: { mode: 0755 }
|
||||
- src: ./packaging/systemd/monky-deployd.service
|
||||
dst: /usr/lib/systemd/system/monky-deployd.service
|
||||
- src: ./packaging/systemd/monky-deployd.timer
|
||||
dst: /usr/lib/systemd/system/monky-deployd.timer
|
||||
- src: ./packaging/systemd/monky-deployd-proxy.service
|
||||
dst: /usr/lib/systemd/system/monky-deployd-proxy.service
|
||||
- src: ./packaging/monky-deployd.sysusers
|
||||
dst: /usr/lib/sysusers.d/monky-deployd.conf
|
||||
- src: ./packaging/monky-deployd.tmpfiles
|
||||
dst: /usr/lib/tmpfiles.d/monky-deployd.conf
|
||||
- src: ./config.example.yaml
|
||||
dst: /etc/monky-deployd/config.example.yaml
|
||||
type: config
|
||||
- src: ./packaging/install.sh
|
||||
dst: /usr/share/monky-deployd/install.sh
|
||||
file_info: { mode: 0755 }
|
||||
- src: ./README.md
|
||||
dst: /usr/share/doc/monky-deployd/README.md
|
||||
- src: ./docs/OPERATIONS.md
|
||||
dst: /usr/share/doc/monky-deployd/OPERATIONS.md
|
||||
scripts:
|
||||
postinstall: ./packaging/scripts/postinstall.sh
|
||||
preremove: ./packaging/scripts/preremove.sh
|
||||
postremove: ./packaging/scripts/postremove.sh
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
# service account + dirs
|
||||
if command -v systemd-sysusers >/dev/null 2>&1; then
|
||||
systemd-sysusers /usr/lib/sysusers.d/monky-deployd.conf || true
|
||||
else
|
||||
getent passwd monky-deployd >/dev/null || useradd --system --home-dir /var/lib/monky-deployd --shell /usr/sbin/nologin monky-deployd
|
||||
fi
|
||||
if command -v systemd-tmpfiles >/dev/null 2>&1; then
|
||||
systemd-tmpfiles --create /usr/lib/tmpfiles.d/monky-deployd.conf || true
|
||||
fi
|
||||
install -d -m 0700 -o monky-deployd -g monky-deployd /var/lib/monky-deployd
|
||||
install -d -m 0750 -o root -g monky-deployd /etc/monky-deployd
|
||||
# the agent drives docker compose: docker group membership (no root)
|
||||
if getent group docker >/dev/null; then usermod -a -G docker monky-deployd || true; fi
|
||||
# the venv is relocatable only to the path it was built at; refuse a broken interpreter early
|
||||
/opt/monky-deployd/venv/bin/python -c 'import monky_deployd' || { echo "monky-deployd: venv unusable (python3 mismatch?)" >&2; exit 1; }
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl daemon-reload || true
|
||||
# do NOT enable the timer here: install.sh / the ansible role do it after the config exists
|
||||
fi
|
||||
exit 0
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
if [ -d /run/systemd/system ]; then systemctl daemon-reload || true; fi
|
||||
# purge (dpkg -P) removes state; a plain remove keeps /var/lib/monky-deployd (token + releases)
|
||||
if [ "$1" = "purge" ]; then rm -rf /var/lib/monky-deployd /etc/monky-deployd; fi
|
||||
exit 0
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl disable --now monky-deployd.timer 2>/dev/null || true
|
||||
systemctl disable --now monky-deployd-proxy.service 2>/dev/null || true
|
||||
fi
|
||||
exit 0
|
||||
@@ -0,0 +1,42 @@
|
||||
# Fallback transport (b): `ziti tunnel proxy` publishes the two mesh services on loopback so the
|
||||
# agent (transport: proxy) can reach them without the Python SDK. Runs as user ziti with the
|
||||
# SAME host identity ziti-edge-tunnel run-host uses. Enable only with `transport: proxy`.
|
||||
# monky.tenancy.deploy -> 127.0.0.1:18443 (plain HTTP inside the mesh)
|
||||
# openbao -> 127.0.0.1:18200 (TLS end-to-end; SNI bao.cbs.tikali.net)
|
||||
# NOTE: `ziti tunnel proxy` listens on IPv4 0.0.0.0 only (see project_openbao_consumer_gateway);
|
||||
# the agent dials 127.0.0.1, so that is fine here.
|
||||
[Unit]
|
||||
Description=ziti tunnel proxy for monky-deployd (transport: proxy)
|
||||
Documentation=https://scm.tikali.ai/tikali/applications/monky/monky-deployd
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
ConditionPathExists=/etc/monky-deployd/proxy.env
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=ziti
|
||||
Group=ziti
|
||||
# proxy.env sets ZITI_IDENTITY=/opt/openziti/etc/identities/monky-host.<env>.json
|
||||
EnvironmentFile=/etc/monky-deployd/proxy.env
|
||||
ExecStart=/usr/bin/ziti tunnel proxy -i ${ZITI_IDENTITY} monky.tenancy.deploy:18443 openbao:18200
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
CapabilityBoundingSet=
|
||||
ReadOnlyPaths=-/opt/openziti/etc/identities
|
||||
IPAddressDeny=any
|
||||
IPAddressAllow=localhost
|
||||
# the mesh edge routers are dialled by the ziti library itself: allow egress everywhere but
|
||||
# only accept on loopback (the listeners bind 0.0.0.0 — IPAddressAllow above limits who connects)
|
||||
IPAddressAllow=0.0.0.0/0 ::/0
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,55 @@
|
||||
# monky-deployd — one reconcile tick (MONKY-ADR-0028 §D). Fired by monky-deployd.timer.
|
||||
[Unit]
|
||||
Description=Monky backend pull agent (one tick: checkin -> bundle -> lease -> OpenBao -> compose -> report)
|
||||
Documentation=https://scm.tikali.ai/tikali/applications/monky/monky-deployd
|
||||
After=network-online.target docker.service ziti-edge-tunnel.service
|
||||
Wants=network-online.target
|
||||
# The proxy transport needs the local ziti proxy; harmless when the unit is disabled.
|
||||
After=monky-deployd-proxy.service
|
||||
ConditionPathExists=/etc/monky-deployd/config.yaml
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=monky-deployd
|
||||
Group=monky-deployd
|
||||
SupplementaryGroups=docker
|
||||
ExecStart=/usr/bin/monky-deployd run --once
|
||||
# 75 = temporary network failure (next tick retries), 78 = AGENT_ENV_MISMATCH (operator action)
|
||||
SuccessExitStatus=75
|
||||
TimeoutStartSec=20min
|
||||
Nice=5
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
Environment=MONKY_DEPLOYD_CONFIG=/etc/monky-deployd/config.yaml
|
||||
UMask=0077
|
||||
# hardening
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectClock=yes
|
||||
ProtectHostname=yes
|
||||
RestrictSUIDSGID=yes
|
||||
RestrictRealtime=yes
|
||||
LockPersonality=yes
|
||||
# (no MemoryDenyWriteExecute: the openziti SDK's ctypes callbacks need libffi trampolines)
|
||||
RestrictNamespaces=yes
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallFilter=~@privileged @resources
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
StateDirectory=monky-deployd
|
||||
StateDirectoryMode=0700
|
||||
ReadWritePaths=/var/lib/monky-deployd
|
||||
# the bootstrap grant is deleted after the first login (the only write under /etc)
|
||||
ReadWritePaths=/etc/monky-deployd
|
||||
ReadOnlyPaths=-/opt/openziti/etc/identities
|
||||
# docker socket + compose need the socket path writable
|
||||
ReadWritePaths=-/var/run/docker.sock -/run/docker.sock
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,14 @@
|
||||
# Every 60 s after the previous tick finished (+ up to 10 s jitter); flock in the agent prevents overlap.
|
||||
[Unit]
|
||||
Description=Monky backend pull agent tick timer
|
||||
Documentation=https://scm.tikali.ai/tikali/applications/monky/monky-deployd
|
||||
|
||||
[Timer]
|
||||
OnBootSec=90s
|
||||
OnUnitActiveSec=60s
|
||||
RandomizedDelaySec=10s
|
||||
AccuracySec=1s
|
||||
Unit=monky-deployd.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Reference in New Issue
Block a user