DodoApp: Implement dev proxy mode

With dev proxy user can substitute any service with their own local
machine. In which case dodo will run proxy server on the platform
which will forward all requests to the configured address.

When VPN is enabled, dodo will run tailscale sidecar in the proxy pod.

Change-Id: I12592ae77d2e88e0582c8fe1e0f82e5fd24e02cb
diff --git a/charts/proxy/.helmignore b/charts/proxy/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/charts/proxy/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/proxy/Chart.yaml b/charts/proxy/Chart.yaml
new file mode 100644
index 0000000..05aea82
--- /dev/null
+++ b/charts/proxy/Chart.yaml
@@ -0,0 +1,6 @@
+apiVersion: v2
+name: proxy
+description: A Helm chart for proxy
+type: application
+version: 0.0.1
+appVersion: "0.0.1"
diff --git a/charts/proxy/templates/install.yaml b/charts/proxy/templates/install.yaml
new file mode 100644
index 0000000..849cd75
--- /dev/null
+++ b/charts/proxy/templates/install.yaml
@@ -0,0 +1,127 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ .Values.name }}
+  namespace: {{ .Release.Namespace }}
+data:
+  nginx.conf: |
+    {{ .Values.config | nindent 4 }}
+---
+{{- if .Values.vpn.enabled }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ .Values.name }}-vpn-pre-auth-key
+  namespace: {{ .Release.Namespace }}
+stringData:
+  authkey: {{ .Values.vpn.preAuthKey }}
+{{- end }}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Values.name }}
+  namespace: {{ .Release.Namespace }}
+spec:
+  selector:
+    matchLabels:
+      app: proxy
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: proxy
+    spec:
+      serviceAccountName: {{ .Values.name }}-proxy
+      volumes:
+      - name: config
+        configMap:
+          name: {{ .Values.name }}
+      containers:
+      - name: proxy
+        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
+        imagePullPolicy: {{ .Values.image.pullPolicy }}
+        ports:
+          {{- range .Values.ports }}
+          - name: {{ .name }}
+            containerPort: {{ .value }}
+            protocol: {{ .protocol }}
+          {{- end }}
+        volumeMounts:
+        - name: config
+          mountPath: /etc/nginx
+      {{- if .Values.vpn.enabled }}
+      - name: tailscale
+        image: {{ .Values.vpn.image.repository }}:{{ .Values.vpn.image.tag }}
+        imagePullPolicy: {{ .Values.vpn.image.pullPolicy }}
+        securityContext:
+          privileged: true
+          capabilities:
+            add:
+            - NET_ADMIN
+        env:
+        - name: TS_KUBE_SECRET
+          value: {{ .Values.name }}-vpn-pre-auth-key
+        - name: TS_HOSTNAME
+          value: {{ .Values.vpn.hostname }}
+        - name: TS_USERSPACE
+          value: "false"
+        - name: TS_ACCEPT_DNS
+          value: "true"
+        - name: TS_EXTRA_ARGS
+          value: --login-server={{ .Values.vpn.loginServer }}
+      {{- end }}
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ .Values.name }}
+  namespace: {{ .Release.Namespace }}
+spec:
+  type: ClusterIP
+  selector:
+    app: proxy
+  ports:
+    {{- range .Values.ports }}
+    - name: {{ .name }}
+      port: {{ .value }}
+      targetPort: {{ .name }}
+      protocol: {{ .protocol }}
+    {{- end }}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ .Values.name }}-proxy
+  namespace: {{ .Release.Namespace }}
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: {{ .Values.name }}-proxy
+  namespace: {{ .Release.Namespace }}
+rules:
+- apiGroups: [""] # "" indicates the core API group
+  resources: ["secrets"]
+  # Create can not be restricted to a resource name.
+  verbs: ["create"]
+- apiGroups: [""] # "" indicates the core API group
+  resourceNames: ["{{ .Values.name }}-vpn-pre-auth-key"]
+  resources: ["secrets"]
+  verbs: ["get", "update", "patch"]
+- apiGroups: [""] # "" indicates the core API group
+  resources: ["events"]
+  verbs: ["get", "create", "patch"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: {{ .Values.name }}-proxy
+  namespace: {{ .Release.Namespace }}
+subjects:
+- kind: ServiceAccount
+  name: {{ .Values.name }}-proxy
+roleRef:
+  kind: Role
+  name: {{ .Values.name }}-proxy
+  apiGroup: rbac.authorization.k8s.io
diff --git a/charts/proxy/values.yaml b/charts/proxy/values.yaml
new file mode 100644
index 0000000..a701c3e
--- /dev/null
+++ b/charts/proxy/values.yaml
@@ -0,0 +1,22 @@
+name: proxy
+image:
+  repository: library/nginx
+  tag: 1.27.1-alpine3.20-slim
+  pullPolicy: IfNotPresent
+config: "nginx configuration"
+ports:
+- name: web
+  value: 8080
+  protocol: TCP
+- name: API
+  value: 9090
+  protocol: UDP
+vpn:
+  enabled: false
+  image:
+    repository: tailscale/tailscale
+    tag: v1.42.0
+    pullPolicy: IfNotPresent
+  preAuthKey: ""
+  loginServer: ""
+  hostname: ""