| import ( |
| "encoding/base64" |
| ) |
| |
| input: { |
| privateNetwork: { |
| hostname: string |
| username: string |
| ipSubnet: string // TODO(gio): use cidr type |
| } |
| sshPrivateKey: string |
| controllerReplicaCount: int | *3 |
| } |
| |
| 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.82.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" |
| } |
| } |
| "ingress-nginx": { |
| chart: charts["ingress-nginx"] |
| values: { |
| fullnameOverride: "\(global.id)-nginx-private" |
| controller: { |
| replicaCount: input.controllerReplicaCount |
| updateStrategy: { |
| type: "RollingUpdate" |
| rollingUpdate: { |
| maxUnavailable: "30%" |
| } |
| } |
| service: { |
| enabled: true |
| type: "LoadBalancer" |
| annotations: { |
| "metallb.universe.tf/address-pool": _ingressPrivate |
| } |
| extraPorts: { |
| tcp: {} |
| udp: {} |
| } |
| } |
| 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 |
| } |
| }, { |
| name: "proxy-backend-pid" |
| emptyDir: { |
| size: "2Mi" |
| } |
| }, { |
| name: "ts-proxy-state" |
| emptyDir: { |
| size: "2Mi" |
| } |
| }] |
| shareProcessNamespace: true |
| extraContainers: [{ |
| name: "proxy" |
| image: images.tailscale.fullNameWithTag |
| securityContext: { |
| capabilities: { |
| add: ["NET_ADMIN"] |
| } |
| privileged: true |
| } |
| env: [{ |
| name: "TS_STATE_DIR" |
| value: "/ts-state" |
| }, { |
| name: "TS_HOSTNAME" |
| valueFrom: { |
| fieldRef: { |
| fieldPath: "metadata.name" |
| } |
| } |
| }, { |
| name: "TS_EXTRA_ARGS" |
| value: "--login-server=https://headscale.\(global.domain)" |
| }, { |
| name: "TS_USERSPACE" |
| value: "false" |
| }] |
| command: ["/bin/sh"] |
| args: [ |
| "-c", |
| "TS_AUTHKEY=$(wget --post-data=\"\" -O /tmp/authkey http://headscale-api.\(global.namespacePrefix)app-headscale.svc.cluster.local/user/private-network-proxy/preauthkey > /dev/null 2>&1 && cat /tmp/authkey) /usr/local/bin/containerboot", |
| ] |
| volumeMounts: [{ |
| mountPath: "/ts-state" |
| name: "ts-proxy-state" |
| readOnly: 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 |
| }, { |
| name: "proxy-backend-pid" |
| mountPath: "/var/run/nginx" |
| readOnly: false |
| }] |
| }, { |
| name: "reload-config" |
| image: "giolekva/reload:latest" |
| imagePullPolicy: "Always" |
| command: [ |
| "/usr/bin/reload", |
| "--watch=/etc/nginx/nginx.conf", |
| "--reload=/var/run/nginx/nginx.pid", |
| ] |
| volumeMounts: [{ |
| name: "proxy-backend-config" |
| mountPath: "/etc/nginx" |
| readOnly: true |
| }, { |
| name: "proxy-backend-pid" |
| mountPath: "/var/run/nginx" |
| readOnly: true |
| }] |
| securityContext: { |
| capabilities: { |
| add: ["SYS_PTRACE"] |
| } |
| } |
| }] |
| 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; |
| pid /var/run/nginx/nginx.pid; |
| events { |
| worker_connections 1024; |
| } |
| http { |
| map $http_host $backend { |
| } |
| server { |
| listen 9090; |
| location / { |
| resolver 135.181.48.180; |
| proxy_pass http://$backend; |
| } |
| } |
| } |
| """ |
| } |
| } |
| } |
| |
| _proxyBackendConfigName: "proxy-backend-config" |