Compare commits
3 Commits
d4ea0e58f1
...
fc2b0c9b53
| Author | SHA1 | Date | |
|---|---|---|---|
| fc2b0c9b53 | |||
| 25545ee7ca | |||
| 24d0ab736c |
@@ -82,6 +82,7 @@ jobs:
|
||||
- name: Apply Kubernetes manifests
|
||||
run: |
|
||||
kubectl apply -f k8s/namespace.yaml
|
||||
kubectl apply -f k8s/middleware.yaml
|
||||
kubectl apply -f k8s/deployment.yaml
|
||||
kubectl apply -f k8s/service.yaml
|
||||
kubectl apply -f k8s/ingress.yaml
|
||||
|
||||
@@ -1,3 +1,61 @@
|
||||
# signalledger.nl
|
||||
|
||||
Signal Ledger news site
|
||||
Signal Ledger — an independent news publication, a subsidiary of Jopdorp.
|
||||
|
||||
## Architecture Decision Record (ADR)
|
||||
|
||||
### ADR-002: Ingress Controller Migration (nginx → Traefik)
|
||||
|
||||
**Status:** Accepted
|
||||
|
||||
**Context:**
|
||||
The cluster uses Traefik as its ingress controller. The initial K8s manifests were written with `ingressClassName: nginx` and nginx-specific annotations. This caused a mismatch: the Ingress resource was never picked up by any controller, leaving the site unreachable via the configured domains.
|
||||
|
||||
**Decision:**
|
||||
Migrate all ingress configuration to Traefik-native resources.
|
||||
|
||||
1. **Ingress class:** Changed from `nginx` to `traefik`.
|
||||
2. **Annotations:** Replaced nginx-specific `configuration-snippet` with Traefik `router.entrypoints`, `router.tls`, and `router.middlewares` annotations.
|
||||
3. **Security headers:** Extracted from inline nginx snippets into a dedicated `Middleware` CRD (`k8s/middleware.yaml`). This keeps header policy declarative and reusable.
|
||||
|
||||
**Migration strategy:** In-place update
|
||||
- The namespace `openclaw-private` already exists.
|
||||
- The deployment, service, and TLS secret are unchanged.
|
||||
- We apply the new Ingress and Middleware manifests; Traefik picks them up immediately.
|
||||
- Rolling back is a single `kubectl apply` of the previous manifest version.
|
||||
|
||||
**Consequences:**
|
||||
- Positive: Aligns with cluster infrastructure. No extra ingress controller needed.
|
||||
- Positive: Middleware CRD is cleaner and version-controllable than inline snippets.
|
||||
- Risk: Traefik middleware syntax errors will cause 404/500 until fixed. Mitigated by validating manifests in CI before deploy.
|
||||
|
||||
## Deployment
|
||||
|
||||
### Prerequisites
|
||||
- Kubernetes cluster with Traefik and cert-manager installed.
|
||||
- `registry.claw.jopdorp.nl` push access.
|
||||
- `KUBECONFIG_BASE64` and `REGISTRY_TOKEN` secrets configured in Gitea.
|
||||
|
||||
### CI/CD Pipeline
|
||||
Gitea Actions workflow (`.gitea/workflows/build-and-deploy.yaml`):
|
||||
1. Build and test on every PR/push.
|
||||
2. Build and push Docker image on merge to `main`.
|
||||
3. Apply K8s manifests and wait for rollout.
|
||||
|
||||
### Manual Deploy
|
||||
```bash
|
||||
kubectl apply -f k8s/namespace.yaml
|
||||
kubectl apply -f k8s/middleware.yaml
|
||||
kubectl apply -f k8s/deployment.yaml
|
||||
kubectl apply -f k8s/service.yaml
|
||||
kubectl apply -f k8s/ingress.yaml
|
||||
kubectl rollout status deployment/news-site -n openclaw-private --timeout=120s
|
||||
```
|
||||
|
||||
### Domains
|
||||
- `signalledger.nl`
|
||||
- `www.signalledger.nl`
|
||||
|
||||
### Contact
|
||||
- Email: signalledger@jopdorp.nl
|
||||
- Owner: Signal Ledger is a subsidiary of Jopdorp.
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ metadata:
|
||||
name: news-site
|
||||
namespace: openclaw-private
|
||||
labels:
|
||||
app.kubernetes.io/name: signalledger
|
||||
app: news-site
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/part-of: signalledger
|
||||
app.kubernetes.io/managed-by: gitea-actions
|
||||
@@ -17,11 +17,11 @@ spec:
|
||||
maxUnavailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: signalledger
|
||||
app: news-site
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: signalledger
|
||||
app: news-site
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/part-of: signalledger
|
||||
spec:
|
||||
|
||||
+15
-13
@@ -4,21 +4,33 @@ metadata:
|
||||
name: news-site
|
||||
namespace: openclaw-private
|
||||
labels:
|
||||
app.kubernetes.io/name: signalledger
|
||||
app: news-site
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/part-of: signalledger
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: "websecure"
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
traefik.ingress.kubernetes.io/router.middlewares: "openclaw-private-security-headers@kubernetescrd"
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- news.claw.jopdorp.nl
|
||||
- signalledger.nl
|
||||
- www.signalledger.nl
|
||||
secretName: signalledger-tls
|
||||
secretName: news-site-tls
|
||||
rules:
|
||||
- host: news.claw.jopdorp.nl
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: news-site
|
||||
port:
|
||||
number: 80
|
||||
- host: signalledger.nl
|
||||
http:
|
||||
paths:
|
||||
@@ -39,13 +51,3 @@ spec:
|
||||
name: news-site
|
||||
port:
|
||||
number: 80
|
||||
- host: news.claw.jopdorp.nl
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: news-site
|
||||
port:
|
||||
number: 80
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: security-headers
|
||||
namespace: openclaw-private
|
||||
labels:
|
||||
app: news-site
|
||||
app.kubernetes.io/component: middleware
|
||||
app.kubernetes.io/part-of: signalledger
|
||||
spec:
|
||||
headers:
|
||||
customRequestHeaders:
|
||||
X-Forwarded-Proto: "https"
|
||||
customResponseHeaders:
|
||||
Strict-Transport-Security: "max-age=31536000; includeSubDomains; preload"
|
||||
X-Frame-Options: "DENY"
|
||||
X-Content-Type-Options: "nosniff"
|
||||
Referrer-Policy: "strict-origin-when-cross-origin"
|
||||
Content-Security-Policy: "default-src 'self'; script-src 'self' 'unsafe-inline' https://pagead2.googlesyndication.com https://partner.googleadservices.com https://tpc.googlesyndication.com; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; font-src 'self'; connect-src 'self'; frame-src https://googleads.g.doubleclick.net; object-src 'none'; base-uri 'self'; form-action 'self';"
|
||||
X-XSS-Protection: "1; mode=block"
|
||||
Permissions-Policy: "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
|
||||
sslRedirect: true
|
||||
+2
-2
@@ -4,12 +4,12 @@ metadata:
|
||||
name: news-site
|
||||
namespace: openclaw-private
|
||||
labels:
|
||||
app.kubernetes.io/name: signalledger
|
||||
app: news-site
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/part-of: signalledger
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: signalledger
|
||||
app: news-site
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
|
||||
Reference in New Issue
Block a user