290 lines
11 KiB
Markdown
290 lines
11 KiB
Markdown
# Single-VM deployment
|
|
|
|
The deployment is relocatable. Compose paths and management commands are
|
|
resolved relative to the repository checkout, so it can live anywhere on the
|
|
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
|
|
- Certbot, as an on-demand utility for certificate issuance and renewal
|
|
|
|
The same deployment supports an explicitly insecure HTTP testing phase before
|
|
public DNS and port forwarding are ready. HTTP mode changes only routing, URL
|
|
schemes, and cookie security; it does not create separate databases or move
|
|
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
|
|
SSH is disabled. Deployment-level variables such as `HTTP_PORT` are not passed
|
|
into the Gitea container, preventing them from overriding its internal listener.
|
|
|
|
## 1. VM and network
|
|
|
|
Install Docker Engine with Compose v2. Permit the selected HTTP and HTTPS ports
|
|
and forward public TCP 80 and 443 from the datacenter edge to them. Create
|
|
public DNS A/AAAA records for both application names before requesting the
|
|
certificate.
|
|
|
|
Public port 80 must reach the container's port 80 for HTTP-01 certificate
|
|
renewal. For example, if `HTTP_PORT=8080`, the edge must forward public port 80
|
|
to VM port 8080. If IPv6 is published, it must reach this same VM.
|
|
|
|
The company firewall should provide volumetric DDoS protection and permit only
|
|
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
|
|
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
|
|
temporary filesystems. The broker trusts proxy headers because Nginx is its
|
|
only production ingress; Nginx replaces rather than appends client-supplied
|
|
forwarding headers.
|
|
|
|
## 2. Install the repository
|
|
|
|
```sh
|
|
sudo mkdir -p /YOUR/INSTALL/PARENT
|
|
sudo git clone GITEA-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
|
|
```
|
|
|
|
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
|
|
certificate or public port forwarding is required.
|
|
|
|
With the default `HTTP_PORT=8680`, test URLs include that port:
|
|
|
|
```sh
|
|
http://BROKER_DOMAIN:8680
|
|
http://GITEA_DOMAIN:8680
|
|
```
|
|
|
|
Both names must resolve to the Docker VM from the test workstation. Temporary
|
|
hosts-file records are sufficient. Do not forward this HTTP test listener to
|
|
the public Internet.
|
|
|
|
For a manual installation instead, copy `.env.example` to `.env`, replace its
|
|
example hostnames, generate `TAPM_COOKIE_SECRET` with
|
|
`openssl rand -base64 48`, and run `./manage.sh bootstrap`.
|
|
|
|
Runtime state is deliberately visible below the checkout:
|
|
|
|
```text
|
|
config/
|
|
├── broker/ # tapm.db and SQLite WAL files
|
|
├── gitea/
|
|
│ ├── config/ # app.ini and Gitea configuration
|
|
│ └── data/ # repositories, packages, and gitea.db
|
|
├── letsencrypt/ # account data, certificate, and private key
|
|
├── ssl/ # optional administrator-provided certificate
|
|
├── certbot-webroot/ # HTTP-01 challenge files
|
|
└── backups/ # local offline snapshots
|
|
```
|
|
|
|
The contents are ignored by Git. They must never be committed.
|
|
|
|
## 3. Configure Gitea and the broker
|
|
|
|
The installer has already bootstrapped Gitea. Create the initial administrator:
|
|
|
|
```sh
|
|
./manage.sh create-admin
|
|
```
|
|
|
|
Sign in at the Gitea 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.
|
|
2. `tapm-publisher`, with a write package token.
|
|
3. An OAuth2 application. In HTTP testing mode its callback is
|
|
`http://BROKER_DOMAIN:HTTP_PORT/auth/callback`.
|
|
|
|
Save the resulting values without echoing secrets to the terminal:
|
|
|
|
```sh
|
|
./manage.sh configure-broker
|
|
./manage.sh start
|
|
```
|
|
|
|
The broker is deliberately not started during the first bootstrap. Blank
|
|
OAuth/package values are valid for Gitea-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_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.
|
|
|
|
Deployment codes default to seven days:
|
|
|
|
```dotenv
|
|
TAPM_DEFAULT_DURATION=7d
|
|
```
|
|
|
|
The setting accepts day values such as `7d` and legacy Go duration values such
|
|
as `168h`. Existing deployments retain their current `.env`; change a previous
|
|
`TAPM_DEFAULT_DURATION=3h` value to `7d` and run `./manage.sh start` to adopt
|
|
the new default. Individual active codes can be extended from the Codes page.
|
|
The same editor can change the maximum host count and add or remove protected
|
|
packages and installer actions without issuing a replacement code.
|
|
|
|
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
|
|
OAuth secret that appeared.
|
|
|
|
## 4. Enable TLS when forwarding is ready
|
|
|
|
After public DNS resolves and the datacenter edge forwards public TCP 80 and
|
|
443 to the configured VM ports, run:
|
|
|
|
```sh
|
|
./manage.sh set-ssl letsencrypt
|
|
./manage.sh bootstrap
|
|
./manage.sh start
|
|
```
|
|
|
|
`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
|
|
databases, repositories, packages, installation identities, and secrets remain
|
|
in place.
|
|
|
|
For an administrator-provided certificate instead, place the certificate and
|
|
key in `config/ssl/` and set:
|
|
|
|
```dotenv
|
|
SSL_MODE=custom
|
|
SSL_CERTIFICATE_DIR=./config/ssl
|
|
SSL_CERTIFICATE_FILE=fullchain.pem
|
|
SSL_CERTIFICATE_KEY_FILE=privkey.pem
|
|
```
|
|
|
|
The certificate must cover both `BROKER_DOMAIN` and `GITEA_DOMAIN`. Run
|
|
`./manage.sh set-ssl custom` before bootstrap. With custom
|
|
mode, `bootstrap` skips Certbot and `renew` is intentionally unavailable;
|
|
replace the files through the certificate provider's process and restart or
|
|
reload Nginx. `SSL_CERTIFICATE_DIR` can point at another directory relative to
|
|
the repository or at an absolute host path.
|
|
|
|
## 5. Start and inspect the complete deployment
|
|
|
|
```sh
|
|
./manage.sh start
|
|
./manage.sh status
|
|
curl --fail "http://BROKER_DOMAIN:HTTP_PORT/health/ready" # SSL_MODE=none
|
|
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
|
|
`config/gitea/data/data/gitea.db`.
|
|
After TLS is enabled, use the equivalent `https://` URLs without the temporary
|
|
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
|
|
authentication source. Register the final callback URL:
|
|
|
|
```text
|
|
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
|
|
form. Keep a documented VM-console recovery procedure and retain the package
|
|
reader and publisher as non-admin service accounts with narrowly scoped
|
|
tokens.
|
|
|
|
For local testing that bypasses Nginx, set `BROKER_DIRECT_PORT` and
|
|
`GITEA_DIRECT_PORT` in `.env`. Set `GITEA_DIRECT_ROOT_URL` to the exact URL the
|
|
test browser will use, including the VM hostname or IP address and port:
|
|
|
|
```dotenv
|
|
BROKER_DIRECT_PORT=8080
|
|
GITEA_DIRECT_PORT=3000
|
|
GITEA_DIRECT_ROOT_URL=http://192.0.2.10:3000/
|
|
```
|
|
|
|
Then run:
|
|
|
|
```sh
|
|
./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.
|
|
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
|
|
production URL and secure-cookie policy with:
|
|
|
|
```sh
|
|
./manage.sh start
|
|
```
|
|
|
|
## 6. Move repositories
|
|
|
|
For each existing repository, create an empty matching repository in the new
|
|
Gitea and mirror all refs:
|
|
|
|
```sh
|
|
git clone --mirror OLD-REPOSITORY-URL
|
|
cd REPOSITORY.git
|
|
git push --mirror https://GITEA_DOMAIN/TAI/REPOSITORY.git
|
|
```
|
|
|
|
Update developer remotes, CI credentials, submodules, documentation, and the Go
|
|
module path only if the hostname embedded in the module path is also changing.
|
|
Move the deployment repository last so this checkout remains updateable during
|
|
the transition.
|
|
|
|
## 7. Renewal and backups
|
|
|
|
When `SSL_MODE=letsencrypt`, run renewal twice daily from root's crontab:
|
|
|
|
```cron
|
|
17 3,15 * * * cd /YOUR/INSTALL/PARENT/TA-Deployment-Access && ./manage.sh renew
|
|
```
|
|
|
|
Create an application-consistent local snapshot with:
|
|
|
|
```sh
|
|
./manage.sh backup
|
|
```
|
|
|
|
The backup briefly stops both SQLite writers. Copy `config/backups/` to storage
|
|
outside the VM. A backup left only on this VM does not protect against VM or
|
|
datacenter loss. Also back up `.env` through a secrets-aware system.
|
|
|
|
## 8. Updates
|
|
|
|
```sh
|
|
cd /YOUR/INSTALL/PARENT/TA-Deployment-Access
|
|
./update.sh
|
|
```
|
|
|
|
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
|
|
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.
|