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
This commit is contained in:
Mukul Sharma
2026-09-09 12:35:35 +05:30
co-authored by Claude Opus 5
parent 9014a170d7
commit 4a5f4d94ab
@@ -85,3 +85,47 @@ roleRef:
apiGroup: rbac.authorization.k8s.io apiGroup: rbac.authorization.k8s.io
kind: ClusterRole kind: ClusterRole
name: toolshed-deployer name: toolshed-deployer
---
# A custom domain's Ingress and TLS secret live in the gateway's own
# namespace (toolshed, same as above) — never an app's namespace. Scoped
# with a namespaced Role/RoleBinding rather than widening the ClusterRole
# above: Ingress management here only ever targets this one fixed
# namespace, unlike the per-app namespaces the ClusterRole necessarily
# spans. Added alongside toolshed's custom-domains feature — see
# internal/deploy.Client.EnsureDomainIngress/DomainCertReady/
# RemoveDomainIngress and this file's own header comment about keeping it
# and deploy/helm/toolshed/templates/rbac.yaml in sync.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: toolshed-deployer-ingress
namespace: toolshed
labels:
app.kubernetes.io/part-of: toolshed
rules:
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Reads the TLS secret cert-manager's ingress-shim populates for a domain
# Ingress, and deletes it (and the Ingress above) when a domain is
# removed. Never create/update — cert-manager, not deployer, writes this
# secret.
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: toolshed-deployer-ingress
namespace: toolshed
labels:
app.kubernetes.io/part-of: toolshed
subjects:
- kind: ServiceAccount
name: toolshed-deployer
namespace: toolshed
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: toolshed-deployer-ingress