Prepare public repository privacy and trusted runner gates
This commit is contained in:
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user