25 Commits

Author SHA1 Message Date
Chris Farhood 1c5bc1115a Delete .gitea/workflows/best-practices.yaml 2026-05-22 15:40:07 +00:00
Chris Farhood fc753ea5ca Delete .gitea/workflows/security.yaml
Best Practices / Kube-score Analysis (push) Failing after 5s
Best Practices / Polaris Audit (push) Failing after 5s
Best Practices / PR Summary Report (push) Has been skipped
Best Practices / Polaris PR Review (push) Has been skipped
Best Practices / Resource Usage Analysis (push) Successful in 4s
2026-05-22 15:40:00 +00:00
Chris Farhood 9dbac86289 Delete .gitea/workflows/validate.yaml
Best Practices / Kube-score Analysis (push) Failing after 7s
Security Scan / Checkov IaC Scan (push) Failing after 52s
Best Practices / Resource Usage Analysis (push) Successful in 6s
Security Scan / Trivy PR Review (push) Has been skipped
Security Scan / Checkov PR Review (push) Has been skipped
Best Practices / PR Summary Report (push) Has been skipped
Best Practices / Polaris PR Review (push) Has been skipped
Best Practices / Polaris Audit (push) Failing after 9s
Security Scan / Trivy Security Scan (push) Successful in 14s
2026-05-22 15:39:55 +00:00
Chris Farhood 060ac76748 feat(irc): migrate from Cilium to Istio AuthorizationPolicies
Best Practices / Kube-score Analysis (push) Failing after 6s
Security Scan / Trivy PR Review (push) Has been skipped
Best Practices / Polaris PR Review (push) Has been skipped
Security Scan / Checkov PR Review (push) Has been skipped
Validate Manifests / YAML Lint (push) Successful in 4s
Validate Manifests / Kustomize Build Test (push) Successful in 3s
Security Scan / Trivy Security Scan (push) Successful in 12s
Best Practices / Polaris Audit (push) Failing after 9s
Best Practices / Resource Usage Analysis (push) Successful in 5s
Best Practices / PR Summary Report (push) Has been skipped
Validate Manifests / Kubernetes Schema Validation (push) Successful in 6s
Security Scan / Checkov IaC Scan (push) Failing after 52s
Replace CiliumNetworkPolicies with Istio AuthorizationPolicies, point
the thelounge HTTPRoute at the istio-external gateway, and give each
workload a dedicated ServiceAccount for precise mTLS identity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 11:31:28 -04:00
Chris Farhood 03467ec947 chore(irc): remove namespace, now managed by infra repo
Security Scan / Checkov IaC Scan (push) Failing after 31s
Best Practices / Polaris PR Review (push) Has been skipped
Best Practices / Kube-score Analysis (push) Failing after 5s
Security Scan / Trivy PR Review (push) Has been skipped
Best Practices / Polaris Audit (push) Failing after 5s
Security Scan / Checkov PR Review (push) Has been skipped
Best Practices / Resource Usage Analysis (push) Successful in 5s
Validate Manifests / YAML Lint (push) Failing after 3s
Validate Manifests / Kubernetes Schema Validation (push) Successful in 6s
Best Practices / PR Summary Report (push) Has been skipped
Validate Manifests / Kustomize Build Test (push) Successful in 7s
Security Scan / Trivy Security Scan (push) Successful in 13s
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 11:17:29 -04:00
Chris Farhood 2308e1103a fix(thelounge): allow ingress from cilium gateway entity
Security Scan / Checkov PR Review (push) Has been skipped
Validate Manifests / Kubernetes Schema Validation (push) Successful in 6s
Best Practices / Polaris PR Review (push) Has been skipped
Best Practices / Resource Usage Analysis (push) Successful in 6s
Validate Manifests / Kustomize Build Test (push) Successful in 4s
Security Scan / Trivy PR Review (push) Has been skipped
Best Practices / Kube-score Analysis (push) Failing after 7s
Security Scan / Trivy Security Scan (push) Successful in 7s
Best Practices / Polaris Audit (push) Failing after 8s
Best Practices / PR Summary Report (push) Has been skipped
Validate Manifests / YAML Lint (push) Failing after 4s
Security Scan / Checkov IaC Scan (push) Failing after 30s
The previous selector matched an Istio-style label that does not exist
on Cilium gateway pods, which caused default-deny ingress and blocked
access to thelounge. Use fromEntities: ingress to match Cilium's own
gateway/ingress pods, consistent with the cluster repo's webhook CNP.
2026-05-21 10:15:19 -04:00
Chris Farhood 19b175dcf2 feat(irc): restrict ingress to gateway and thelounge
Best Practices / Kube-score Analysis (push) Has been cancelled
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Successful in 35s
Validate Manifests / YAML Lint (push) Failing after 35s
Validate Manifests / Kubernetes Schema Validation (push) Successful in 35s
Add CiliumNetworkPolicy ingress rules so thelounge only accepts traffic
from the cilium external gateway in gateway-system, and znc only from
the thelounge pod. Allow host entity on both for kubelet probes.

Switch znc service to ClusterIP and drop the external-dns annotation
since direct external IRC client access is no longer desired.
2026-05-17 08:17:20 -04:00
Chris Farhood ee93aca3b8 fix(irc): remove namespace from istio ambient mode
Best Practices / Kube-score Analysis (push) Has been cancelled
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Validate Manifests / YAML Lint (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Has been cancelled
Validate Manifests / Kubernetes Schema Validation (push) Has been cancelled
Drop the istio.io/dataplane-mode label and the AuthorizationPolicies
for thelounge and znc. Gateway was returning upstream connect errors
when traffic transited ztunnel; reverting the namespace to non-mesh
restores reachability. CiliumNetworkPolicies remain for egress filtering.
2026-05-17 08:13:26 -04:00
Chris Farhood 2992d7d326 feat(thelounge): add cilium egress filtering
Best Practices / Kube-score Analysis (push) Has been cancelled
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Validate Manifests / YAML Lint (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Has been cancelled
Validate Manifests / Kubernetes Schema Validation (push) Has been cancelled
Allow: DNS, LDAP (authentik), in-cluster ZNC, irc.passthepopcorn.me:6697.
Default-deny for everything else.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 18:18:40 -04:00
Chris Farhood 84ee1fa8b8 refactor: drop istio mesh egress, use cilium FQDN for znc egress filtering
Best Practices / Kube-score Analysis (push) Has been cancelled
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Has been cancelled
Validate Manifests / Kubernetes Schema Validation (push) Has been cancelled
Validate Manifests / YAML Lint (push) Has been cancelled
Istio ambient cannot do hostname-based egress filtering without L7
processing (waypoint/sidecar). Cilium FQDN CiliumNetworkPolicy is the
right tool — DNS-aware L3/L4 enforcement.

- Remove waypoint deployment and namespace/service label references
- Move TheLounge HTTPRoute back to Cilium external gateway
- Add CiliumNetworkPolicy for znc: allow DNS + irc.passthepopcorn.me:6697
- Remove orphaned znc/egress.yaml (Istio VirtualService routing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 18:03:11 -04:00
Chris Farhood 31e6864a2a fix(irc): switch AuthorizationPolicies to selector-based for ztunnel L4 enforcement
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Validate Manifests / YAML Lint (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Has been cancelled
Validate Manifests / Kubernetes Schema Validation (push) Has been cancelled
Best Practices / Kube-score Analysis (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
targetRefs: kind: Service policies require waypoint binding which is not
working in Istio 1.29.2 — WaypointAccepted: False and ztunnel routes
directly to pods bypassing the waypoint. Selector-based policies are
enforced at ztunnel L4 without requiring waypoint.
2026-05-15 22:53:56 -04:00
Chris Farhood 0614d6b91a fix(irc): bind services to waypoint for AuthorizationPolicy enforcement
Best Practices / Kube-score Analysis (push) Has been cancelled
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Validate Manifests / YAML Lint (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
Validate Manifests / Kubernetes Schema Validation (push) Has been cancelled
WaypointAccepted: False on both policies — Istio 1.29 requires
istio.io/use-waypoint on the Service directly, namespace label alone
is insufficient for targetRefs: kind: Service policy binding.
2026-05-15 22:46:21 -04:00
Chris Farhood 605002f58a feat(thelounge): add config.js with LDAP, reverseProxy, and sqlite storage
Best Practices / Kube-score Analysis (push) Has been cancelled
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Has been cancelled
Validate Manifests / Kubernetes Schema Validation (push) Has been cancelled
Validate Manifests / YAML Lint (push) Has been cancelled
2026-05-15 22:31:32 -04:00
Chris Farhood f577121ea7 chore: remove Happy Engineering commit annotation
Best Practices / Kube-score Analysis (push) Has been cancelled
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Validate Manifests / YAML Lint (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Has been cancelled
Validate Manifests / Kubernetes Schema Validation (push) Has been cancelled
2026-05-15 22:18:43 -04:00
Chris Farhood 87c03682c4 feat(irc): add Istio ambient mode, waypoint, and AuthorizationPolicies
Best Practices / Polaris Audit (push) Has been cancelled
Best Practices / Resource Usage Analysis (push) Has been cancelled
Best Practices / PR Summary Report (push) Has been cancelled
Best Practices / Polaris PR Review (push) Has been cancelled
Security Scan / Trivy Security Scan (push) Has been cancelled
Security Scan / Trivy PR Review (push) Has been cancelled
Security Scan / Checkov IaC Scan (push) Has been cancelled
Security Scan / Checkov PR Review (push) Has been cancelled
Validate Manifests / YAML Lint (push) Has been cancelled
Validate Manifests / Kustomize Build Test (push) Has been cancelled
Validate Manifests / Kubernetes Schema Validation (push) Has been cancelled
Best Practices / Kube-score Analysis (push) Has been cancelled
- Add namespace.yaml with istio ambient + waypoint labels
- Add waypoint Gateway (istio-waypoint) scoped to irc namespace
- Switch thelounge HTTPRoute from Cilium external to istio-external gateway
- Add AuthorizationPolicy for thelounge (allow inbound from gateway-system only)
- Add AuthorizationPolicy for znc (allow all on port 6501 for IRC clients)
- Add namespace: irc to root kustomization, remove dependency on targetNamespace

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-05-15 21:02:10 -04:00
Chris Farhood 8a9cf61137 fix(storage): migrate znc from block-truenas to ceph-block
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 23:30:34 -04:00
Chris Farhood 927bd66811 fix(irc): add init container to fix thelounge volume permissions
Fresh PVC needs ownership set to uid 1000 before thelounge can start.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:48:53 -05:00
Chris Farhood 37cb7b9a14 fix(irc): revert thelounge to ceph-block
thelounge chmod fails on NVMe-oF volumes. Keep on ceph-block.
ZNC remains on block-truenas.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:46:54 -05:00
Chris Farhood be697980d5 chore(irc): migrate thelounge and znc storage to block-truenas
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:09:16 -05:00
Chris Farhood 64a658ce9e Merge pull request 'chore: remove NetworkPolicy resources for IRC apps' (#6) from chore/remove-network-policies into main
Reviewed-on: #6
Reviewed-by: polaris <no-reply.polaris@farh.net>
Reviewed-by: trivvy <no-reply.trivvy@farh.net>
Reviewed-by: checkov <no-reply.checkov@farh.net>
2026-02-10 13:05:24 -05:00
Chris Farhood a0df6cd978 chore: remove NetworkPolicy resources for IRC apps 2026-02-10 12:48:09 -05:00
Chris Farhood 0f4cf77ec3 Merge pull request 'fix: correct HTTPRoute gateway namespace to gateway-system' (#5) from fix/httproute-gateway-namespace into main
Reviewed-on: #5
Reviewed-by: polaris <no-reply.polaris@farh.net>
2026-02-10 12:41:48 -05:00
Chris Farhood 91d790b651 fix: correct HTTPRoute gateway namespace to gateway-system 2026-02-10 12:41:27 -05:00
Chris Farhood 976a758d10 Merge pull request 'chore: add comprehensive CI/CD exemptions for ZNC' (#4) from fix/znc-container-crash into main
Reviewed-on: #4
Reviewed-by: polaris <no-reply.polaris@farh.net>
Reviewed-by: checkov <no-reply.checkov@farh.net>
2026-02-10 12:32:45 -05:00
Chris Farhood 9af4b27510 Merge pull request 'fix: remove PUID/PGID env vars from ZNC container' (#2) from fix/znc-container-crash into main
Reviewed-on: #2
Reviewed-by: polaris <no-reply.polaris@farh.net>
Reviewed-by: checkov <no-reply.checkov@farh.net>
2026-02-08 14:38:01 -05:00
18 changed files with 106 additions and 789 deletions
-284
View File
@@ -1,284 +0,0 @@
name: Best Practices
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
kube-score:
name: Kube-score Analysis
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install kubectl and kube-score
run: |
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
# Install kube-score
wget https://github.com/zegl/kube-score/releases/download/v1.18.0/kube-score_1.18.0_linux_amd64.tar.gz
tar -xzf kube-score_1.18.0_linux_amd64.tar.gz
chmod +x kube-score
mv kube-score /usr/local/bin/
- name: Run kube-score
run: |
if [ -f "kustomization.yaml" ]; then
kubectl kustomize . | kube-score score - \
--ignore-test pod-networkpolicy \
--ignore-test deployment-has-poddisruptionbudget \
--ignore-test container-security-context-readonlyrootfilesystem \
--ignore-test container-image-tag \
--ignore-test container-security-context-user-group-id \
--ignore-test probe-not-identical \
--ignore-test container-security-context \
--ignore-test container-seccomp-profile \
--ignore-test container-ephemeral-storage-request-and-limit \
--ignore-test statefulset-has-poddisruptionbudget \
--ignore-test container-security-context-privileged \
--ignore-test container-security-context-privilege-escalation \
--ignore-test pod-probes \
--output-format ci
fi
polaris:
name: Polaris Audit
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install kubectl and polaris
run: |
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
# Install polaris
wget https://github.com/FairwindsOps/polaris/releases/download/9.5.0/polaris_linux_amd64.tar.gz
tar -xzf polaris_linux_amd64.tar.gz
chmod +x polaris
mv polaris /usr/local/bin/
- name: Run Polaris audit
run: |
if [ -f "kustomization.yaml" ]; then
kubectl kustomize . > manifests.yaml
polaris audit --audit-path manifests.yaml \
--format pretty \
--set-exit-code-on-danger \
--set-exit-code-below-score 70
fi
resource-analysis:
name: Resource Usage Analysis
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install kubectl and yq
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O yq
chmod +x yq
mv yq /usr/local/bin/
- name: Analyze resource requests and limits
run: |
echo "# Resource Analysis Report"
echo ""
echo "## Applications Resource Configuration"
echo ""
echo "| Application | Container | CPU Request | CPU Limit | Memory Request | Memory Limit |"
echo "|-------------|-----------|-------------|-----------|----------------|--------------|"
# Find all directories with kustomization.yaml
find . -maxdepth 2 -name "kustomization.yaml" | while read config; do
app_dir=$(dirname "$config")
if [ "$app_dir" != "." ]; then
manifests=$(kubectl kustomize "$app_dir" 2>/dev/null)
if [ -n "$manifests" ]; then
echo "$manifests" | yq eval-all '
select(.kind == "Deployment" or .kind == "StatefulSet") |
.spec.template.spec.containers[] |
"| '"$app_dir"' | \(.name) | \(.resources.requests.cpu // "none") | \(.resources.limits.cpu // "none") | \(.resources.requests.memory // "none") | \(.resources.limits.memory // "none") |"
' - 2>/dev/null || true
fi
fi
done
pr-summary:
name: PR Summary Report
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
needs: [kube-score, polaris, resource-analysis]
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Generate PR summary
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments
run: |
cat > summary.md << EOF
## Best Practices Validation Summary
✅ All validation checks completed
### Checks Run:
- **kube-score**: Kubernetes best practices analysis
- **Polaris**: Security and reliability audit
- **Resource Analysis**: CPU and memory configuration review
See individual job logs for detailed results.
---
*Automated by Gitea Actions*
EOF
if [ -n "${GITEA_TOKEN}" ]; then
jq -n --rawfile body summary.md '{body: $body}' > comment-payload.json
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d @comment-payload.json \
"${GITEA_API}" || echo "Failed to post comment (token may not be configured)"
else
echo "GITEA_TOKEN not configured, skipping comment"
cat summary.md
fi
polaris-pr-review:
name: Polaris PR Review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install tools
run: |
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
# Install polaris
wget https://github.com/FairwindsOps/polaris/releases/download/9.5.0/polaris_linux_amd64.tar.gz
tar -xzf polaris_linux_amd64.tar.gz
chmod +x polaris
mv polaris /usr/local/bin/
# Install jq
apt-get update && apt-get install -y jq
- name: Run Polaris and post review
env:
GITEA_TOKEN: ${{ secrets.POLARIS_GITEA_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews
run: |
if [ ! -f "kustomization.yaml" ]; then
echo "No root kustomization.yaml, skipping Polaris review"
exit 0
fi
kubectl kustomize . > manifests.yaml
if [ ! -s manifests.yaml ]; then
echo "Manifests are empty, skipping"
exit 0
fi
polaris audit --audit-path manifests.yaml --format json > polaris-results.json || true
DANGERS=$(jq '.Summary.Dangers // 0' polaris-results.json)
WARNINGS=$(jq '.Summary.Warnings // 0' polaris-results.json)
SCORE=$(jq '.Summary.Score // 0' polaris-results.json)
if [ "$DANGERS" -gt 0 ]; then
REVIEW_STATE="REQUEST_CHANGES"
VERDICT="BLOCKED: ${DANGERS} danger(s) detected. Score: ${SCORE}%"
EXIT_CODE=1
elif [ "$WARNINGS" -gt 0 ]; then
REVIEW_STATE="COMMENT"
VERDICT="WARNING: ${WARNINGS} warning(s) detected. Score: ${SCORE}%"
EXIT_CODE=0
else
REVIEW_STATE="APPROVED"
VERDICT="PASSED: No dangers or warnings. Score: ${SCORE}%"
EXIT_CODE=0
fi
DETAILS=$(jq -r '
.Results[]? |
.Name as $resName | .Kind as $resKind | .Namespace as $resNs |
(
(.PodResult?.Results[]? | {sev: .Severity, msg: .Message, check: .ID, target: "Pod"}),
(.PodResult?.ContainerResults[]? | .Name as $contName | .Results[]? | {sev: .Severity, msg: .Message, check: .ID, target: $contName})
) |
select(.sev == "danger" or .sev == "warning") |
"| \(.sev) | \($resKind)/\($resName) | \(.target) | \(.check) | \(.msg) |"
' polaris-results.json | head -c 4000)
cat > review-body.md << INTERNAL_EOF
## Polaris Audit Results
**${VERDICT}**
### Summary
| Metric | Value |
|--------|-------|
| Score | ${SCORE}% |
| Dangers | ${DANGERS} |
| Warnings | ${WARNINGS} |
<details>
<summary>Issues (click to expand)</summary>
| Severity | Resource | Container | Check | Message |
|----------|----------|-----------|-------|---------|
\${DETAILS}
</details>
---
*Scanned by [Polaris](https://github.com/FairwindsOps/polaris)*
INTERNAL_EOF
jq -n \
--rawfile body review-body.md \
--arg event "$REVIEW_STATE" \
'{body: $body, event: $event}' > review-payload.json
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d @review-payload.json \
"${GITEA_API}" | jq .
exit $EXIT_CODE
-302
View File
@@ -1,302 +0,0 @@
name: Security Scan
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
trivy-scan:
name: Trivy Security Scan
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Trivy
run: |
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
- name: Run Trivy config scan
run: |
trivy config \
--severity CRITICAL,HIGH \
--ignorefile .trivyignore \
--exit-code 0 \
--format table \
.
trivy-pr-review:
name: Trivy PR Review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Trivy and jq
run: |
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
apt-get update && apt-get install -y jq
- name: Get changed files
id: changed
run: |
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- '*.yaml' '*.yml' '*.tf' | tr '\n' ' ')
echo "files=${CHANGED_FILES}" >> $GITHUB_OUTPUT
echo "Changed files: ${CHANGED_FILES}"
- name: Run Trivy scan and post review
env:
GITEA_TOKEN: ${{ secrets.TRIVY_GITEA_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews
CHANGED_FILES: ${{ steps.changed.outputs.files }}
run: |
if [ -z "${CHANGED_FILES}" ]; then
echo "No IaC files changed, skipping scan"
exit 0
fi
trivy config \
--severity CRITICAL,HIGH,MEDIUM,LOW \
--ignorefile .trivyignore \
--exit-code 0 \
--format json \
--output trivy-results.json \
.
CHANGED_FILES_JSON=$(echo "${CHANGED_FILES}" | tr ' ' '\n' | sed '/^$/d' | jq -R . | jq -s .)
CRITICAL=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.Results[]? | select(.Target as $t | $files | any(. as $f | $t | endswith($f))) | .Misconfigurations[]? | select(.Severity == "CRITICAL")] | length' \
trivy-results.json)
HIGH=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.Results[]? | select(.Target as $t | $files | any(. as $f | $t | endswith($f))) | .Misconfigurations[]? | select(.Severity == "HIGH")] | length' \
trivy-results.json)
MEDIUM=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.Results[]? | select(.Target as $t | $files | any(. as $f | $t | endswith($f))) | .Misconfigurations[]? | select(.Severity == "MEDIUM")] | length' \
trivy-results.json)
LOW=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.Results[]? | select(.Target as $t | $files | any(. as $f | $t | endswith($f))) | .Misconfigurations[]? | select(.Severity == "LOW")] | length' \
trivy-results.json)
TOTAL_FAILED=$(( CRITICAL + HIGH + MEDIUM + LOW ))
if [ "$CRITICAL" -gt 0 ]; then
REVIEW_STATE="REQUEST_CHANGES"
VERDICT="BLOCKED: ${CRITICAL} critical finding(s) detected"
EXIT_CODE=1
elif [ "$HIGH" -gt 0 ]; then
REVIEW_STATE="COMMENT"
VERDICT="WARNING: ${HIGH} high severity finding(s) detected"
EXIT_CODE=0
else
REVIEW_STATE="APPROVED"
VERDICT="PASSED: No critical or high severity findings"
EXIT_CODE=0
fi
DETAILS=$(jq -r --argjson files "$CHANGED_FILES_JSON" '
[.Results[]? | select(.Target as $t | $files | any(. as $f | $t | endswith($f))) |
.Target as $t | .Misconfigurations[]? |
"| \(.Severity) | \(.ID) | \(.Title) | \($t) |"
] | join("\n")' trivy-results.json)
cat > review-body.md << EOF
## Trivy Security Scan Results
**${VERDICT}**
> Scanned ${CHANGED_FILES:-"no files"}
### Summary
| Severity | Count |
|----------|-------|
| Critical | ${CRITICAL} |
| High | ${HIGH} |
| Medium | ${MEDIUM} |
| Low | ${LOW} |
| **Total** | **${TOTAL_FAILED}** |
<details>
<summary>Failed Checks (click to expand)</summary>
| Severity | Check ID | Description | File |
|----------|----------|-------------|------|
${DETAILS}
</details>
---
*Scanned by [Trivy](https://github.com/aquasecurity/trivy)*
EOF
jq -n \
--rawfile body review-body.md \
--arg event "$REVIEW_STATE" \
'{body: $body, event: $event}' > review-payload.json
echo "Posting review to: ${GITEA_API}"
echo "Review state: ${REVIEW_STATE}"
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d @review-payload.json \
"${GITEA_API}" || echo "Failed to post review (token may not be configured)"
exit $EXIT_CODE
checkov-scan:
name: Checkov IaC Scan
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Checkov
run: |
python3 -m venv /opt/checkov
/opt/checkov/bin/pip install checkov
- name: Run Checkov scan
run: |
/opt/checkov/bin/checkov -d . \
--config-file .checkov.yaml \
--output cli
checkov-pr-review:
name: Checkov PR Review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Checkov and jq
run: |
python3 -m venv /opt/checkov
/opt/checkov/bin/pip install checkov
apt-get update && apt-get install -y jq
- name: Get changed files
id: changed
run: |
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- '*.yaml' '*.yml' '*.tf' | tr '\n' ' ')
echo "files=${CHANGED_FILES}" >> $GITHUB_OUTPUT
echo "Changed files: ${CHANGED_FILES}"
- name: Run Checkov scan and post review
env:
GITEA_TOKEN: ${{ secrets.CHECKOV_GITEA_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews
CHANGED_FILES: ${{ steps.changed.outputs.files }}
run: |
if [ -z "${CHANGED_FILES}" ]; then
echo "No IaC files changed, skipping scan"
exit 0
fi
/opt/checkov/bin/checkov -d . \
--config-file .checkov.yaml \
--output json \
> checkov-results.json || true
CHANGED_FILES_JSON=$(echo "${CHANGED_FILES}" | tr ' ' '\n' | sed '/^$/d' | jq -R . | jq -s .)
CRITICAL=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.[] | .results.failed_checks[]? | select(.file_path as $fp | $files | any(. as $f | $fp | endswith($f))) | select(.severity == "CRITICAL")] | length' \
checkov-results.json)
HIGH=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.[] | .results.failed_checks[]? | select(.file_path as $fp | $files | any(. as $f | $fp | endswith($f))) | select(.severity == "HIGH")] | length' \
checkov-results.json)
MEDIUM=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.[] | .results.failed_checks[]? | select(.file_path as $fp | $files | any(. as $f | $fp | endswith($f))) | select(.severity == "MEDIUM")] | length' \
checkov-results.json)
LOW=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.[] | .results.failed_checks[]? | select(.file_path as $fp | $files | any(. as $f | $fp | endswith($f))) | select(.severity == "LOW")] | length' \
checkov-results.json)
UNSET=$(jq --argjson files "$CHANGED_FILES_JSON" \
'[.[] | .results.failed_checks[]? | select(.file_path as $fp | $files | any(. as $f | $fp | endswith($f))) | select(.severity == null or .severity == "UNKNOWN" or .severity == "NONE")] | length' \
checkov-results.json)
TOTAL_FAILED=$(( CRITICAL + HIGH + MEDIUM + LOW + UNSET ))
if [ "$CRITICAL" -gt 0 ]; then
REVIEW_STATE="REQUEST_CHANGES"
VERDICT="BLOCKED: ${CRITICAL} critical finding(s) detected"
EXIT_CODE=1
elif [ "$HIGH" -gt 0 ]; then
REVIEW_STATE="COMMENT"
VERDICT="WARNING: ${HIGH} high severity finding(s) detected"
EXIT_CODE=0
else
REVIEW_STATE="APPROVED"
VERDICT="PASSED: No critical or high severity findings"
EXIT_CODE=0
fi
DETAILS=$(jq -r --argjson files "$CHANGED_FILES_JSON" '
[.[] | .check_type as $ct |
.results.failed_checks[]? |
select(.file_path as $fp | $files | any(. as $f | $fp | endswith($f))) |
"| \(.severity // "UNSET") | \(.check_id) | \(.check_name) | \(.file_path) |"
] | join("\n")' checkov-results.json)
cat > review-body.md << EOF
## Checkov IaC Scan Results
**${VERDICT}**
> Scanned ${CHANGED_FILES:-"no files"}
### Summary
| Severity | Count |
|----------|-------|
| Critical | ${CRITICAL} |
| High | ${HIGH} |
| Medium | ${MEDIUM} |
| Low | ${LOW} |
| Unset | ${UNSET} |
| **Total** | **${TOTAL_FAILED}** |
<details>
<summary>Failed Checks (click to expand)</summary>
| Severity | Check ID | Description | File |
|----------|----------|-------------|------|
${DETAILS}
</details>
---
*Scanned by [Checkov](https://github.com/bridgecrewio/checkov)*
EOF
jq -n \
--rawfile body review-body.md \
--arg event "$REVIEW_STATE" \
'{body: $body, event: $event}' > review-payload.json
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d @review-payload.json \
"${GITEA_API}" || echo "Failed to post review (token may not be configured)"
exit $EXIT_CODE
-95
View File
@@ -1,95 +0,0 @@
name: Validate Manifests
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
yaml-lint:
name: YAML Lint
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install yamllint
run: |
python3 -m pip install --break-system-packages yamllint
- name: Run yamllint
run: |
yamllint -c .yamllint.yaml .
kustomize-build:
name: Kustomize Build Test
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install kubectl with kustomize
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
- name: Test root kustomization
run: |
if [ -f "kustomization.yaml" ]; then
echo "Building root kustomization..."
kubectl kustomize . > /tmp/manifests.yaml
echo "✓ Root kustomization builds successfully"
else
echo "No root kustomization.yaml found"
fi
- name: Test individual app kustomizations
run: |
find . -maxdepth 2 -name "kustomization.yaml" -not -path "./kustomization.yaml" | while read config; do
app_dir=$(dirname "$config")
echo "Building $app_dir kustomization..."
kubectl kustomize "$app_dir" > /dev/null
echo "✓ $app_dir kustomization builds successfully"
done
kubeconform:
name: Kubernetes Schema Validation
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install kubectl and kubeconform
run: |
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
# Install kubeconform
curl -L https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz | tar xz
chmod +x kubeconform
mv kubeconform /usr/local/bin/
- name: Validate Kubernetes manifests
run: |
if [ -f "kustomization.yaml" ]; then
kubectl kustomize . | kubeconform \
-schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
-summary \
-output text \
-ignore-missing-schemas \
-skip HTTPRoute \
-verbose
fi
+1 -9
View File
@@ -142,13 +142,5 @@ kubectl kustomize . | polaris audit --format pretty --set-exit-code-on-danger --
- Exit code 1 on critical findings, 0 on warnings/pass
## Commit Message Format
When making commits, include credits:
```
<main commit message>
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
```
Format: `{type}({scope}): {description}`
+3
View File
@@ -1,5 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: irc
resources:
# Uncomment if storing configuration in the repo
# - configmap.yaml
+15
View File
@@ -0,0 +1,15 @@
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: thelounge
namespace: irc
spec:
selector:
matchLabels:
app.kubernetes.io/name: thelounge
action: ALLOW
rules:
- from:
- source:
principals:
- cluster.local/ns/gateway-system/sa/istio-external-istio
+26
View File
@@ -0,0 +1,26 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: thelounge-config
namespace: irc
data:
config.js: |
module.exports = {
public: false,
reverseProxy: true,
port: 9000,
messageStorage: ["sqlite"],
storagePolicy: {
enabled: true,
maxAgeDays: 365,
deletionPolicy: "statusOnly",
},
ldap: {
enable: true,
url: "ldap://ak-outpost-ldap-outpost.auth.svc.cluster.local",
primaryKey: "cn",
baseDN: "ou=users,dc=farh,dc=net",
},
};
+2 -2
View File
@@ -5,8 +5,8 @@ metadata:
namespace: irc
spec:
parentRefs:
- name: external
namespace: istio-system
- name: istio-external
namespace: gateway-system
hostnames:
- ${THELOUNGE_HOSTNAME}
rules:
+3 -1
View File
@@ -1,7 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- networkpolicy.yaml
- serviceaccount.yaml
- statefulset.yaml
- service.yaml
- httproute.yaml
- authorizationpolicy.yaml
- config.yaml
-46
View File
@@ -1,46 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: thelounge
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: thelounge
policyTypes:
- Ingress
- Egress
ingress:
### Allow all ingress traffic (web app needs external access via gateway)
- {}
###
egress:
### Allow DNS resolution
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
###
### Allow intra-namespace communication
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: irc
###
### Allow outbound to the world
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
###
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: thelounge
namespace: irc
automountServiceAccountToken: false
+24
View File
@@ -21,6 +21,7 @@ spec:
app.kubernetes.io/instance: thelounge
spec:
priorityClassName: low-priority
serviceAccountName: thelounge
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
@@ -29,6 +30,20 @@ spec:
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
initContainers:
- name: fix-permissions
image: busybox:1.37
command: ["sh", "-c", "chown -R 1000:1000 /var/opt/thelounge"]
volumeMounts:
- name: config
mountPath: /var/opt/thelounge
securityContext:
runAsNonRoot: false
runAsUser: 0
resources:
limits:
cpu: 100m
memory: 64Mi
containers:
- name: thelounge
image: ghcr.io/thelounge/thelounge:latest
@@ -48,6 +63,10 @@ spec:
volumeMounts:
- name: config
mountPath: /var/opt/thelounge
- name: thelounge-config
mountPath: /var/opt/thelounge/config.js
subPath: config.js
readOnly: true
resources:
requests:
cpu: "100m"
@@ -72,10 +91,15 @@ spec:
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 3
volumes:
- name: thelounge-config
configMap:
name: thelounge-config
volumeClaimTemplates:
- metadata:
name: config
spec:
storageClassName: ceph-block
accessModes:
- ReadWriteOnce
resources:
+15
View File
@@ -0,0 +1,15 @@
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: znc
namespace: irc
spec:
selector:
matchLabels:
app.kubernetes.io/name: znc
action: ALLOW
rules:
- from:
- source:
principals:
- cluster.local/ns/irc/sa/thelounge
+2 -1
View File
@@ -1,6 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- networkpolicy.yaml
- serviceaccount.yaml
- statefulset.yaml
- service.yaml
- authorizationpolicy.yaml
-46
View File
@@ -1,46 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: znc
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: znc
policyTypes:
- Ingress
- Egress
ingress:
### Allow all ingress traffic (IRC bouncer needs external connections)
- {}
###
egress:
### Allow DNS resolution
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
###
### Allow intra-namespace communication
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: irc
###
### Allow outbound to the world
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
###
+1 -3
View File
@@ -6,10 +6,8 @@ metadata:
name: znc
labels:
app.kubernetes.io/name: znc
annotations:
external-dns.alpha.kubernetes.io/hostname: ${ZNC_HOSTNAME}
spec:
type: LoadBalancer
type: ClusterIP
ports:
- port: 6501
selector:
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: znc
namespace: irc
automountServiceAccountToken: false
+2
View File
@@ -30,6 +30,7 @@ spec:
app.kubernetes.io/instance: znc
spec:
priorityClassName: low-priority
serviceAccountName: znc
automountServiceAccountToken: false
containers:
- name: znc
@@ -73,6 +74,7 @@ spec:
- metadata:
name: config
spec:
storageClassName: ceph-block
accessModes:
- ReadWriteOnce
resources: