update gui

This commit is contained in:
2026-07-28 21:11:54 -05:00
parent 6c4ce8f6df
commit 555aae0ab2
12 changed files with 71 additions and 58 deletions
+6 -6
View File
@@ -2,13 +2,13 @@
TAPM Deployment Access is a short-lived authorization broker for protected
deployment packages and installer actions. Technicians authenticate through
the bundled Gitea service, create deployment codes, and authorize a limited
the bundled Git service, create deployment codes, and authorize a limited
number of hosts for selected capabilities.
The production layout is a self-contained, single-VM Docker deployment:
- Go broker with a persistent embedded SQLite database
- rootless Gitea with SQLite, repositories, and package registry
- rootless Git service with SQLite, repositories, and package registry
- Nginx reverse proxy for both public domains
- Certbot and Let's Encrypt HTTP-01 certificate management
- explicit runtime storage under `config/` beside the Compose files
@@ -19,7 +19,7 @@ needs to forward public TCP 80 and 443 to the configured Docker VM ports.
All services are managed by one Compose project while remaining separate
containers for independent health checks and upgrades.
See [docs/deployment.md](docs/deployment.md) for the installation, Gitea setup,
See [docs/deployment.md](docs/deployment.md) for the installation, Git setup,
TLS bootstrap, repository move, renewal, backup, and update procedures. The
ProxMenu integration contract is in [docs/client-api.md](docs/client-api.md).
@@ -32,9 +32,9 @@ After cloning the repository, run:
```
The installer defaults to `SSL_MODE=none`, writes a mode-`0600` `.env`, creates
the runtime directories and Docker network, and starts Gitea behind the
HTTP-only Nginx test configuration. It does not invent Gitea OAuth or package
credentials. After creating those resources in Gitea, run
the runtime directories and Docker network, and starts Git behind the
HTTP-only Nginx test configuration. It does not invent Git OAuth or package
credentials. After creating those resources in Git, run
`./manage.sh configure-broker` and `./manage.sh start`.
New deployment codes default to seven days. While a code is active, the Codes
+1 -1
View File
@@ -40,7 +40,7 @@ server {
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
location / {
# Resolve at request time so Gitea-only bootstrap works before the
# Resolve at request time so Git-only bootstrap works before the
# broker container exists. Requests return 502 until broker setup ends.
set $broker_backend_url http://broker:8080;
proxy_pass $broker_backend_url;
+1 -1
View File
@@ -1,6 +1,6 @@
# Proxmox client API
The ProxMenu client uses two HTTPS requests. It never receives Gitea
The ProxMenu client uses two HTTPS requests. It never receives Git
credentials.
## Exchange a deployment code
+26 -26
View File
@@ -7,8 +7,8 @@ VM. `/opt/idssys/TA-Deployment-Access` is only one possible example.
One Compose project manages:
- TAPM broker, with an embedded SQLite database
- Gitea, with its own embedded SQLite database and repository storage
- Nginx, terminating TLS for the broker and Gitea
- Git, with its own embedded SQLite database and repository storage
- Nginx, terminating TLS for the broker and Git
- Certbot, as an on-demand utility for certificate issuance and renewal
The same deployment supports an explicitly insecure HTTP testing phase before
@@ -18,9 +18,9 @@ runtime data.
In production, only the configured Nginx HTTP and HTTPS ports are published.
They default to 8680 and 8643. Inside Docker, Nginx listens on ports 80 and 443,
the broker on 8080, and Gitea is explicitly pinned to `0.0.0.0:3000`. Gitea
the broker on 8080, and Git is explicitly pinned to `0.0.0.0:3000`. Git
SSH is disabled. Deployment-level variables such as `HTTP_PORT` are not passed
into the Gitea container, preventing them from overriding its internal listener.
into the Git container, preventing them from overriding its internal listener.
## 1. VM and network
@@ -38,7 +38,7 @@ the required forwarded ports. Nginx supplies the application-edge controls:
unknown HTTP hostnames are dropped, unknown TLS SNI names are rejected,
TLS is restricted to 1.2/1.3, session tickets and version disclosure are
disabled, broker requests and per-address connections are limited, slow-client
timeouts are bounded, Gitea login bursts are throttled, and HSTS plus browser
timeouts are bounded, Git login bursts are throttled, and HSTS plus browser
security headers are returned. Container PID ceilings and Docker log rotation
reduce the impact of local resource exhaustion.
Nginx also runs with a read-only container filesystem and narrowly scoped
@@ -50,7 +50,7 @@ forwarding headers.
```sh
sudo mkdir -p /YOUR/INSTALL/PARENT
sudo git clone GITEA-REPOSITORY-URL /YOUR/INSTALL/PARENT/TA-Deployment-Access
sudo git clone GIT-REPOSITORY-URL /YOUR/INSTALL/PARENT/TA-Deployment-Access
sudo chown -R 1000:1000 /YOUR/INSTALL/PARENT/TA-Deployment-Access
cd /YOUR/INSTALL/PARENT/TA-Deployment-Access
./install.sh
@@ -58,7 +58,7 @@ cd /YOUR/INSTALL/PARENT/TA-Deployment-Access
The guided installer validates both hostnames and Docker ports, generates the
broker cookie secret, writes `.env` with mode `0600`, prepares persistent
storage, and starts only Gitea and Nginx. It defaults to `SSL_MODE=none`, so no
storage, and starts only Git and Nginx. It defaults to `SSL_MODE=none`, so no
certificate or public port forwarding is required.
With the default `HTTP_PORT=8680`, test URLs include that port:
@@ -82,7 +82,7 @@ Runtime state is deliberately visible below the checkout:
config/
├── broker/ # tapm.db and SQLite WAL files
├── gitea/
│ ├── config/ # app.ini and Gitea configuration
│ ├── config/ # app.ini and Git configuration
│ └── data/ # repositories, packages, and gitea.db
├── letsencrypt/ # account data, certificate, and private key
├── ssl/ # optional administrator-provided certificate
@@ -92,15 +92,15 @@ config/
The contents are ignored by Git. They must never be committed.
## 3. Configure Gitea and the broker
## 3. Configure Git and the broker
The installer has already bootstrapped Gitea. Create the initial administrator:
The installer has already bootstrapped Git. Create the initial administrator:
```sh
./manage.sh create-admin
```
Sign in at the Gitea URL printed by the command and change the temporary
Sign in at the Git URL printed by the command and change the temporary
password. Create the `TAI` organization, then create:
1. `tapm-packages`, with a read-only package token.
@@ -116,13 +116,13 @@ Save the resulting values without echoing secrets to the terminal:
```
The broker is deliberately not started during the first bootstrap. Blank
OAuth/package values are valid for Gitea-only setup, while `start` refuses to
OAuth/package values are valid for Git-only setup, while `start` refuses to
launch the broker until all required credentials are present.
`TAPM_GITEA_URL` is the public browser-facing Gitea URL.
`TAPM_GITEA_URL` is the public browser-facing Git URL.
`TAPM_GITEA_INTERNAL_URL` defaults to `http://gitea:3000` in installations so
OAuth token exchange and package traffic stay on the private Compose network.
Do not publish Gitea's internal port to make broker authentication work.
Do not publish Git's internal port to make broker authentication work.
Deployment codes default to seven days:
@@ -141,7 +141,7 @@ For troubleshooting, use `./manage.sh diagnostics`. Do not share unredacted
`docker compose config` output: Compose resolves the broker's `.env` file and
can print cookie, OAuth, and package secrets. If configuration output is
accidentally disclosed, rotate the cookie secret with
`./manage.sh rotate-cookie-secret` and replace any configured Gitea tokens or
`./manage.sh rotate-cookie-secret` and replace any configured Git tokens or
OAuth secret that appeared.
## 4. Enable TLS when forwarding is ready
@@ -157,7 +157,7 @@ After public DNS resolves and the datacenter edge forwards public TCP 80 and
`set-ssl` changes all public URLs to HTTPS and removes the temporary `:8680`
test port. `bootstrap` obtains the certificate and switches Nginx to TLS.
`start` recreates Gitea and the broker with secure cookies. Existing SQLite
`start` recreates Git and the broker with secure cookies. Existing SQLite
databases, repositories, packages, installation identities, and secrets remain
in place.
@@ -188,7 +188,7 @@ curl --fail "http://GITEA_DOMAIN:HTTP_PORT/api/healthz" # SSL_MODE=none
```
The broker automatically applies SQLite schema migrations before starting.
Gitea uses its own SQLite database at
Git uses its own SQLite database at
`config/gitea/data/data/gitea.db`.
After TLS is enabled, use the equivalent `https://` URLs without the temporary
HTTP test port.
@@ -196,7 +196,7 @@ HTTP test port.
### Final identity hardening
After public DNS, WAN TCP 80/443 forwarding, and the Let's Encrypt certificate
are working, configure Microsoft Entra ID as Gitea's OpenID Connect
are working, configure Microsoft Entra ID as Git's OpenID Connect
authentication source. Register the final callback URL:
```text
@@ -204,9 +204,9 @@ https://GITEA_DOMAIN/user/oauth2/entra/callback
```
Require assignment to the Entra enterprise application, apply the company's
Duo-backed Conditional Access policy, and map the Entra `Gitea.User` and
`Gitea.Admin` application roles into Gitea. Verify an Entra administrator can
sign in and reach the broker before disabling Gitea's local password sign-in
Duo-backed Conditional Access policy, and map the Entra `Git.User` and
`Git.Admin` application roles into Git. Verify an Entra administrator can
sign in and reach the broker before disabling Git's local password sign-in
form. Keep a documented VM-console recovery procedure and retain the package
reader and publisher as non-admin service accounts with narrowly scoped
tokens.
@@ -227,8 +227,8 @@ Then run:
./manage.sh start-direct
```
This adds host mappings to the broker's port 8080 and Gitea's port 3000. It
also changes Gitea's root URL and session cookie policy to permit HTTP login.
This adds host mappings to the broker's port 8080 and Git's port 3000. It
also changes Git's root URL and session cookie policy to permit HTTP login.
The direct broker port is suitable for health and API testing; technician
OAuth login remains designed for the HTTPS public URL. Do not use the
direct-port override on an Internet-facing production VM. Return to the
@@ -241,7 +241,7 @@ production URL and secure-cookie policy with:
## 6. Move repositories
For each existing repository, create an empty matching repository in the new
Gitea and mirror all refs:
Git and mirror all refs:
```sh
git clone --mirror OLD-REPOSITORY-URL
@@ -280,10 +280,10 @@ cd /YOUR/INSTALL/PARENT/TA-Deployment-Access
```
The updater operates only on the current VM. It fast-forwards the checked-out
branch, pulls the pinned Gitea and Nginx images, rebuilds and restarts the local
branch, pulls the pinned Git and Nginx images, rebuilds and restarts the local
stack, and waits for the local broker to become healthy. Certbot is not pulled
or run during a routine update; it is used only by `bootstrap` and `renew`. The
updater has no peer discovery, SSH, or multi-node update behavior.
Pin image versions as supplied and review release notes before changing them.
Never change Gitea between rootless and rootful image families in place.
Never change Git between rootless and rootful image families in place.
+9 -9
View File
@@ -52,15 +52,15 @@ docker info >/dev/null 2>&1 ||
fail "the Docker daemon is unavailable or this user cannot access it"
printf '\nTAPM Deployment Access installer\n'
printf 'This creates the initial environment and starts Gitea behind Nginx.\n'
printf 'The broker starts only after its Gitea OAuth and package credentials are configured.\n\n'
printf 'This creates the initial environment and starts Git behind Nginx.\n'
printf 'The broker starts only after its Git OAuth and package credentials are configured.\n\n'
broker_domain="$(prompt_default 'Broker hostname' 'tapm.example.com')"
gitea_domain="$(prompt_default 'Gitea hostname' 'git.example.com')"
gitea_domain="$(prompt_default 'Git hostname' 'git.example.com')"
valid_hostname "$broker_domain" || fail "invalid broker hostname"
valid_hostname "$gitea_domain" || fail "invalid Gitea hostname"
valid_hostname "$gitea_domain" || fail "invalid Git hostname"
[[ "$broker_domain" != "$gitea_domain" ]] ||
fail "broker and Gitea hostnames must be different"
fail "broker and Git hostnames must be different"
http_port="$(prompt_default 'VM port mapped to Nginx HTTP port 80' '8680')"
https_port="$(prompt_default 'VM port mapped to Nginx HTTPS port 443' '8643')"
@@ -136,20 +136,20 @@ chmod 600 .env
printf '\nInitial configuration saved to %s/.env.\n' "$DEPLOY_ROOT"
if [[ "$ssl_mode" == "custom" ]]; then
printf 'Place the certificate and key in config/ssl, then run ./manage.sh bootstrap.\n'
printf 'Gitea has not been started yet.\n'
printf 'Git has not been started yet.\n'
exit 0
fi
"${DEPLOY_ROOT}/manage.sh" bootstrap
printf '\nInitial Gitea deployment is running.\n'
printf 'Gitea URL: %s://%s%s\n' "$public_scheme" "$gitea_domain" "$public_port_suffix"
printf '\nInitial Git deployment is running.\n'
printf 'Git URL: %s://%s%s\n' "$public_scheme" "$gitea_domain" "$public_port_suffix"
if [[ "$ssl_mode" == "none" ]]; then
printf 'This is HTTP-only testing mode. Do not expose it publicly.\n'
printf 'Make both hostnames resolve to this VM or its test-forwarding address.\n'
fi
printf '\nNext steps:\n'
printf ' 1. Run ./manage.sh create-admin\n'
printf ' 2. Sign in to Gitea and create the organization, package users/tokens,\n'
printf ' 2. Sign in to Git and create the organization, package users/tokens,\n'
printf ' and OAuth application documented in docs/deployment.md.\n'
printf ' 3. Run ./manage.sh configure-broker\n'
printf ' 4. Run ./manage.sh start\n'
+3 -3
View File
@@ -91,13 +91,13 @@ func (s *Server) handleCallback(w http.ResponseWriter, r *http.Request) {
user, err := s.exchangeOAuthCode(r.Context(), code)
if err != nil {
log.Printf("Gitea OAuth code exchange failed: %v", err)
http.Error(w, "Gitea sign-in failed", http.StatusBadGateway)
log.Printf("Git OAuth code exchange failed: %v", err)
http.Error(w, "Git sign-in failed", http.StatusBadGateway)
return
}
if _, allowed := s.cfg.AllowedGiteaUsers[strings.ToLower(user.Login)]; !allowed {
_ = s.audit(r.Context(), "login_denied", user.Login, nil, "", "", s.clientIP(r), "user is not allowed")
http.Error(w, "this Gitea user is not authorized", http.StatusForbidden)
http.Error(w, "this Git user is not authorized", http.StatusForbidden)
return
}
+1 -1
View File
@@ -139,7 +139,7 @@ func LoadConfig() (Config, error) {
return cfg, fmt.Errorf("TAPM_COOKIE_SECRET must contain at least 32 bytes")
}
if len(cfg.AllowedGiteaUsers) == 0 {
return cfg, fmt.Errorf("at least one Gitea user must be allowed")
return cfg, fmt.Errorf("at least one Git user must be allowed")
}
return cfg, nil
+14 -1
View File
@@ -247,7 +247,20 @@ dd { margin: 4px 0 0; overflow-wrap: anywhere; }
.package-row { display: flex; justify-content: space-between; gap: 16px; padding: 14px 0; border-bottom: 1px solid var(--line); }
.package-row div { display: grid; gap: 4px; }
.package-row small { color: var(--muted); }
.package-row .status { align-items: center; justify-content: center; align-self: center; line-height: 1; text-align: center; }
.package-row .status {
align-items: center;
justify-content: center;
align-self: center;
min-width: 88px;
padding: 7px 12px;
line-height: 1;
text-align: center;
}
.package-row .status:not(.enabled) {
color: var(--danger);
border-color: rgba(181, 59, 51, .38);
background: rgba(181, 59, 51, .1);
}
.package-update-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.toggle { display: flex; align-items: center; gap: 9px; }
.empty, .fine-print { color: var(--muted); }
+1 -1
View File
@@ -124,7 +124,7 @@ func (s *Server) handleUploadPackage(w http.ResponseWriter, r *http.Request) {
if target.Replacing {
if err := s.deletePackageVersion(r, target.PreviousName, target.PreviousVersion); err != nil {
warning = fmt.Sprintf(
"Package updated, but Gitea could not remove superseded version %s: %s",
"Package updated, but Git could not remove superseded version %s: %s",
target.PreviousVersion,
err,
)
+7 -7
View File
@@ -244,10 +244,10 @@ create-admin)
require_env
command -v openssl >/dev/null 2>&1 ||
fail "openssl is required to generate the temporary password"
printf 'Gitea administrator username [taiadmin]: '
printf 'Git administrator username [taiadmin]: '
read -r admin_username
admin_username="${admin_username:-taiadmin}"
printf 'Gitea administrator email: '
printf 'Git administrator email: '
read -r admin_email
[[ -n "$admin_email" ]] || fail "an administrator email is required"
temporary_password="$(openssl rand -base64 24)"
@@ -257,7 +257,7 @@ create-admin)
--email "$admin_email" \
--password "$temporary_password" \
--must-change-password
printf '\nGitea administrator created.\n'
printf '\nGit administrator created.\n'
printf 'Username: %s\n' "$admin_username"
printf 'Temporary password: %s\n' "$temporary_password"
printf 'Sign in at %s and change the password immediately.\n' \
@@ -267,12 +267,12 @@ configure-broker)
require_env
command -v openssl >/dev/null 2>&1 ||
fail "openssl is required to generate the cookie secret"
printf 'Create the Gitea OAuth application with this callback URL:\n'
printf 'Create the Git OAuth application with this callback URL:\n'
printf ' %s/auth/callback\n\n' "$(read_env_setting TAPM_PUBLIC_URL)"
printf 'Gitea OAuth client ID: '
printf 'Git OAuth client ID: '
read -r oauth_client_id
[[ -n "$oauth_client_id" ]] || fail "OAuth client ID is required"
printf 'Gitea OAuth client secret: '
printf 'Git OAuth client secret: '
read -r -s oauth_client_secret
printf '\n'
[[ -n "$oauth_client_secret" ]] || fail "OAuth client secret is required"
@@ -290,7 +290,7 @@ configure-broker)
read -r -s publisher_token
printf '\n'
[[ -n "$publisher_token" ]] || fail "publisher token is required"
printf 'Allowed Gitea users [taiadmin]: '
printf 'Allowed Git users [taiadmin]: '
read -r allowed_users
allowed_users="${allowed_users:-taiadmin}"
cookie_secret="$(read_env_setting TAPM_COOKIE_SECRET)"
+1 -1
View File
@@ -96,7 +96,7 @@ fi
}
if "${deployment}/manage.sh" start >/dev/null 2>&1; then
printf 'FAIL: broker started without Gitea credentials\n' >&2
printf 'FAIL: broker started without Git credentials\n' >&2
exit 1
fi
+1 -1
View File
@@ -53,7 +53,7 @@ if ((post_pull == 0)); then
fi
fi
printf 'Updating the local Gitea image and deployment stack...\n'
printf 'Updating the local Git image and deployment stack...\n'
docker compose --env-file .env pull gitea nginx
"${DEPLOY_ROOT}/manage.sh" start