apiVersion: apps/v1 kind: Deployment metadata: name: gsc-shell-api namespace: gsc-shell labels: app.kubernetes.io/name: gsc-shell-api app.kubernetes.io/component: api spec: replicas: 2 selector: matchLabels: app.kubernetes.io/name: gsc-shell-api template: metadata: labels: app.kubernetes.io/name: gsc-shell-api # KEYCLOAK_DISCOVERY_URL points at the in-cluster service, but the # discovery doc returns jwks_uri = https://auth.gosec.cloud/... # (the canonical issuer claim). JWKS fetch needs public egress. # allow-web-proxy GlobalNetworkPolicy gates on this label. egress-internet: "true" spec: containers: - name: api image: registry.gosec.internal/gsc-shell-api:v0.1.4 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8080 env: - name: PORT value: "8080" # Route public-internet calls (notably JWKS at auth.gosec.cloud) # through the in-cluster Squid proxy. Go's http.ProxyFromEnvironment # picks these up automatically — no code change needed. - name: HTTP_PROXY value: "http://web-proxy.web-proxy.svc.cluster.local:3128" - name: HTTPS_PROXY value: "http://web-proxy.web-proxy.svc.cluster.local:3128" - name: NO_PROXY value: "localhost,127.0.0.1,.cluster.local,.svc,.gosec.internal" - name: KEYCLOAK_ISSUER value: "https://auth.gosec.cloud/realms/gosecCloud" # KEYCLOAK_DISCOVERY_URL intentionally NOT set. When set, # keycloak.go builds a custom http.Client with no Proxy field — # which then defeats HTTPS_PROXY for the JWKS fetch (jwks_uri # is the public hostname even when discovery hits an internal # URL). Falling through to go-oidc's default client uses # http.DefaultTransport.Proxy = http.ProxyFromEnvironment, so # both discovery and JWKS go through web-proxy correctly. - name: CACHE_TTL_SECONDS value: "60" - name: DATABASE_URL valueFrom: secretKeyRef: name: gsc-shell-api-db key: database-url livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 30 readinessProbe: httpGet: path: /readyz port: 8080 initialDelaySeconds: 3 periodSeconds: 10 resources: requests: cpu: 50m memory: 64Mi limits: cpu: 250m memory: 256Mi securityContext: runAsNonRoot: true runAsUser: 1000 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: [ALL] imagePullSecrets: - name: registry-credentials affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app.kubernetes.io/name: gsc-shell-api topologyKey: kubernetes.io/hostname --- apiVersion: v1 kind: Service metadata: name: gsc-shell-api namespace: gsc-shell labels: app.kubernetes.io/name: gsc-shell-api spec: type: ClusterIP selector: app.kubernetes.io/name: gsc-shell-api ports: - name: http port: 8080 targetPort: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gsc-shell-api namespace: gsc-shell annotations: nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" spec: ingressClassName: nginx tls: - hosts: - shell-api.gosec.internal secretName: gsc-shell-api-tls rules: - host: shell-api.gosec.internal http: paths: - path: / pathType: Prefix backend: service: name: gsc-shell-api port: number: 8080