From 624160b8ad5f554007282529646cd1fe412b6060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Huss?= Date: Thu, 12 Oct 2023 09:18:09 +0200 Subject: [PATCH] fix --- apps/dbgate/configs.tf | 35 +++++++++ apps/dbgate/datas.tf | 22 ++++++ apps/dbgate/deploy.tf | 75 +++++++++++++++++++ apps/dbgate/forward.tf | 161 +++++++++++++++++++++++++++++++++++++++++ apps/dbgate/index.yaml | 119 ++++++++++++++++++++++++++++++ apps/dbgate/ingress.tf | 76 +++++++++++++++++++ apps/dbgate/pvc.tf | 17 +++++ apps/dbgate/svc.tf | 18 +++++ 8 files changed, 523 insertions(+) create mode 100644 apps/dbgate/configs.tf create mode 100644 apps/dbgate/datas.tf create mode 100644 apps/dbgate/deploy.tf create mode 100644 apps/dbgate/forward.tf create mode 100644 apps/dbgate/index.yaml create mode 100644 apps/dbgate/ingress.tf create mode 100644 apps/dbgate/pvc.tf create mode 100644 apps/dbgate/svc.tf diff --git a/apps/dbgate/configs.tf b/apps/dbgate/configs.tf new file mode 100644 index 0000000..e3a1f66 --- /dev/null +++ b/apps/dbgate/configs.tf @@ -0,0 +1,35 @@ +locals { + postgresqls = merge(flatten([ + for pg in var.postgresql: { + join("_",["LABEL_pg", pg.namespace, pg.name]) = join("|",["pg", pg.namespace, pg.name]) + join("_",["SERVER_pg", pg.namespace, pg.name]) = join("|",["pg", pg.namespace, pg.name]) + join("_",["ENGINE_pg", pg.namespace, pg.name]) = join("|",["pg", pg.namespace, pg.name]) + } + ])) + //marias = [] + //mongos = [] + //connections = join(",",concat(local.postgresqls, local.marias)) + connections = "" + datas = {} + secrets = {} +} + +resource "kubectl_manifest" "dbgate-config" { + yaml_body = <<-EOF + apiVersion: v1 + kind: ConfigMap + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + data: ${jsonencode(local.datas)} + EOF +} + +resource "kubernetes_secret_v1" "dbgate-config-secret" { + metadata { + name = "${var.component}-${var.instance}" + namespace = var.namespace + } + data = local.secrets +} diff --git a/apps/dbgate/datas.tf b/apps/dbgate/datas.tf new file mode 100644 index 0000000..ac5f6fe --- /dev/null +++ b/apps/dbgate/datas.tf @@ -0,0 +1,22 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = "${var.domain}-auth" + } +} + +data "kustomization_overlay" "data" { + resources = [] +} diff --git a/apps/dbgate/deploy.tf b/apps/dbgate/deploy.tf new file mode 100644 index 0000000..96ec681 --- /dev/null +++ b/apps/dbgate/deploy.tf @@ -0,0 +1,75 @@ +resource "kubectl_manifest" "deploy" { + yaml_body = <<-EOF + apiVersion: apps/v1 + kind: Deployment + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + replicas: 1 + selector: + matchLabels: ${jsonencode(local.common-labels)} + template: + metadata: + labels: ${jsonencode(local.common-labels)} + spec: + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + containers: + - name: dbgate + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + envFrom: + - secretRef: + name: "${var.component}-${var.instance}" + - configMapRef: + name: "${var.component}-${var.instance}" + env: + - name: CONNECTIONS + value: ${local.connections} + image: "${var.images.dbgate.registry}/${var.images.dbgate.repository}:${var.images.dbgate.tag}" + imagePullPolicy: "${var.images.dbgate.pullPolicy}" + ports: + - containerPort: 3000 + name: http + protocol: TCP + livenessProbe: + failureThreshold: 3 + httpGet: + path: / + port: http + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + readinessProbe: + failureThreshold: 3 + httpGet: + path: / + port: http + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + volumeMounts: + - name: data + mountPath: /home/node/.dbgate + restartPolicy: Always + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + volumes: + - name: data + persistentVolumeClaim: + claimName: "${var.component}-${var.instance}" + - name: run + emptyDir: {} + EOF +} diff --git a/apps/dbgate/forward.tf b/apps/dbgate/forward.tf new file mode 100644 index 0000000..7d1c0e7 --- /dev/null +++ b/apps/dbgate/forward.tf @@ -0,0 +1,161 @@ +locals { + authentik-token = data.kubernetes_secret_v1.authentik.data["AUTHENTIK_BOOTSTRAP_TOKEN"] + request_headers = { + "Content-Type" = "application/json" + Authorization = "Bearer ${local.authentik-token}" + } + forward-outpost-providers = jsondecode(data.http.get_forward_outpost.response_body).results[0].providers + forward-outpost-pk = jsondecode(data.http.get_forward_outpost.response_body).results[0].pk + app-name = var.component == var.instance ? var.instance : format("%s-%s", var.component, var.instance) + app-icon = "_static/src/browser/media/favicon-dark-support.svg" + main-group = format("app-%s", local.app-name) + sub-groups = [] + external-url = format("https://%s", local.dns-names[0]) + access-token-validity = "hours=10" // ;minutes=10 + rules-icons = [ for v in local.dns-names : { + "host" = "${v}" + "http" = { + "paths" = [{ + "backend" = { + "service" = local.service + } + "path" = "/${local.app-icon}" + "pathType" = "Prefix" + }] + } + }] +} + +resource "kubectl_manifest" "prj_ingress_icon" { + force_conflicts = true + yaml_body = <<-EOF + apiVersion: "networking.k8s.io/v1" + kind: "Ingress" + metadata: + name: "${var.instance}-icons" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + ingressClassName: "${var.ingress-class}" + rules: ${jsonencode(local.rules-icons)} + tls: + - hosts: ${jsonencode(local.dns-names)} + secretName: "${var.instance}-cert" + EOF +} + +data "authentik_flow" "default-authorization-flow" { + depends_on = [authentik_group.prj_users] + slug = "default-provider-authorization-implicit-consent" +} + +resource "authentik_provider_proxy" "prj_forward" { + name = local.app-name + external_host = local.external-url + authorization_flow = data.authentik_flow.default-authorization-flow.id + mode = "forward_single" + access_token_validity = local.access-token-validity +} + + +resource "authentik_application" "prj_application" { + name = local.app-name + slug = "${var.component}-${var.instance}" + group = var.app-group + protocol_provider = authentik_provider_proxy.prj_forward.id + meta_launch_url = local.external-url + meta_icon = format("%s/%s", local.external-url, local.app-icon) +} + +resource "authentik_group" "prj_users" { + name = local.main-group + attributes = jsonencode({"${local.app-name}" = true}) +} + +resource "authentik_group" "subgroup" { + count = length(local.sub-groups) + name = format("%s-%s", local.app-name, local.sub-groups[count.index]) + parent = authentik_group.prj_users.id +} + +resource "authentik_policy_expression" "policy" { + name = local.main-group + expression = <<-EOF + attr = request.user.group_attributes() + return attr['${local.app-name}'] if '${local.app-name}' in attr else False + EOF +} + +data "authentik_group" "vynil-admin" { + depends_on = [authentik_group.prj_users] # fake dependency so it is not evaluated at plan stage + name = "vynil-forward-admins" +} + +resource "authentik_policy_binding" "prj_access_users" { + target = authentik_application.prj_application.uuid + policy = authentik_policy_expression.policy.id + order = 0 +} +resource "authentik_policy_binding" "prj_access_vynil" { + target = authentik_application.prj_application.uuid + group = data.authentik_group.vynil-admin.id + order = 1 +} + +data "http" "get_forward_outpost" { + depends_on = [authentik_provider_proxy.prj_forward] + url = "http://authentik.${var.domain}-auth.svc/api/v3/outposts/instances/?name__iexact=forward" + method = "GET" + request_headers = local.request_headers + lifecycle { + postcondition { + condition = contains([200], self.status_code) + error_message = "Status code invalid" + } + } +} + +provider "restapi" { + uri = "http://authentik.${var.domain}-auth.svc/api/v3/" + headers = local.request_headers + create_method = "PATCH" + update_method = "PATCH" + destroy_method = "PATCH" + write_returns_object = true + id_attribute = "name" +} + +resource "restapi_object" "forward_outpost_binding" { + path = "/outposts/instances/${local.forward-outpost-pk}/" + data = jsonencode({ + name = "forward" + providers = contains(local.forward-outpost-providers, authentik_provider_proxy.prj_forward.id) ? local.forward-outpost-providers : concat(local.forward-outpost-providers, [authentik_provider_proxy.prj_forward.id]) + }) +} + +resource "kubectl_manifest" "prj_middleware" { + yaml_body = <<-EOF + apiVersion: traefik.containo.us/v1alpha1 + kind: Middleware + metadata: + name: "forward-${local.app-name}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + forwardAuth: + address: http://ak-outpost-forward.${var.domain}-auth.svc:9000/outpost.goauthentik.io/auth/traefik + trustForwardHeader: true + authResponseHeaders: + - X-authentik-username + # - X-authentik-groups + # - X-authentik-email + # - X-authentik-name + # - X-authentik-uid + # - X-authentik-jwt + # - X-authentik-meta-jwks + # - X-authentik-meta-outpost + # - X-authentik-meta-provider + # - X-authentik-meta-app + # - X-authentik-meta-version + EOF +} diff --git a/apps/dbgate/index.yaml b/apps/dbgate/index.yaml new file mode 100644 index 0000000..e0e31b7 --- /dev/null +++ b/apps/dbgate/index.yaml @@ -0,0 +1,119 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: apps +metadata: + name: dbgate + description: null +options: + storage: + default: + accessMode: ReadWriteOnce + size: 1Gi + type: Filesystem + examples: + - accessMode: ReadWriteOnce + size: 1Gi + type: Filesystem + properties: + accessMode: + default: ReadWriteOnce + enum: + - ReadWriteOnce + - ReadOnlyMany + - ReadWriteMany + type: string + size: + default: 1Gi + type: string + type: + default: Filesystem + enum: + - Filesystem + - Block + type: string + type: object + images: + default: + dbgate: + pullPolicy: IfNotPresent + registry: docker.io + repository: dbgate/dbgate + tag: 5.2.6-alpine + examples: + - dbgate: + pullPolicy: IfNotPresent + registry: docker.io + repository: dbgate/dbgate + tag: 5.2.6-alpine + properties: + dbgate: + default: + pullPolicy: IfNotPresent + registry: docker.io + repository: dbgate/dbgate + tag: 5.2.6-alpine + properties: + pullPolicy: + default: IfNotPresent + enum: + - Always + - Never + - IfNotPresent + type: string + registry: + default: docker.io + type: string + repository: + default: dbgate/dbgate + type: string + tag: + default: 5.2.6-alpine + type: string + type: object + type: object + ingress-class: + default: traefik + examples: + - traefik + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + sub-domain: + default: dbgate + examples: + - dbgate + type: string + domain: + default: your-company + examples: + - your-company + type: string + app-group: + default: dev + examples: + - dev + type: string + domain-name: + default: your_company.com + examples: + - your_company.com + type: string +dependencies: +- dist: null + category: share + component: authentik-forward +- dist: null + category: core + component: secret-generator +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: true + http: true +tfaddtype: null diff --git a/apps/dbgate/ingress.tf b/apps/dbgate/ingress.tf new file mode 100644 index 0000000..19ecf4f --- /dev/null +++ b/apps/dbgate/ingress.tf @@ -0,0 +1,76 @@ + +locals { + dns-names = ["${var.sub-domain}.${var.domain-name}"] + middlewares = ["${var.instance}-https", "forward-${local.app-name}"] + service = { + "name" = "${var.component}-${var.instance}" + "port" = { + "number" = 80 + } + } + rules = [ for v in local.dns-names : { + "host" = "${v}" + "http" = { + "paths" = [{ + "backend" = { + "service" = local.service + } + "path" = "/" + "pathType" = "Prefix" + }] + } + }] +} + +resource "kubectl_manifest" "prj_certificate" { + yaml_body = <<-EOF + apiVersion: "cert-manager.io/v1" + kind: "Certificate" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + secretName: "${var.instance}-cert" + dnsNames: ${jsonencode(local.dns-names)} + issuerRef: + name: "${var.issuer}" + kind: "ClusterIssuer" + group: "cert-manager.io" + EOF +} + +resource "kubectl_manifest" "prj_https_redirect" { + yaml_body = <<-EOF + apiVersion: "traefik.containo.us/v1alpha1" + kind: "Middleware" + metadata: + name: "${var.instance}-https" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + redirectScheme: + scheme: "https" + permanent: true + EOF +} + +resource "kubectl_manifest" "prj_ingress" { + force_conflicts = true + yaml_body = <<-EOF + apiVersion: "networking.k8s.io/v1" + kind: "Ingress" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + annotations: + "traefik.ingress.kubernetes.io/router.middlewares": "${join(",", [for m in local.middlewares : format("%s-%s@kubernetescrd", var.namespace, m)])}" + spec: + ingressClassName: "${var.ingress-class}" + rules: ${jsonencode(local.rules)} + tls: + - hosts: ${jsonencode(local.dns-names)} + secretName: "${var.instance}-cert" + EOF +} diff --git a/apps/dbgate/pvc.tf b/apps/dbgate/pvc.tf new file mode 100644 index 0000000..1398514 --- /dev/null +++ b/apps/dbgate/pvc.tf @@ -0,0 +1,17 @@ +resource "kubectl_manifest" "pvc" { + yaml_body = <<-EOF + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + accessModes: + - "${var.storage.accessMode}" + resources: + requests: + storage: "${var.storage.size}" + volumeMode: "${var.storage.type}" + EOF +} diff --git a/apps/dbgate/svc.tf b/apps/dbgate/svc.tf new file mode 100644 index 0000000..c73fa48 --- /dev/null +++ b/apps/dbgate/svc.tf @@ -0,0 +1,18 @@ +resource "kubectl_manifest" "service" { + yaml_body = <<-EOF + apiVersion: v1 + kind: Service + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + type: ClusterIP + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http + selector: ${jsonencode(local.common-labels)} + EOF +}