portfolio pass: correct the CSP claim to match what nginx sends, rename the project outcome label, plain-english the projects intro and bio, standardise on Argo CD and single-touch, drop the education filler line
build-and-deploy / build (push) Failing after 14m34s
build-and-deploy / build (push) Failing after 14m34s
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "Shipping this site: GitOps from a homelab to the public internet"
|
||||
date: 2026-06-15
|
||||
summary: "How this portfolio is built and served - Astro to a container image, a self-hosted Gitea registry, ArgoCD, and a Cloudflare Tunnel - with security as acceptance criteria, not polish."
|
||||
summary: "How this portfolio is built and served - Astro to a container image, a self-hosted Gitea registry, Argo CD, and a Cloudflare Tunnel - with security as acceptance criteria, not polish."
|
||||
tags: ["gitops", "astro", "homelab", "security"]
|
||||
hero: "/blog/shipping-this-site.webp"
|
||||
heroAlt: "A glowing data container travels a luminous rail from a small server rack toward a softly glowing globe of Earth, wrapped by a reconcile loop."
|
||||
@@ -18,7 +18,7 @@ by GitOps. No special case for "it's just a website."
|
||||
The build is baked into a hardened `nginx-unprivileged` image and pushed to a **self-hosted public
|
||||
Gitea registry**, deliberately a separate instance from the private one holding my infrastructure
|
||||
code, so the public artifact and the private estate never share a trust boundary. The image digest
|
||||
is then pinned in a private `home-ops` repo, **ArgoCD** reconciles that repo onto the cluster, and
|
||||
is then pinned in a private `home-ops` repo, **Argo CD** reconciles that repo onto the cluster, and
|
||||
a **Cloudflare Tunnel** exposes exactly one service, this site, outbound-only.
|
||||
|
||||
Follow the chain and notice what's missing. No open ports: the tunnel dials out. No server runtime:
|
||||
@@ -33,7 +33,7 @@ gesture at. The site didn't ship until every box was ticked:
|
||||
|
||||
```text
|
||||
[x] Static output - no server runtime to attack
|
||||
[x] Strict CSP, no unsafe-inline / unsafe-eval
|
||||
[x] CSP: script-src 'self', no inline or eval JavaScript (style-src keeps 'unsafe-inline' for Shiki)
|
||||
[x] Self-hosted fonts - zero third-party requests
|
||||
[x] No secrets in the client bundle (verified by build-time grep)
|
||||
[x] Outbound-only tunnel, single hostname, no catch-all
|
||||
|
||||
@@ -48,7 +48,7 @@ traffic on the way out), pin-downable with a `/32`. That's how the LLM gateway's
|
||||
be a single line: one legitimate consumer, the agent cluster next door, exactly one `/32` allowed in.
|
||||
|
||||
Here's the one that costs a 2am. I made that change with `kubectl patch` - quick, out-of-band, worked
|
||||
immediately. The service was GitOps-managed with self-heal on. Within seconds ArgoCD noticed the live
|
||||
immediately. The service was GitOps-managed with self-heal on. Within seconds Argo CD noticed the live
|
||||
object differed from git and put it back the way the repo said, which re-enabled the SNAT, which
|
||||
re-broke the policy, which took DNS down with it. **The change must be committed**, or the platform
|
||||
will politely undo your fix while you sleep.
|
||||
|
||||
Reference in New Issue
Block a user