Prepare public repository privacy and trusted runner gates

This commit is contained in:
Pigway
2026-10-11 01:13:55 +08:00
parent 9c9697dc6e
commit c9c950de1b
6 changed files with 73 additions and 17 deletions
+8 -6
View File
@@ -11,6 +11,8 @@ on:
default: plan
jobs:
verify:
# The NAS Docker runner must never execute code from external forks.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
env:
DOCKER_PIP_INDEX_URL: ${{ vars.DOCKER_PIP_INDEX_URL }}
@@ -56,12 +58,12 @@ jobs:
PROD_PROJECT: ${{ vars.PROD_PROJECT }}
PROD_CONTAINER: ${{ vars.PROD_CONTAINER }}
PROD_IMAGE_REPO: ${{ vars.PROD_IMAGE_REPO }}
PROD_DATA_DIR: ${{ vars.PROD_DATA_DIR }}
PROD_CONFIG_DIR: ${{ vars.PROD_CONFIG_DIR }}
PROD_PHOTOS_DIR: ${{ vars.PROD_PHOTOS_DIR }}
SYNOLOGY_PHOTOS_URL: ${{ vars.SYNOLOGY_PHOTOS_URL }}
SYNOLOGY_PHOTOS_CERT_SHA256: ${{ vars.SYNOLOGY_PHOTOS_CERT_SHA256 }}
PROD_WEB_BIND: ${{ vars.PROD_WEB_BIND }}
PROD_DATA_DIR: ${{ secrets.PROD_DATA_DIR }}
PROD_CONFIG_DIR: ${{ secrets.PROD_CONFIG_DIR }}
PROD_PHOTOS_DIR: ${{ secrets.PROD_PHOTOS_DIR }}
SYNOLOGY_PHOTOS_URL: ${{ secrets.SYNOLOGY_PHOTOS_URL }}
SYNOLOGY_PHOTOS_CERT_SHA256: ${{ secrets.SYNOLOGY_PHOTOS_CERT_SHA256 }}
PROD_WEB_BIND: ${{ secrets.PROD_WEB_BIND }}
PROD_WEB_PORT: ${{ vars.PROD_WEB_PORT }}
PROD_STATE_VOLUME: ${{ vars.PROD_STATE_VOLUME }}
PROD_MOCK: ${{ vars.PROD_MOCK }}
+1 -1
View File
@@ -174,7 +174,7 @@ to 25M pixels/20 MiB. This release uses a single worker and is not multi-replica
Gallery reads have no pagination because upstream documents none. Web UI hides
secrets and lazy-loads thumbnails; generating a first thumbnail fetches one original.
Scoped client provisioning and bounded signed webhook delivery are available in Connections. Docker build/NAS
status is recorded honestly in docs/ACCEPTANCE.md.
results should be checked in the Actions run for the relevant commit.
## Roadmap
+20
View File
@@ -0,0 +1,20 @@
# Public repository and deployment privacy
Runtime keys, passwords, sessions, private photos, databases and local `.env` files
must remain outside Git. Examples use documentation-only addresses and identities.
Personal deployment reports and screenshots are excluded from public history.
The NAS Docker runner accepts trusted repository events only. External fork PRs
are not executed on this runner; maintainers must review them before copying changes
to a trusted branch. All lint, type, test, audit and isolated Docker checks remain.
Private deployment configuration is stored in Gitea Actions Secrets:
`PROD_DATA_DIR`, `PROD_CONFIG_DIR`, `PROD_PHOTOS_DIR`, `PROD_WEB_BIND`,
`SYNOLOGY_PHOTOS_URL`, `SYNOLOGY_PHOTOS_CERT_SHA256`, and `PRODUCTION_ENV`.
Project/image names, ports, enable flags and mirror settings remain Variables.
Never print complete Docker inspect environment or secret values.
Publishing requires scanning all refs, removing private historic files, pruning
unreachable remote Git objects and archiving/removing historical Actions logs.
A private original mirror is retained outside the repository. Existing clones must
reclone after the privacy history rewrite; do not merge the original history back.
+9 -9
View File
@@ -9,8 +9,8 @@ host bind/port. SQLite/images/generated/cache live under `/data`; accounts,
vendor sessions and configuration live under `/config`. Neither is disposable.
The optional photos mount is read-only and has supplementary group access.
Historical repository receipts mention `/volume2/docker/pigway-frame-flow/data`,
`/volume2/docker/pigway-frame-flow/config`, `/volume1/photo`, port 18787, and
Historical repository receipts mention `/volume1/docker/pigway-frame-flow/data`,
`/volume1/docker/pigway-frame-flow/config`, `/volume1/photo`, port 18787, and
container/project `pigway-frame-flow`. These are **confirmation candidates**, not
script defaults. The deploy action accepts only explicitly configured paths
that match the existing running container on the Runner's Docker daemon.
@@ -24,7 +24,7 @@ configured names and existing bind mounts, with `create_host_path: false`.
## Gitea configuration
Repository Settings → Actions → Variables (ordinary configuration):
Repository Settings → Actions configuration (private paths/addresses use Secrets):
| Variable | Value to explicitly confirm |
| --- | --- |
@@ -75,7 +75,7 @@ Never upload that journal or a full Docker inspect as a public CI artifact.
docker inspect pigway-frame-flow --format '{{json .Mounts}}'
docker inspect pigway-frame-flow --format '{{json .HostConfig.PortBindings}} {{.HostConfig.NetworkMode}}'
```
Confirm the Variables against these actual values, including photos.
Confirm the Variables/Secrets against these actual values, including photos.
2. Configure the Secret privately. Keep `PROD_DEPLOY_ENABLED=false`.
3. Manually run **Verify Frame Flow** on `main`, `deploy_action=plan`.
Verify must pass first. Plan checks resource identity, paths/port, required
@@ -174,15 +174,15 @@ The confirmed NAS residue is a `created`, never-started container with dead
`/workspace/way/...` bind paths. It is not an adoption/rollback baseline.
No migration from those paths is performed. Production Compose remains independently usable.
Set these repository Actions **Variables** (not Secrets):
Set the ordinary repository Actions Variables below; set paths and addresses as Secrets (see docs/PUBLICATION.md):
```text
PROD_DEPLOY_ENABLED=true
PROD_PROJECT=pigway-frame-flow
PROD_CONTAINER=pigway-frame-flow
PROD_IMAGE_REPO=pigway-frame-flow
PROD_DATA_DIR=/volume2/docker/pigway-frame-flow/data
PROD_CONFIG_DIR=/volume2/docker/pigway-frame-flow/config
PROD_DATA_DIR=/volume1/docker/pigway-frame-flow/data
PROD_CONFIG_DIR=/volume1/docker/pigway-frame-flow/config
PROD_WEB_BIND=192.0.2.10
PROD_WEB_PORT=18787
PROD_STATE_VOLUME=pigway-frame-flow-cd-state
@@ -262,7 +262,7 @@ as an Actions artifact. First bootstrap has no prior healthy version to roll bac
No NAS bootstrap is executed merely by committing this implementation.
3. Require `BOOTSTRAP VERIFIED` in the deploy log. In Container Manager confirm
container `pigway-frame-flow` is running/healthy, user 10333, restart unless-stopped,
exact `/volume2/docker/pigway-frame-flow/{data,config}` mounts and
exact `/volume1/docker/pigway-frame-flow/{data,config}` mounts and
`192.0.2.10:18787` binding (not loopback).
4. Open `http://192.0.2.10:18787/api/v1/version`; its `commit` must equal the full
workflow checkout commit. Check `/ready` returns HTTP 200. The immutable image tag
@@ -305,7 +305,7 @@ not every possible external download. No Runner or NAS network configuration is
### Synology Photos dependencies
For a production photo source using Synology Photos person filtering, configure
`SYNOLOGY_PHOTOS_URL` and `SYNOLOGY_PHOTOS_CERT_SHA256` as Gitea Variables. The
`SYNOLOGY_PHOTOS_URL` and `SYNOLOGY_PHOTOS_CERT_SHA256` as Gitea Secrets. The
certificate fingerprint must match the configured HTTPS endpoint. Keep the Photos
session in the persistent `/config` directory; never put session credentials in
Variables or repository files.
+12 -1
View File
@@ -37,7 +37,18 @@ class Deploy:
self.lock_id = None
self.state = None
self.old_image = None
self.secret_values = []
self.secret_values = [
self.env[key]
for key in (
"PROD_DATA_DIR",
"PROD_CONFIG_DIR",
"PROD_PHOTOS_DIR",
"PROD_WEB_BIND",
"SYNOLOGY_PHOTOS_URL",
"SYNOLOGY_PHOTOS_CERT_SHA256",
)
if self.env.get(key)
]
self.docker = self.env.get("DOCKER_BIN", "docker")
def run(self, *args, data=None, check=True):
+23
View File
@@ -0,0 +1,23 @@
from pathlib import Path
def test_external_forks_do_not_execute_on_nas_runner():
workflow = Path(".github/workflows/verify.yml").read_text()
assert (
"github.event.pull_request.head.repo.full_name == github.repository" in workflow
)
assert "needs: verify" in workflow
def test_private_production_configuration_uses_secrets():
workflow = Path(".github/workflows/verify.yml").read_text()
for key in (
"PROD_DATA_DIR",
"PROD_CONFIG_DIR",
"PROD_PHOTOS_DIR",
"PROD_WEB_BIND",
"SYNOLOGY_PHOTOS_URL",
"SYNOLOGY_PHOTOS_CERT_SHA256",
):
assert "${{ secrets." + key + " }}" in workflow
assert "${{ vars." + key + " }}" not in workflow