diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 09fd964..e0725e1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,8 +10,9 @@ # GITEA_TOKEN — Gitea API token (write:repository) for `release:gitea`; without it the job is manual # # Release assets are published to BOTH the GitLab generic package registry (the installer's primary -# source — the project is public; gitea.cbs.tikali.net is split-horizon inside the estate, cbs/iac#102) -# and the Gitea mirror release (`install.sh --source gitea`, off-estate). +# source — the project is PRIVATE (parent groups are private), so install.sh sends a read-only deploy +# token (read_package_registry) as DEPLOY-TOKEN; gitea.cbs.tikali.net is split-horizon inside the +# estate, cbs/iac#102) and the Gitea mirror release (`install.sh --source gitea`, off-estate). # GITLAB_TRANSLATE_TOKEN — used by the translate-docs component (group level) include: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b65de7..1fcf432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,24 @@ # Changelog +## v0.1.2 — 2026-09-05 + +- **The GitLab project is private** (its parent groups are private, so it cannot be made public; + found when the v0.1.1 one-liner returned 401 anonymously). `install.sh` gains `--token ` + / `MONKY_DEPLOYD_TOKEN`: every download from the generic package registry — the script itself + included — sends `DEPLOY-TOKEN: `, a read-only GitLab **deploy token** (scope + `read_package_registry` only, revocable), seeded in OpenBao at `monky/monky-tenancy/deployd-download` + (key `token`) and handed to the box by the monky-tenancy install kit. The token never reaches the + command line, the log or an xtrace (curl `-K` config file, 0600, deleted after the download; + `set +x` forced). The one-liner now fetches `install.sh` from the registry + (`…/monky-deployd//install.sh`) instead of `-/raw/main`, and takes the bootstrap grant via + `--bootstrap-file` — with `curl … | bash -s --` stdin IS the script, so it is no longer read for + the grant in that mode. `--source gitea` (no token) stays the off-estate alternative. +- Ansible role: `monky_deployd_download_token` (vaulted) → `DEPLOY-TOKEN` header on both fetches, + `no_log: true`. +- README / OPERATIONS: why (private project; split-horizon Gitea, cbs/iac#102), token scope, revocation. + No agent behaviour change. + ## v0.1.1 — 2026-09-05 - `install.sh` / ansible role / README: the **primary download is the public GitLab project's generic diff --git a/CLAUDE.md b/CLAUDE.md index 9e5cf05..81eefe3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,8 +75,9 @@ systemd-analyze verify packaging/systemd/*.service # when available Bump `monky_deployd.__version__` + `pyproject.toml` + `packaging/install.sh` `DEFAULT_VERSION` + `CHANGELOG.md`, merge to `main`, tag `vX.Y.Z` (protected `v*`). The tag pipeline builds the `.deb`, -publishes the **GitLab generic package registry + release** the installer downloads from (public -project; the Gitea name is split-horizon inside the estate, cbs/iac#102) and — with `GITEA_TOKEN` — +publishes the **GitLab generic package registry + release** the installer downloads from (PRIVATE +project — the installer sends a read-only deploy token as `DEPLOY-TOKEN`; the Gitea name is +split-horizon inside the estate, cbs/iac#102) and — with `GITEA_TOKEN` — the **Gitea release** (`install.sh --source gitea`, off-estate; `docs/OPERATIONS.md` has the manual recipe). `lint` refuses a tag whose version differs from `__version__`. diff --git a/README.md b/README.md index 7a6c49f..5595813 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,14 @@ Verified on **Ubuntu 26.04**. ## Install (one-liner, from the enrolment kit) An admin reveals the kit once in the console (`GET /v1/backends/{id}/agent/install`); it hands -you the enrolment JWT and a one-time **bootstrap deploy grant**. On the box: +you the enrolment JWT, a one-time **bootstrap deploy grant** and the read-only **download token** +(the kit runs the line below for you). By hand, on the box: ```sh -curl -fsSL https://scm.tikali.ai/tikali/applications/monky/monky-deployd/-/raw/main/packaging/install.sh \ - | sudo bash -s -- --env env-qa-02 --site cbs --enrol-jwt ./monky-host.env-qa-02.jwt < bootstrap.jwt -# [--transport sdk|proxy|system] [--version 0.1.1] [--laptop] [--bao-ca openbao-ca.pem] [--source gitlab|gitea] +T= # read-only GitLab deploy token (read_package_registry); the kit carries it +curl -sSf -H "DEPLOY-TOKEN: $T" https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd/0.1.2/install.sh \ + | sudo bash -s -- --env env-qa-02 --site cbs --token "$T" --bootstrap-file bootstrap.jwt --enrol-jwt ./monky-host.env-qa-02.jwt +# [--transport sdk|proxy|system] [--version 0.1.2] [--laptop] [--bao-ca openbao-ca.pem] [--source gitlab|gitea] ``` `install.sh` installs `ziti-edge-tunnel` (OpenZiti `jammy` suite) and `docker-compose-plugin` if @@ -32,11 +34,21 @@ absent, downloads the pinned `.deb` + `.sha256` from the [scm.tikali.ai generic enrols `monky-host.` if the identity is missing, switches the tunneler to `run-host`, writes `/etc/monky-deployd/config.yaml`, grants the agent read access to the identity (ACL), stages the bootstrap grant (0600), enables `monky-deployd.timer`, runs one tick and deletes the -JWT. The GitLab project is **public**, so anonymous downloads work from scm.tikali.ai. Why not the -Gitea mirror: inside the estate `gitea.cbs.tikali.net` is split-horizon to jump1's RED EIP -(`10.10.0.175`), which has no HTTP ingress, so backend boxes cannot reach it (cbs/iac#102); -`--source gitea` (or `MONKY_DEPLOYD_SOURCE=gitea`) keeps the -[Gitea release](https://gitea.cbs.tikali.net/mdella/monky-deployd/releases) as the off-estate alternative. +JWT. + +**Why a token.** The GitLab project is **private** and cannot be made public (its parent groups +are private), so anonymous downloads answer 401. Every registry fetch — the script itself +included — therefore sends `DEPLOY-TOKEN: `: a GitLab **deploy token** with the single +scope `read_package_registry` (it can download packages and nothing else: no code, no API, no +write; revoke it in the project's *Settings → Repository → Deploy tokens* and issue a new one). +The operator seeds it in OpenBao at `monky/monky-tenancy/deployd-download` (key `token`); the +monky-tenancy install kit reads it from there and passes `--token` (`MONKY_DEPLOYD_TOKEN` also +works). The script never prints it (curl `-K` config file, 0600, deleted after the download; +`set -x` is switched off). Why not the Gitea mirror: inside the estate `gitea.cbs.tikali.net` is +split-horizon to jump1's RED EIP (`10.10.0.175`), which has no HTTP ingress, so backend boxes +cannot reach it (cbs/iac#102); `--source gitea` (or `MONKY_DEPLOYD_SOURCE=gitea`, no token) keeps +the [Gitea release](https://gitea.cbs.tikali.net/mdella/monky-deployd/releases) as the +off-estate alternative. ## Transports @@ -145,8 +157,8 @@ systemd-analyze verify packaging/systemd/*.service # where systemd is availabl egress to github.com and pypi.org) and `package` builds the `.deb` with `nfpm` (binary from GitHub releases, goreleaser apt repo as fallback). Both were proven on the v0.1.0 tag pipeline and are blocking on `main`/tags (manual on MRs); the `.deb` always ships the SDK wheel. On a `v*` tag -`release` uploads to the GitLab generic package registry + release (**the primary download**, public -project), and `release:gitea` publishes the same assets on the Gitea mirror (the `--source gitea` +`release` uploads to the GitLab generic package registry + release (**the primary download**; the +project is private, so the installer sends the read-only deploy token), and `release:gitea` publishes the same assets on the Gitea mirror (the `--source gitea` alternative; automatic when `GITEA_TOKEN` is set, manual otherwise — see `docs/OPERATIONS.md` for the by-hand recipe). Both locations keep being published. diff --git a/ansible/roles/monky_deployd/README.md b/ansible/roles/monky_deployd/README.md index be89cd4..43f0c5e 100644 --- a/ansible/roles/monky_deployd/README.md +++ b/ansible/roles/monky_deployd/README.md @@ -4,7 +4,9 @@ Installs and configures [monky-deployd](https://scm.tikali.ai/tikali/application (the Monky backend pull agent, MONKY-ADR-0028) on a docker host that already carries an enrolled host identity (`roles/ziti_tunneler`, run-host mode). Skeleton for **osg1-07**; copy it there. -What it does: pin + download the `.deb` from the scm.tikali.ai package registry (sha256 verified; `monky_deployd_base_url`/`_deb_url` switch to the Gitea release off-estate) → ACL +What it does: pin + download the `.deb` from the scm.tikali.ai package registry (sha256 verified; the +project is **private**, so the fetches send the read-only deploy token `monky_deployd_download_token` +as `DEPLOY-TOKEN`, `no_log`; `monky_deployd_base_url`/`_deb_url` switch to the Gitea release off-estate) → ACL `u:monky-deployd:r` on the identity (`rx` on the dir) → `/etc/monky-deployd/config.yaml` from the template → optional openbao-ca PEM → optional `monky-deployd-proxy.service` (`transport: proxy`) → the **one-time bootstrap deploy grant** from a vault var (0600, `no_log`) → `monky-deployd.timer` @@ -14,7 +16,8 @@ template → optional openbao-ca PEM → optional `monky-deployd-proxy.service` | var | note | |---|---| -| `monky_deployd_version` | pinned release, e.g. `0.1.1` | +| `monky_deployd_version` | pinned release, e.g. `0.1.2` | +| `monky_deployd_download_token` | **vaulted**: GitLab deploy token, scope `read_package_registry` only (revocable) — the registry is private; seeded in OpenBao at `monky/monky-tenancy/deployd-download` key `token` (path/key are the operator's choice). Empty = no header (only works with the Gitea `base_url`) | | `monky_deployd_env_id` / `_site` / `_transport` | per host (`env-dev-06`, `cbs`, `sdk`) | | `monky_deployd_bootstrap_grant` | tenancy-minted deploy grant (1 h) — `ansible-vault` or a lookup at play time; empty keeps the existing token | | `monky_deployd_bao_ca_pem` | the `openbao-ca` certificate (PEM) | @@ -30,6 +33,7 @@ template → optional openbao-ca PEM → optional `monky-deployd-proxy.service` - role: monky_deployd vars: monky_deployd_bootstrap_grant: "{{ lookup('pipe', 'tenancy-mint-grant ' ~ inventory_hostname) }}" + monky_deployd_download_token: "{{ vault_monky_deployd_download_token }}" # ansible-vault / OpenBao lookup ``` Rollout order (plan §E): pilot env-qa-02 → env-dev-06..09 → env-dev-01 last (after its live key moves into Bao). diff --git a/ansible/roles/monky_deployd/defaults/main.yml b/ansible/roles/monky_deployd/defaults/main.yml index 08190ea..9f98342 100644 --- a/ansible/roles/monky_deployd/defaults/main.yml +++ b/ansible/roles/monky_deployd/defaults/main.yml @@ -1,16 +1,24 @@ --- # monky_deployd — install and configure the Monky backend pull agent (MONKY-ADR-0028 §D). # Copy this role into osg1-07 (roles/monky_deployd) and roll to env-dev-06..09 after the pilot. -monky_deployd_version: "0.1.1" +monky_deployd_version: "0.1.2" monky_deployd_deb: "monky-deployd_{{ monky_deployd_version }}_amd64.deb" -# PRIMARY download = the public GitLab project's generic package registry on scm.tikali.ai. Inside the -# estate gitea.cbs.tikali.net is split-horizon to jump1's RED EIP (10.10.0.175, no HTTP ingress), so -# backend boxes cannot fetch from the Gitea mirror (cbs/iac#102). Off-estate alternative (Gitea release): +# PRIMARY download = the GitLab project's generic package registry on scm.tikali.ai. Inside the estate +# gitea.cbs.tikali.net is split-horizon to jump1's RED EIP (10.10.0.175, no HTTP ingress), so backend +# boxes cannot fetch from the Gitea mirror (cbs/iac#102). The project is PRIVATE (its parent groups are +# private), so the registry needs a read-only GitLab deploy token (scope read_package_registry only, +# revocable) sent as the `DEPLOY-TOKEN` header — `monky_deployd_download_token`, a VAULTED var +# (ansible-vault or an OpenBao lookup: KV `monky/monky-tenancy/deployd-download`, key `token`). +# Off-estate alternative (Gitea release, no token): # monky_deployd_base_url: "https://gitea.cbs.tikali.net/mdella/monky-deployd" # monky_deployd_deb_url: "{{ monky_deployd_base_url }}/releases/download/v{{ monky_deployd_version }}/{{ monky_deployd_deb }}" +# monky_deployd_download_token: "" monky_deployd_base_url: "https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd" monky_deployd_deb_url: "{{ monky_deployd_base_url }}/{{ monky_deployd_version }}/{{ monky_deployd_deb }}" monky_deployd_deb_sha256_url: "{{ monky_deployd_deb_url }}.sha256" +monky_deployd_download_token: "" +# the header the download tasks send (empty map when no token) +monky_deployd_download_headers: "{{ {'DEPLOY-TOKEN': monky_deployd_download_token} if monky_deployd_download_token | length > 0 else {} }}" # per host (inventory / host_vars) monky_deployd_env_id: "{{ inventory_hostname }}" # env-dev-06 ... diff --git a/ansible/roles/monky_deployd/tasks/main.yml b/ansible/roles/monky_deployd/tasks/main.yml index 9564354..89bfcd2 100644 --- a/ansible/roles/monky_deployd/tasks/main.yml +++ b/ansible/roles/monky_deployd/tasks/main.yml @@ -22,20 +22,26 @@ changed_when: false failed_when: false +# The registry is PRIVATE: both fetches send `DEPLOY-TOKEN: {{ monky_deployd_download_token }}` +# (read_package_registry only) and run no_log so the header never reaches the play output. - name: monky_deployd | download .deb + sha256 from the scm.tikali.ai package registry when: monky_deployd_installed.stdout != monky_deployd_version block: - name: monky_deployd | fetch sha256 ansible.builtin.uri: url: "{{ monky_deployd_deb_sha256_url }}" + headers: "{{ monky_deployd_download_headers }}" return_content: true register: monky_deployd_sha + no_log: true - name: monky_deployd | fetch .deb (checksum verified) ansible.builtin.get_url: url: "{{ monky_deployd_deb_url }}" + headers: "{{ monky_deployd_download_headers }}" dest: "/var/cache/apt/archives/{{ monky_deployd_deb }}" checksum: "sha256:{{ monky_deployd_sha.content.split()[0] }}" mode: "0644" + no_log: true - name: monky_deployd | install .deb ansible.builtin.apt: deb: "/var/cache/apt/archives/{{ monky_deployd_deb }}" diff --git a/config.example.yaml b/config.example.yaml index 157ec1e..ca255f1 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,4 +1,4 @@ -# /etc/monky-deployd/config.yaml — monky-deployd v0.1.1 (MONKY-ADR-0028 §D) +# /etc/monky-deployd/config.yaml — monky-deployd v0.1.2 (MONKY-ADR-0028 §D) # Written by packaging/install.sh (or the ansible role monky_deployd). YAML *subset*: maps, scalars, # simple lists, comments. Keys not listed here are a config error. diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 6da59a1..313d7f7 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -88,14 +88,27 @@ the picker, nothing is retired automatically. ## Where the installer downloads from -The primary source is the **scm.tikali.ai generic package registry** of this (public) project: +The primary source is the **scm.tikali.ai generic package registry** of this project: `https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd//` for -`monky-deployd__amd64.deb`, `.sha256` and `install.sh`; the one-liner fetches the script from -`https://scm.tikali.ai/tikali/applications/monky/monky-deployd/-/raw/main/packaging/install.sh`. -Inside the estate `gitea.cbs.tikali.net` is split-horizon to jump1's RED EIP (`10.10.0.175`), which -has no HTTP ingress, so backend boxes cannot fetch from the Gitea mirror (cbs/iac#102). Off-estate, -`install.sh --source gitea` (ansible: `monky_deployd_base_url`/`_deb_url`) uses the Gitea release -instead. The tag pipeline publishes to both (`release`, `release:gitea`). +`monky-deployd__amd64.deb`, `.sha256` and `install.sh`. The project is **private** (its parent +groups are private, so it cannot be made public): every fetch, the script itself included, sends the +`DEPLOY-TOKEN` header with a read-only GitLab **deploy token** — scope `read_package_registry` only, +nothing else (no repository, no API, no write); revocable at any time in the project's *Settings → +Repository → Deploy tokens*. It lives in OpenBao at `monky/monky-tenancy/deployd-download` (key +`token`); the monky-tenancy install kit carries it and passes `--token`, the ansible role sends it +from the vaulted `monky_deployd_download_token`. The one-liner: + +```sh +curl -sSf -H "DEPLOY-TOKEN: $T" https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd//install.sh \ + | sudo bash -s -- --env --site --token "$T" --bootstrap-file bootstrap.jwt +``` + +`install.sh` never prints the token (it goes through a 0600 curl `-K` file that is deleted after the +download; xtrace is switched off). A 401 on the download means the token is missing, revoked or +lacks the scope. Inside the estate `gitea.cbs.tikali.net` is split-horizon to jump1's RED EIP +(`10.10.0.175`), which has no HTTP ingress, so backend boxes cannot fetch from the Gitea mirror +(cbs/iac#102). Off-estate, `install.sh --source gitea` (ansible: `monky_deployd_base_url`/`_deb_url`, +no token) uses the Gitea release instead. The tag pipeline publishes to both (`release`, `release:gitea`). ## Publishing a release to Gitea by hand diff --git a/monky_deployd/__init__.py b/monky_deployd/__init__.py index c98d5e6..1638825 100644 --- a/monky_deployd/__init__.py +++ b/monky_deployd/__init__.py @@ -4,4 +4,4 @@ Dials monky-tenancy over the mesh with the box's host identity, fetches the rend leases a deploy grant, logs in to OpenBao, reads its own secrets, runs `docker compose`, reports. Stdlib only; the optional `openziti` SDK is the `sdk` transport.""" -__version__ = "0.1.1" +__version__ = "0.1.2" diff --git a/packaging/install.sh b/packaging/install.sh index 2295c9f..bba6f6a 100755 --- a/packaging/install.sh +++ b/packaging/install.sh @@ -1,18 +1,26 @@ #!/usr/bin/env bash # monky-deployd installer — Ubuntu 26.04 (verified target). # -# curl -fsSL https://scm.tikali.ai/tikali/applications/monky/monky-deployd/-/raw/main/packaging/install.sh \ -# | sudo bash -s -- --env env-qa-02 --site cbs [--transport sdk|proxy|system] [--version 0.1.1] \ -# [--enrol-jwt /path/monky-host.env-qa-02.jwt] [--laptop] [--source gitlab|gitea] < bootstrap.jwt +# T= # from the install kit / OpenBao +# curl -sSf -H "DEPLOY-TOKEN: $T" \ +# https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd/0.1.2/install.sh \ +# | sudo bash -s -- --env env-qa-02 --site cbs --token "$T" --bootstrap-file bootstrap.jwt \ +# [--transport sdk|proxy|system] [--version 0.1.2] [--enrol-jwt /path/monky-host.env-qa-02.jwt] \ +# [--laptop] [--source gitlab|gitea] # -# 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. +# --token / MONKY_DEPLOYD_TOKEN: the GitLab project is PRIVATE (its parent groups are private, so it +# cannot be made public); every download from the generic package registry — this script included — +# sends `DEPLOY-TOKEN: `. The token is read-only (read_package_registry), revocable, and is +# never printed by this script. Not needed with --source gitea. +# --bootstrap-file (or stdin when the script runs from a 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__amd64.deb + .sha256 from the scm.tikali.ai package registry -# (--source gitea: the Gitea release, off-estate), verifies, installs +# (DEPLOY-TOKEN header; --source gitea: the Gitea release, off-estate), verifies, installs # 3. enrols /opt/openziti/etc/identities/monky-host..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, @@ -21,22 +29,25 @@ set -euo pipefail umask 077 -DEFAULT_VERSION="0.1.1" -# Download source. PRIMARY is the public GitLab project's generic package registry on scm.tikali.ai: -# inside the estate gitea.cbs.tikali.net is split-horizon to jump1's RED EIP (10.10.0.175), which has -# no HTTP ingress, so backend boxes cannot reach the Gitea mirror (cbs/iac#102); scm.tikali.ai they can. -# `--source gitea` keeps the Gitea release as the off-estate alternative. --base-url / MONKY_DEPLOYD_BASE_URL -# override the base for whichever layout is selected. +DEFAULT_VERSION="0.1.2" +# Download source. PRIMARY is the GitLab project's generic package registry on scm.tikali.ai: inside +# the estate gitea.cbs.tikali.net is split-horizon to jump1's RED EIP (10.10.0.175), which has no HTTP +# ingress, so backend boxes cannot reach the Gitea mirror (cbs/iac#102); scm.tikali.ai they can. The +# project is PRIVATE (parent groups are private), so the registry needs the read-only deploy token +# (--token / MONKY_DEPLOYD_TOKEN, sent as the DEPLOY-TOKEN header). `--source gitea` keeps the Gitea +# release as the off-estate alternative (no token). --base-url / MONKY_DEPLOYD_BASE_URL override the +# base for whichever layout is selected. GITLAB_BASE_URL="https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd" GITEA_BASE_URL="https://gitea.cbs.tikali.net/mdella/monky-deployd" SOURCE="${MONKY_DEPLOYD_SOURCE:-gitlab}" BASE_URL="${MONKY_DEPLOYD_BASE_URL:-}" +TOKEN="${MONKY_DEPLOYD_TOKEN:-}" 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}"; } +usage() { if [ -f "$0" ]; then sed -n '2,28p' "$0"; else echo "monky-deployd install.sh — see README.md (Install)"; fi; exit "${1:-0}"; } die() { echo "install.sh: $*" >&2; exit 1; } log() { echo "==> $*"; } @@ -51,6 +62,7 @@ while [ $# -gt 0 ]; do --bao-ca) BAO_CA="$2"; shift 2 ;; --base-url) BASE_URL="$2"; shift 2 ;; --source) SOURCE="$2"; shift 2 ;; + --token) TOKEN="$2"; shift 2 ;; --laptop) LAPTOP="true"; shift ;; --no-run) NO_RUN=1; shift ;; --force-config) FORCE_CONFIG=1; shift ;; @@ -67,6 +79,9 @@ SITE="${SITE,,}" [[ "$SITE" =~ ^(cbs|pdx)$ ]] || die "site must be cbs|pdx" [[ "$TRANSPORT" =~ ^(sdk|proxy|system)$ ]] || die "transport must be sdk|proxy|system" [[ "$SOURCE" =~ ^(gitlab|gitea)$ ]] || die "source must be gitlab|gitea" +# never let the token leak through xtrace / the environment of children +{ set +x; } 2>/dev/null +export -n MONKY_DEPLOYD_TOKEN 2>/dev/null || true IDENTITY="$IDENTITY_DIR/monky-host.$ENV_ID.json" export DEBIAN_FRONTEND=noninteractive @@ -109,14 +124,23 @@ if [ "$installed" = "$VERSION" ]; then else tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT deb="monky-deployd_${VERSION}_amd64.deb" + # the deploy token travels in a 0600 curl config file (-K), never on the command line, so neither + # `ps` nor an xtrace shows it; the file dies with $tmp + curlrc="$tmp/curlrc"; : > "$curlrc"; chmod 0600 "$curlrc" if [ "$SOURCE" = gitlab ]; then url="${BASE_URL:-$GITLAB_BASE_URL}/${VERSION}" + if [ -n "$TOKEN" ]; then + printf 'header = "DEPLOY-TOKEN: %s"\n' "$TOKEN" > "$curlrc" + else + echo "WARNING: no --token/MONKY_DEPLOYD_TOKEN: the scm.tikali.ai project is private, the download will 401 (use --source gitea off-estate)" >&2 + fi else url="${BASE_URL:-$GITEA_BASE_URL}/releases/download/v${VERSION}" fi - log "downloading $deb from $url" - curl -fsSL -o "$tmp/$deb" "$url/$deb" - curl -fsSL -o "$tmp/$deb.sha256" "$url/$deb.sha256" + log "downloading $deb from $url${TOKEN:+ (DEPLOY-TOKEN)}" + curl -fsSL -K "$curlrc" -o "$tmp/$deb" "$url/$deb" || die "download of $deb failed (private registry: is the deploy token set and valid?)" + curl -fsSL -K "$curlrc" -o "$tmp/$deb.sha256" "$url/$deb.sha256" || die "download of $deb.sha256 failed" + rm -f "$curlrc" (cd "$tmp" && sha256sum -c "$deb.sha256") || die "sha256 mismatch on $deb" apt_update_once || true apt-get install -y -qq "$tmp/$deb" @@ -194,10 +218,11 @@ 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 +# bootstrap grant: --bootstrap-file, or stdin — but ONLY when this script runs from a file: with +# `curl … | bash -s --` stdin IS the script, so reading it here would swallow the rest of it grant="" if [ -n "$BOOTSTRAP_FILE" ]; then grant="$(tr -d '[:space:]' < "$BOOTSTRAP_FILE")" -elif [ ! -t 0 ]; then grant="$(tr -d '[:space:]' "$ETC/bootstrap.jwt" chown monky-deployd:monky-deployd "$ETC/bootstrap.jwt"; chmod 0600 "$ETC/bootstrap.jwt" @@ -207,7 +232,7 @@ elif [ -s /var/lib/monky-deployd/bao.token ]; then 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 +unset grant TOKEN # --- 5. timer + first tick ---------------------------------------------------------------------------------- systemctl daemon-reload diff --git a/pyproject.toml b/pyproject.toml index beaef8f..771c552 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "monky-deployd" -version = "0.1.1" +version = "0.1.2" description = "Monky backend pull agent: checkin -> bundle -> lease -> OpenBao -> docker compose -> report, over the ziti mesh (MONKY-ADR-0028)" readme = "README.md" requires-python = ">=3.12"