initial upload

This commit is contained in:
David Schroeder
2026-07-25 13:11:37 -05:00
commit 943ddb064f
25 changed files with 2586 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
# Proxmox client API
The ProxMenu client uses two HTTPS requests. It never receives Gitea
credentials.
## Exchange a deployment code
`POST https://tapm.scity.us/api/v1/exchange`
```json
{
"code": "TAPM-ABCDE-FGHIJ",
"host_fingerprint": "sha256-of-the-host-machine-id",
"hostname": "pve01"
}
```
Successful response:
```json
{
"session_token": "opaque-session-token",
"expires_at": "2026-07-25T23:30:00Z",
"packages": [
{
"slug": "sentinelone-linux",
"display_name": "SentinelOne Linux Agent",
"version": "26.1.1.31",
"sha256": "64-lowercase-hexadecimal-characters",
"download_url": "https://tapm.scity.us/api/v1/packages/sentinelone-linux"
}
]
}
```
The first exchange enrolls that host against the authorization. Reusing the
same code and host fingerprint does not consume another host slot. A different
fingerprint consumes one slot until the configured limit is reached.
## Download a package
Send the session from the exchange:
```sh
curl --fail --location \
--header "Authorization: Bearer SESSION_TOKEN" \
--output /tmp/package.deb \
'https://tapm.scity.us/api/v1/packages/sentinelone-linux'
```
The client must calculate SHA-256 after download and compare it to the value
returned by the exchange. It must delete the file and stop if the values differ.
The server also returns the expected value in `X-TAPM-SHA256`.
## Status behavior
- `400`: malformed request
- `403`: invalid, expired, revoked, or exhausted authorization
- `429`: too many failed exchanges from the source address; retry after the
`Retry-After` interval
- `502`: private package registry unavailable
Codes and sessions expire at the same authorization deadline. Revoking an
authorization immediately invalidates all of its sessions.
+153
View File
@@ -0,0 +1,153 @@
# Deployment
This service is intended to run on both webserver VMs, with only the active
webserver's broker container started. Both installations use the same MariaDB
Galera database, Gitea application, package registry, and secrets.
## 1. DNS and TLS
Create `tapm.scity.us` in PowerDNS and point it at the existing HAProxy
frontend. Install the certificate using the existing certificate automation.
The broker only listens on `127.0.0.1:8080`; Nginx terminates TLS.
An example virtual host is in
[`deploy/nginx/tapm.scity.us.conf`](../deploy/nginx/tapm.scity.us.conf).
Adjust only the certificate paths if the local convention differs.
## 2. MariaDB Galera
Create a dedicated database and least-privilege application user. Run this once
against the cluster as a database administrator:
```sql
CREATE DATABASE tapm_broker
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE USER 'tapm'@'10.%' IDENTIFIED BY 'replace-with-a-random-password';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, REFERENCES
ON tapm_broker.* TO 'tapm'@'10.%';
FLUSH PRIVILEGES;
```
Restrict the host pattern further if the two webserver addresses are known.
The application does not need global privileges.
## 3. Gitea accounts and OAuth
Create these separately:
1. The `taiadmin` technician account used to sign in to the portal.
2. A `tapm-packages` service account with read access only to the private
`TAI/files` package owner. Create a personal access token for this account
and store it only in the broker environment file.
3. An OAuth2 application with callback URL
`https://tapm.scity.us/auth/callback`.
The OAuth client secret authenticates the portal to Gitea. The package token
is used only server-side while proxying an authorized file. Neither value is
returned to a technician or Proxmox host.
## 4. Broker configuration
On each webserver:
```sh
git clone https://git.scity.us/TAI/TA-Deployment-Broker.git
cd TA-Deployment-Broker
cp .env.example .env
chmod 600 .env
```
Edit `.env` and use the same values on both webservers. Generate the cookie
secret with:
```sh
openssl rand -base64 48
```
The database DSN must retain `parseTime=true`, `multiStatements=true`, and the
encoded UTC `time_zone` setting from `.env.example`.
Set the Galera address to an existing database VIP or load-balanced endpoint,
not a single cluster member.
## 5. Build and migrate
Build the image on each webserver:
```sh
docker compose build
```
Apply migrations from only one webserver:
```sh
docker compose run --rm migrate
```
Migrations use a MariaDB advisory lock, so an accidental simultaneous run
cannot apply the same migration twice.
## 6. Start the active instance
On the active webserver:
```sh
docker compose up -d broker
curl --fail http://127.0.0.1:8080/health/ready
```
On the standby webserver, keep the image and `.env` current but leave the
broker stopped. The existing failover automation can run:
```sh
docker compose stop broker
```
on the old active VM, followed by:
```sh
docker compose up -d broker
```
on the new active VM. There is no local application state to transfer.
## 7. HAProxy health check
The load balancer should consider an origin healthy only when
`GET /health/ready` returns HTTP 200. `/health/live` checks only the process;
`/health/ready` also verifies Galera connectivity.
Only the active webserver should be eligible for traffic. Retaining a single
active origin avoids duplicate service management while Galera preserves
sessions and authorization state across failover.
## 8. Publish a protected package
Publish files to the private Gitea Generic Package Registry:
```sh
curl --user 'publisher:PACKAGE_WRITE_TOKEN' \
--upload-file ./SentinelAgent_linux_x86_64.deb \
'https://git.scity.us/api/packages/TAI/generic/sentinelone-linux/26.1.1.31/SentinelAgent_linux_x86_64.deb'
```
Calculate its checksum:
```sh
sha256sum SentinelAgent_linux_x86_64.deb
```
Sign in at `https://tapm.scity.us`, enter the matching registry package name,
version, filename, and SHA-256, then enable it. The broker intentionally does
not browse the registry with the technician's credentials.
## 9. Backup and rotation
- Include `tapm_broker` in the existing Galera backup policy.
- Keep `.env` outside Git and readable only by the service administrator.
- Rotate the Gitea package token and OAuth secret if either webserver is
compromised.
- Changing `TAPM_COOKIE_SECRET` signs out portal users; it does not invalidate
active deployment codes.
- Revoke active deployment authorizations from the portal when a deployment
ends early.