| import ( |
| "encoding/base64" |
| ) |
| |
| input: { |
| privateNetwork: { |
| hostname: string |
| username: string |
| ipSubnet: string // TODO(gio): use cidr type |
| } |
| sshPrivateKey: string |
| } |
| |
| name: "private-network" |
| namespace: "ingress-private" |
| |
| out: { |
| images: { |
| "ingress-nginx": { |
| registry: "registry.k8s.io" |
| repository: "ingress-nginx" |
| name: "controller" |
| tag: "v1.8.0" |
| pullPolicy: "IfNotPresent" |
| } |
| nginx: { |
| repository: "library" |
| name: "nginx" |
| tag: "1.27.1-alpine3.20-slim" |
| pullPolicy: "IfNotPresent" |
| } |
| tailscale: { |
| repository: "tailscale" |
| name: "tailscale" |
| tag: "v1.42.0" |
| pullPolicy: "IfNotPresent" |
| } |
| portAllocator: { |
| repository: "giolekva" |
| name: "port-allocator" |
| tag: "latest" |
| pullPolicy: "Always" |
| } |
| } |
| |
| charts: { |
| "access-secrets": { |
| kind: "GitRepository" |
| address: "https://code.v1.dodo.cloud/helm-charts" |
| branch: "main" |
| path: "charts/access-secrets" |
| } |
| service: { |
| kind: "GitRepository" |
| address: "https://code.v1.dodo.cloud/helm-charts" |
| branch: "main" |
| path: "charts/service" |
| } |
| "ingress-nginx": { |
| kind: "GitRepository" |
| address: "https://code.v1.dodo.cloud/helm-charts" |
| branch: "main" |
| path: "charts/ingress-nginx" |
| } |
| "tailscale-proxy": { |
| kind: "GitRepository" |
| address: "https://code.v1.dodo.cloud/helm-charts" |
| branch: "main" |
| path: "charts/tailscale-proxy" |
| } |
| portAllocator: { |
| kind: "GitRepository" |
| address: "https://code.v1.dodo.cloud/helm-charts" |
| branch: "main" |
| path: "charts/port-allocator" |
| } |
| headscaleUser: { |
| kind: "GitRepository" |
| address: "https://code.v1.dodo.cloud/helm-charts" |
| branch: "main" |
| path: "charts/headscale-user" |
| } |
| } |
| |
| _ingressPrivate: "\(global.id)-ingress-private" |
| |
| helm: { |
| "access-secrets": { |
| chart: charts["access-secrets"] |
| values: { |
| serviceAccountName: "default" |
| } |
| } |
| "access-secrets-nginx": { |
| chart: charts["access-secrets"] |
| values: { |
| serviceAccountName: "\(global.id)-nginx-private" |
| } |
| } |
| "headscale-user": { |
| chart: charts.headscaleUser |
| values: { |
| resourceName: "private-network-proxy-backend" |
| username: "private-network-proxy" |
| headscaleApiAddress: "http://headscale-api.\(global.namespacePrefix)app-headscale.svc.cluster.local" |
| preAuthKey: { |
| enabled: true |
| secretName: _clusterProxySecretName |
| } |
| } |
| } |
| "ingress-nginx": { |
| chart: charts["ingress-nginx"] |
| values: { |
| fullnameOverride: "\(global.id)-nginx-private" |
| controller: { |
| service: { |
| enabled: true |
| type: "LoadBalancer" |
| annotations: { |
| "metallb.universe.tf/address-pool": _ingressPrivate |
| } |
| } |
| ingressClassByName: true |
| ingressClassResource: { |
| name: _ingressPrivate |
| enabled: true |
| default: false |
| controllerValue: "k8s.io/\(_ingressPrivate)" |
| } |
| config: { |
| "proxy-body-size": "200M" // TODO(giolekva): configurable |
| "force-ssl-redirect": "true" |
| "server-snippet": """ |
| more_clear_headers "X-Frame-Options"; |
| """ |
| } |
| extraArgs: { |
| "default-ssl-certificate": "\(_ingressPrivate)/cert-wildcard.\(global.privateDomain)" |
| } |
| extraVolumes: [{ |
| name: _proxyBackendConfigName |
| configMap: { |
| name: _proxyBackendConfigName |
| } |
| }] |
| extraContainers: [{ |
| name: "proxy" |
| image: images.tailscale.fullNameWithTag |
| securityContext: { |
| capabilities: { |
| add: ["NET_ADMIN"] |
| } |
| privileged: true |
| } |
| env: [{ |
| name: "TS_KUBE_SECRET" |
| value: _clusterProxySecretName |
| }, { |
| name: "TS_HOSTNAME" |
| value: "cluster-proxy" |
| }, { |
| name: "TS_EXTRA_ARGS" |
| value: "--login-server=https://headscale.\(global.domain)" |
| }, { |
| name: "TS_USERSPACE" |
| value: "false" |
| }] |
| }, { |
| name: "proxy-backend" |
| image: images.nginx.fullNameWithTag |
| imagePullPolicy: images.nginx.pullPolicy |
| ports: [{ |
| name: "proxy" |
| containerPort: 9090 |
| protocol: "TCP" |
| }] |
| volumeMounts: [{ |
| name: _proxyBackendConfigName |
| mountPath: "/etc/nginx" |
| readOnly: true |
| }] |
| }] |
| admissionWebhooks: { |
| enabled: false |
| } |
| image: { |
| registry: images["ingress-nginx"].registry |
| image: images["ingress-nginx"].imageName |
| tag: images["ingress-nginx"].tag |
| pullPolicy: images["ingress-nginx"].pullPolicy |
| } |
| } |
| } |
| } |
| "tailscale-proxy": { |
| chart: charts["tailscale-proxy"] |
| values: { |
| hostname: input.privateNetwork.hostname |
| apiServer: "http://headscale-api.\(global.namespacePrefix)app-headscale.svc.cluster.local" |
| loginServer: "https://headscale.\(networks.public.domain)" // TODO(gio): take headscale subdomain from configuration |
| ipSubnet: input.privateNetwork.ipSubnet |
| username: input.privateNetwork.username // TODO(gio): maybe install headscale-user chart separately? |
| preAuthKeySecret: "headscale-preauth-key" |
| image: { |
| repository: images.tailscale.fullName |
| tag: images.tailscale.tag |
| pullPolicy: images.tailscale.pullPolicy |
| } |
| } |
| } |
| "port-allocator": { |
| chart: charts.portAllocator |
| values: { |
| repoAddr: release.repoAddr |
| sshPrivateKey: base64.Encode(null, input.sshPrivateKey) |
| ingressNginxPath: "\(release.appDir)/resources/ingress-nginx.yaml" |
| image: { |
| repository: images.portAllocator.fullName |
| tag: images.portAllocator.tag |
| pullPolicy: images.portAllocator.pullPolicy |
| } |
| } |
| } |
| // TODO(gio): Generate proxy-backend-config as well |
| "proxy-backend-service": { |
| chart: charts.service |
| values: { |
| name: "proxy-backend-service" |
| type: "ClusterIP" |
| selector: { |
| "app.kubernetes.io/component": "controller" |
| "app.kubernetes.io/instance": "ingress-nginx" |
| "app.kubernetes.io/name": "ingress-nginx" |
| } |
| ports:[{ |
| name: "http" |
| port: 80 |
| targetPort: 9090 |
| protocol: "TCP" |
| }] |
| } |
| } |
| } |
| } |
| resources: { |
| "proxy-backend-config": { |
| apiVersion: "v1" |
| kind: "ConfigMap" |
| metadata: { |
| name: "proxy-backend-config" |
| namespace: release.namespace |
| } |
| data: { |
| "nginx.conf": """ |
| worker_processes 1; |
| worker_rlimit_nofile 8192; |
| events { |
| worker_connections 1024; |
| } |
| http { |
| map $http_host $backend { |
| } |
| server { |
| listen 9090; |
| location / { |
| resolver 135.181.48.180; |
| proxy_pass http://$backend; |
| } |
| } |
| } |
| """ |
| } |
| } |
| } |
| |
| _clusterProxySecretName: "cluster-proxy-preauthkey" |
| _proxyBackendConfigName: "proxy-backend-config" |