Commit Graph
37 Commits
Author SHA1 Message Date
Mukul SharmaandClaude Opus 5 3aebc99635 Grant deployer StatefulSet rights, for apps with persistent storage
toolshed deploys an app that asked for a persistent volume as a StatefulSet
with a volumeClaimTemplate rather than a Deployment
(internal/deploy.Client.ensureStatefulSet). Without this the deployer gets
"forbidden" the moment anyone creates one — the two-repositories drift
internal/deploy/kubernetes.go's own package doc warns about, and the same
way the custom-domains Ingress rights were missed until after that feature
shipped.

Delete is included deliberately, not for tidiness: a Deployment and a
StatefulSet share the app's selector, so switching an app between stateless
and stateful must remove whichever controller it no longer is, or both stay
alive fighting over the same pods.

persistentvolumeclaims is read-only. The claims are created by the
StatefulSet's own volumeClaimTemplates, never directly by toolshed, and
deleting one would destroy an app's data — so there is no reason for this
credential to be able to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF
2026-09-09 15:16:16 +05:30
mukul dde2622149 Merge pull request 'Toolshed redis and domain rbac' (#1) from toolshed-redis-and-domain-rbac into main
Reviewed-on: http://gitea.100.90.248.118.nip.io/mukul/devops-infra-argo-config/pulls/1
2026-09-09 07:29:49 +00:00
Mukul SharmaandClaude Opus 5 dcd3583656 Deploy Redis, backing toolshed's managed cache add-on
Registers the new hand-written redis chart (devops-infra-helm-charts,
separate commit) and the ExternalSecret feeding its admin password from
Vault. Own namespace, addressed over cluster DNS like every other platform
component here:

  redis.redis.svc.cluster.local:6379

Only one consumer for the credential, unlike the Postgres one next door:
the server itself, to seed its ACL file on first boot. toolshed's api gets
it from the connection an operator configures in the dashboard, encrypted
in toolshed's own database — so there is deliberately no second
ExternalSecret into the toolshed namespace.

Order matters: put the password in Vault at secret/toolshed/redis before
syncing, or the init container sits in CreateContainerConfigError. The
exact command, the reason the password must be alphanumeric (it is written
into an ACL directive where a space or quote would split it), and the
manual rotation procedure are all recorded in the ExternalSecret's own
header.

Nothing here needs to change for Postgres: toolshed's managed database
add-on points at the existing postgresql.postgres.svc.cluster.local, whose
POSTGRES_USER is the initdb superuser and so already has the CREATEDB and
CREATEROLE that provisioning needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF
2026-09-09 12:35:46 +05:30
Mukul SharmaandClaude Opus 5 4a5f4d94ab Grant deployer Ingress rights in its own namespace, for custom domains
Overdue: this belongs with toolshed's custom-domains feature, which has
already merged and shipped. Without it, deployer gets "forbidden" the
moment anyone actually adds a custom domain — the exact failure mode
internal/deploy/kubernetes.go's own package doc warns about, since its
resource list and this file are unsynchronised copies in two repositories.

Scoped as a namespaced Role/RoleBinding rather than widening the
ClusterRole above it. A custom domain's Ingress and TLS secret live in the
gateway's own namespace and never in an app's, so this only ever targets
one fixed namespace — unlike the per-app namespaces the ClusterRole
necessarily spans. Secrets are get/list/watch/delete only: cert-manager
writes them, deployer just reads readiness and cleans up on removal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF
2026-09-09 12:35:35 +05:30
Mukul SharmaandClaude Opus 5 9014a170d7 Add autoscaling/horizontalpodautoscalers to deployer's ClusterRole
Companion change to toolshed's new multi-replica/autoscaling feature
(internal/deploy.Client.ensureAutoscaler in the toolshed repo) — without
this, deployer's own attempt to create a HorizontalPodAutoscaler for any
app with autoscaling enabled fails with "forbidden" the first time
someone actually uses the feature, exactly the failure mode
internal/deploy/kubernetes.go's own package doc comment warns about
for these two unsynchronized copies of deployer's permission list.

Kept in sync with toolshed's own deploy/helm/toolshed/templates/rbac.yaml,
which received the identical addition.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF
2026-09-08 06:58:22 +05:30
Mukul Sharma 94a055b341 Add quant-agent ExternalSecrets 2026-09-07 07:10:56 +05:30
Mukul SharmaandClaude Opus 5 89a0aacbd7 Add toolshed-harbor-cleanup: a delete-scoped Harbor robot for builder
Deliberately separate from harbor-robot-dockerconfig, which is mounted
into build pods running arbitrary user Dockerfiles and is scoped to
push+pull only. This one is held by builder itself — a trusted platform
service that never executes user code directly — and carries a
permission the other should never have: delete. A leaked build-pod
credential can never delete anything this way, and a leaked cleanup
credential can never push.

Used by toolshed's cleanupImage (internal/builder/builder.go) to remove
an app's Harbor repository when the app itself is deleted. Nothing
destructive happens until the Vault path this pulls from is actually
populated — builder logs and skips that step otherwise, per its own
code comment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF
2026-09-06 21:17:45 +05:30
Mukul SharmaandClaude Opus 5 cab110c268 Add grafana appSpec entry and its admin-credentials ExternalSecret
Matches the chart-side addition in devops-infra-helm-charts. The
ExternalSecret needs its Vault path populated before either this or
the grafana Application syncs — see that file's own header for the
exact vault kv put command, same requirement postgres and every other
admin credential in this repo already has.

app-of-secretstores.yaml is automated (prune: true), so this new
secretstore file needs no separate manual sync of its own — only that
app-of-apps wrapper, same as any other new file under secretstores/.

Verified with `helm template` against generic-argo-apps-chart and this
values file: 12 Applications render, grafana present and correctly
formed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF
2026-09-06 08:57:42 +05:30
Mukul SharmaandClaude Opus 5 919ebd5ab8 Replace the prometheus appSpec entry with victoria-metrics-single/vmagent/node-exporter
Matches the chart-side replacement in devops-infra-helm-charts. Three
Applications now where there was one, since victoria-metrics-single
bundles no scraper or exporter the way the Prometheus chart did.

Sync order matters here specifically: victoria-metrics-single needs to
exist before vmagent's remote_write target resolves, though ArgoCD sync
failures are naturally retried so this only affects how quickly it
converges, not whether it does.

Verified with `helm template` against generic-argo-apps-chart and this
values file: 11 Applications render, the three new ones present and
correctly formed, nothing left pointing at the removed prometheus
chartDir.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF
2026-09-06 08:38:56 +05:30
Mukul SharmaandClaude Opus 5 1c6c6fc2c3 Register the prometheus appSpec entry
Points at the chart and values just added to devops-infra-helm-charts.
nameOverride pinned to "prometheus" so the Helm release name matches
what the chart's server Service naming depends on
("<release-name>-server") — see that repo's commit for the full
reasoning, since this file is the one place that decision has to be
made correctly for toolshed's already-seeded PROMETHEUS_URL to resolve.

Needs the usual two syncs to actually deploy: this values file only
takes effect once incubator-infra-k8s-admin-prd-ase1 is synced (which
creates/updates the child "prometheus" Application object), and that
child Application then needs its own sync to actually create anything
in the cluster.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF
2026-09-06 07:30:51 +05:30
Mukul Sharma 9f770e5ac7 Add toolshed's credential encryption keys 2026-09-05 08:54:14 +05:30
Mukul Sharma 48ecd1db73 Add Gitea API token for toolshed's webhook management 2026-09-05 02:22:42 +05:30
Mukul Sharma 2f14a256c5 deployer: read pod logs, so an app's output reaches the dashboard 2026-09-05 01:55:47 +05:30
Mukul Sharma f23fc5f03c deployer: permission to manage app configuration Secrets
Environment variables are delivered through a Secret so values never appear
in the pod spec, but the ClusterRole was never given the resource — so every
deploy with configuration failed on a forbidden error, minutes after the
change looked fine.

Granted without list or watch. Kubernetes RBAC cannot scope a ClusterRole to
a namespace pattern, so this necessarily covers every namespace; withholding
list at least stops deployer enumerating the cluster's secrets, leaving only
access by a name it already knows. That narrows the blast radius rather than
removing it, and is called out in the manifest.

The proper fix, once there are tenants who are not the operator, is a
RoleBinding created per app namespace. That requires deployer to be able to
create RoleBindings, which is its own escalation path and wants deciding
deliberately rather than being slipped in here.
2026-09-05 01:49:49 +05:30
Mukul Sharma 6f37fa0fd3 Add toolshed deployer's cluster RBAC
Scoped to the resource kinds toolshed creates for an app — not
cluster-admin, not a wildcard. This is the one credential in the system
whose compromise means the cluster, so what it can do should fit on one
screen.

Here rather than in toolshed's chart because these are cluster-scoped and
that Application runs in the webapp project, which permits only Namespace.
Widening webapp would have given every demo app in it the ability to create
cluster-wide RBAC.
2026-09-04 22:42:27 +05:30
Mukul Sharma 0eeef50781 Add Jenkins API credentials for toolshed's builder
A dedicated Jenkins user with an API token, not the admin account. Needs
Job/Create as well as Build and Read, because builder creates an app's job
on first deploy — which is what guarantees the SCM polling trigger exists
and so that a webhook can ever find it (issue #30).
2026-09-04 20:33:21 +05:30
Mukul Sharma 83f4aa0259 Add the toolshed session signing key from Vault
Only api gets the private half. The gateway is given the public half in
plain Helm values, and that asymmetry is the point: the gateway
terminates traffic for every deployed app, so holding only a verification
key means compromising it cannot forge a session for anyone.

The two halves must be installed together — a private key here that does
not match the public key in the gateway's values means every session api
issues is rejected and every app redirects to login forever.
2026-09-04 17:06:50 +05:30
Mukul Sharma 84f5e27238 Add postgresql to appSpec and its Vault-backed credentials
One Vault path (secret/toolshed/postgres) into two namespaces: the server
needs the credential to initialise, api needs it to connect. Kept in one
file because it is one credential with two consumers — split, they drift,
and drift surfaces as an authentication error that looks nothing like a
config mistake.

The api-side Secret also carries a composed libpq URL, so host, port and
database name live in one place instead of across several environment
variables that can disagree.

Postgres gets its own namespace rather than living inside toolshed, so it
is addressed over cluster DNS like any other platform component and
outlives whatever consumes it.
2026-09-04 16:21:36 +05:30
Mukul Sharma ec049c0671 Remove demo-go-app-image-pull-secret (homelab Harbor project now public)
No longer needed now that anonymous pull works — pairs with
devops-helm-charts#efc2c4f dropping the corresponding pullSecret
reference from demo-go-app's values. app-of-secretstores has automated
prune, so this deletion applies on its own on next sync.
2026-09-03 06:41:24 +05:30
Mukul Sharma 33ae7a2104 Add demo-go-app-image-pull-secret; rekey harbor-robot-dockerconfig
Both needed for the registry hostname switch to
harbor.192.168.1.7.nip.io (devops-lib#3ffa2d8):

- harbor-robot-dockerconfig's auths key was hardcoded to
  harbor-core.harbor.svc.cluster.local — docker matches stored
  credentials by exact registry hostname, so this had to move with
  the image reference or push would go back to unauthorized.
- New demo-go-app-image-pull-secret, in the demo-go-app namespace
  (imagePullSecrets must be same-namespace as the pod), for the
  Deployment's own pull — separate from harbor-robot-dockerconfig,
  which only ever lived in the jenkins namespace for the build pod's
  push. Same underlying robot account/Vault path.
2026-09-02 23:54:50 +05:30
Mukul Sharma 27c0445074 Add PriorityClass, ArgoCD jenkins-ci token, and Harbor robot secretstores
extra-manifests/priorityclass-high-priority.yaml: cluster-side
PriorityClass backing the app chart's hardcoded priorityClassName.
secretstores/argocd-jenkins-ci-token.yaml: ExternalSecret for the
scoped ArgoCD account devops-lib's syncArgoApp stage authenticates
with.
secretstores/harbor-robot-dockerconfig.yaml: ExternalSecret building a
dockerconfigjson from the Harbor robot account for image pulls.
2026-09-02 01:29:39 +05:30
Mukul Sharma 944ba711e2 harbor ingress fix 2026-08-31 14:40:18 +05:30
Mukul Sharma f7e37d18b4 added harbor app 2026-08-31 13:18:18 +05:30
Mukul Sharma f722a8bc9c added jenkins 2026-08-31 09:40:54 +05:30
Mukul Sharma c8bda77bd5 fix for secret sa 2026-08-31 09:13:44 +05:30
Mukul Sharma 4719d0305f added secret 2026-08-31 09:02:30 +05:30
Mukul Sharma 675dfe4595 fix for external secret 2026-08-31 08:04:13 +05:30
Mukul Sharma e0378cb43e added external-secret 2026-08-31 07:56:21 +05:30
Mukul Sharma d4a19467f3 added vault and contour 2026-08-31 07:45:45 +05:30
Mukul Sharma be12e34f77 fix argocd 2026-08-31 01:54:57 +05:30
Mukul Sharma 680b97582c fix 2026-08-31 01:41:21 +05:30
Mukul Sharma fe4c7aebd9 fix 2026-08-31 01:25:13 +05:30
Mukul Sharma 7f1ef661be fix 2026-08-31 01:13:53 +05:30
Mukul Sharma 2113642f3c add 2026-08-31 01:02:22 +05:30
Mukul Sharma 51e8bb53e2 fix 2026-08-31 00:49:44 +05:30
Your Name 1055e1394f added repo 2026-08-26 04:03:34 +05:30
mukul 2389ec1fd6 Initial commit 2026-08-25 22:13:05 +00:00