diff --git a/tenants/acme/00-namespace-quota-netpol.yaml b/tenants/acme/00-namespace-quota-netpol.yaml new file mode 100644 index 0000000..7266431 --- /dev/null +++ b/tenants/acme/00-namespace-quota-netpol.yaml @@ -0,0 +1,64 @@ +# Silo "demo" — isolamento do tenant (namespace + quota + limites + rede) +apiVersion: v1 +kind: Namespace +metadata: + name: acme-prod + labels: + name: acme-prod + tenant: acme + athleticmap.io/tier: pilot +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: tenant-quota + namespace: acme-prod +spec: + hard: + requests.cpu: "2" + requests.memory: 2Gi + limits.cpu: "4" + limits.memory: 6Gi + pods: "20" + persistentvolumeclaims: "4" +--- +apiVersion: v1 +kind: LimitRange +metadata: + name: defaults + namespace: acme-prod +spec: + limits: + - type: Container + default: + cpu: 500m + memory: 512Mi + defaultRequest: + cpu: 100m + memory: 128Mi +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: deny-cross-tenant + namespace: acme-prod +spec: + podSelector: {} + policyTypes: [Ingress, Egress] + ingress: + - from: + - podSelector: {} + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + egress: + - to: # intra-namespace (pods) + ClusterIPs (VIP de service, pre-DNAT) + - podSelector: {} + - ipBlock: { cidr: 10.43.0.0/16 } + - to: # DNS (CoreDNS em kube-system) + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + ports: + - { protocol: UDP, port: 53 } + - { protocol: TCP, port: 53 } diff --git a/tenants/acme/05-sealed-db-credentials.yaml b/tenants/acme/05-sealed-db-credentials.yaml new file mode 100644 index 0000000..59f688c --- /dev/null +++ b/tenants/acme/05-sealed-db-credentials.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + creationTimestamp: null + name: db-credentials + namespace: acme-prod +spec: + encryptedData: + password: AgBFRogG+gGWgmRSXwsb3PfXhORsSoHGN0nREc98Q4KmoP0f2l9pgN9sDsX0sXNU/x4LG6eSoC6gXArU1dRplw6MkijPtWfhXWDQE7hCmhVDhBp/F+3jqDjzX0Z/1q7e8yopZuqZH3uIIagQ/SMlBCzHsm6ZOW43LoWGvrHCX94QCL3ZzuTX7t1UtO5SbRj/ooUMLDC33LqSrDvH+aAEJMEQimOYT8gbE1W1CN1VYR2A2Rac8lOUyqIFUMwEuyMvws98UEAEecilQRlmGGQJj2ORgYjFQRZCF0jDXbLYJmUxnPVAA3un3tfgABLg/S6KU/fqvIazk+OCknwXmZyQdB35lJcXxMNPuMYQ0mIdaSCDTpuZzUIF/6cdijjB+FvNMdEjTlSLcHlkQT3ABemyCpCrA00Otg+wVle0RGvhaj/2fsEYe28hNc72QO2IL53Ggboraho0BiIaxcnwVovHXno+URO4HmQsg0iecfhS09trXOMPYTf+Ire0NLSGE06kZqvRnqtBWpQYbr0r4rRV5rdF7pRGIgl09txMQ5VCKdUgoufGgQMGOquVWIUnViHTBU63vnGsOeaAr0A8bt2jQ0k4AaG3NmoPP5ROgw5U4y8ddQ9J5p1yd0RkeZ8d3605vnPXEzNRSmLhaBpT6EHg5nwQ/1vpPt4Bnjuk5CKUY1q4aSDJAzRfPfful8/LJzAAINji0e9cdKMn4Mt0bAS9P1MMCHFsOLRLdFGIUO9lBvME6A== + template: + metadata: + creationTimestamp: null + name: db-credentials + namespace: acme-prod diff --git a/tenants/acme/06-sealed-keycloak-admin.yaml b/tenants/acme/06-sealed-keycloak-admin.yaml new file mode 100644 index 0000000..dd2e53c --- /dev/null +++ b/tenants/acme/06-sealed-keycloak-admin.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + creationTimestamp: null + name: keycloak-admin + namespace: acme-prod +spec: + encryptedData: + password: AgCD4+27tP4eujj+4aruY5qQy7JVfahQBasYRh5uz/PdYbRathBDGl5scygR0Zf+hg5Qaoem7VRO1ilBE3Uzjm/EvS2iD/pLasQP1n6ZJYgX91+HQnbORNjrTAKuZ/Jk/rzz3mcADdNl3LwEYy71iKELlmlV8zSmDgpzT8U6AkdoUc1xPErpzBch9ChpVnyTcnXz2rb8ni1zjzrG42p6lf42U9glTy/YvztouZiGggJahTPC5ChcHpWcj+1ILFt8/Lbovrv+8vODXzBg3Jt3Oe+CB0bUSVbSrMudCeFu+raBbfGiUZnpT6pBa4j8rjHL5qFmxxtjfHgNcrhJIi6b3KFt4yLRCM8c24wg29rKSRb8hTV2tLGuao4uAT9HxVW3x29hzgR+Lvj4yRGpKf7rzGJKkSF+dHrAbcaYTIWnYeMGGFKnT1edWFAB2JimDrnmjRJF8eGca/2JVL9P+mvElJetcVMbNd36310fE0Fu36Xlr/CK3gNGWCepsBdmIy7A38UlT7MArJ83bJXrKSFA0qdwZQ/oHB2WE3so/9YfPwJ8MNqv2ROf/MYRjlvL3Yx1K3GxzmR+L7nrEf/KrhsZOibVt81rHdtoH1/VZ/AeH3V2kYp9HPZi/yq1KrlzTuQWvL7mQrXqMC7GnNSzaD2fcTp0LGjshfJdzsIXW9mo5lBarWt6QW5VrR0vf74kHUGwPnS8CK5qVz8OufFHf1faN7q0qrVVjWrU46I= + template: + metadata: + creationTimestamp: null + name: keycloak-admin + namespace: acme-prod diff --git a/tenants/acme/10-postgres.yaml b/tenants/acme/10-postgres.yaml new file mode 100644 index 0000000..6e6edc0 --- /dev/null +++ b/tenants/acme/10-postgres.yaml @@ -0,0 +1,83 @@ +# PostgreSQL dedicado do tenant demo (banco da aplicação + banco do Keycloak) +apiVersion: v1 +kind: ConfigMap +metadata: + name: pg-initdb + namespace: acme-prod +data: + 01-keycloak.sql: | + CREATE DATABASE keycloak; +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-data + namespace: acme-prod +spec: + accessModes: [ReadWriteOnce] + storageClassName: local-path + resources: + requests: + storage: 5Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres + namespace: acme-prod +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:16 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_DB + value: athleticmap + - name: POSTGRES_USER + value: atm + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: password + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + - name: initdb + mountPath: /docker-entrypoint-initdb.d + readinessProbe: + exec: + command: ["pg_isready", "-U", "atm", "-d", "athleticmap"] + initialDelaySeconds: 10 + periodSeconds: 5 + volumes: + - name: data + persistentVolumeClaim: + claimName: postgres-data + - name: initdb + configMap: + name: pg-initdb +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: acme-prod +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 diff --git a/tenants/acme/20-keycloak.yaml b/tenants/acme/20-keycloak.yaml new file mode 100644 index 0000000..b168409 --- /dev/null +++ b/tenants/acme/20-keycloak.yaml @@ -0,0 +1,86 @@ +# Keycloak dedicado do tenant demo (IdP do silo) — modo dev, persistindo no Postgres +apiVersion: apps/v1 +kind: Deployment +metadata: + name: keycloak + namespace: acme-prod +spec: + replicas: 1 + selector: + matchLabels: + app: keycloak + template: + metadata: + labels: + app: keycloak + spec: + containers: + - name: keycloak + image: quay.io/keycloak/keycloak:26.0 + args: ["start-dev", "--import-realm"] + env: + - name: KC_DB + value: postgres + - name: KC_DB_URL + value: "jdbc:postgresql://postgres:5432/keycloak" + - name: KC_DB_USERNAME + value: atm + - name: KC_DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: password + - name: KC_BOOTSTRAP_ADMIN_USERNAME + value: admin + - name: KC_BOOTSTRAP_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-admin + key: password + - name: KC_HEALTH_ENABLED + value: "true" + - name: KC_HTTP_ENABLED + value: "true" + - name: KC_PROXY_HEADERS + value: xforwarded + - name: KC_HOSTNAME + value: "auth-acme.athleticmap.influxdigital.com.br" + - name: KC_HOSTNAME_STRICT + value: "true" + ports: + - containerPort: 8080 + - containerPort: 9000 + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "1" + memory: 1Gi + readinessProbe: + httpGet: + path: /health/ready + port: 9000 + initialDelaySeconds: 30 + periodSeconds: 10 + failureThreshold: 40 + volumeMounts: + - name: realm-import + mountPath: /opt/keycloak/data/import + readOnly: true + volumes: + - name: realm-import + configMap: + name: kc-realm-import +--- +apiVersion: v1 +kind: Service +metadata: + name: keycloak + namespace: acme-prod +spec: + selector: + app: keycloak + ports: + - port: 8080 + targetPort: 8080 diff --git a/tenants/acme/30-apps-stubs.yaml b/tenants/acme/30-apps-stubs.yaml new file mode 100644 index 0000000..6eeaeec --- /dev/null +++ b/tenants/acme/30-apps-stubs.yaml @@ -0,0 +1,150 @@ +# Apps do tenant demo: +# - backend: Spring Boot OAuth2 Resource Server (imagem athletic-map-backend:1.2, porta 8083) +# - frontend: SPA OIDC Authorization Code + PKCE (keycloak-js) chamando /api/me +# - bff: stub (whoami) +--- +apiVersion: apps/v1 +kind: Deployment +metadata: { name: backend, namespace: acme-prod } +spec: + replicas: 1 + selector: { matchLabels: { app: backend } } + template: + metadata: { labels: { app: backend } } + spec: + containers: + - name: backend + image: docker.io/library/athletic-map-backend:1.2 + imagePullPolicy: Never + env: + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-acme.athleticmap.influxdigital.com.br/realms/athleticmap" } + - { name: ATM_TENANT, value: "acme" } + ports: [{ containerPort: 8083 }] + readinessProbe: + httpGet: { path: /api/public/health, port: 8083 } + initialDelaySeconds: 20 + periodSeconds: 10 + failureThreshold: 24 +--- +apiVersion: v1 +kind: Service +metadata: { name: backend, namespace: acme-prod } +spec: + selector: { app: backend } + ports: [{ port: 80, targetPort: 8083 }] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: { name: bff, namespace: acme-prod } +spec: + replicas: 1 + selector: { matchLabels: { app: bff } } + template: + metadata: { labels: { app: bff } } + spec: + containers: + - name: whoami + image: traefik/whoami:latest + args: ["--name", "athletic-map-bff acme (stub)"] + ports: [{ containerPort: 80 }] +--- +apiVersion: v1 +kind: Service +metadata: { name: bff, namespace: acme-prod } +spec: + selector: { app: bff } + ports: [{ port: 80, targetPort: 80 }] +--- +apiVersion: v1 +kind: ConfigMap +metadata: { name: frontend-index, namespace: acme-prod } +data: + index.html: | + +
+Carregando…