implr/vpn: create

Change-Id: I2843ccb60d257ec10f305e8842a7ebb08c2b5e06
diff --git a/personal/implr/vpn/vpn.jsonnet b/personal/implr/vpn/vpn.jsonnet
new file mode 100644
index 0000000..3f39231
--- /dev/null
+++ b/personal/implr/vpn/vpn.jsonnet
@@ -0,0 +1,37 @@
+local kube = import "../../../kube/kube.libsonnet";
+local vpn = import "vpn.libsonnet";
+
+{
+    local top = self,
+    tls: vpn.PKI("implr-vpn"),
+
+    servers: {
+        praisethesun: vpn.Server("openvpn-implr-praisethesun", 11223, top.tls) {
+            cfg+: {
+                namespace: "implr-vpn",
+                configFile: |||
+                    dev tun
+                    tmp-dir /dev/shm/
+                    proto udp
+                    port 11223
+                    topology subnet
+                    server 172.17.1.0 255.255.255.0
+                    keepalive 10 60
+                    persist-tun
+                    persist-key
+                    compress lz4
+                    cipher AES-256-CBC
+                    dh none
+                    ca /mnt/pki/ca.crt
+                    cert /mnt/pki/tls.crt
+                    key /mnt/pki/tls.key
+                |||
+            }
+        },
+    },
+    clients: {
+        kektop: vpn.Client("kektop", top.servers.praisethesun),
+        admin1: vpn.Client("admin1", top.servers.praisethesun),
+        desk1: vpn.Client("desk1", top.servers.praisethesun),
+    }
+}
diff --git a/personal/implr/vpn/vpn.libsonnet b/personal/implr/vpn/vpn.libsonnet
new file mode 100644
index 0000000..70873b3
--- /dev/null
+++ b/personal/implr/vpn/vpn.libsonnet
@@ -0,0 +1,169 @@
+local kube = import "../../../kube/kube.libsonnet";
+
+{
+    PKI(namespace):: {
+        local env = self,
+        namespace:: namespace,
+        selfSignedIssuer: kube.Issuer("pki-selfsigned") {
+            metadata+: {
+                namespace: env.namespace,
+            },
+            spec: {
+                selfSigned: {},
+            },
+        },
+        selfSignedCert: kube.Certificate("pki-selfsigned") {
+            metadata+: {
+                namespace: env.namespace,
+            },
+            spec: {
+                secretName: "pki-selfsigned-cert",
+                duration: "43800h0m0s", // 5 years,
+                isCA: true,
+                issuerRef: {
+                    name: env.selfSignedIssuer.metadata.name,
+                },
+                commonName: "pki-ca",
+            },
+        },
+        issuer: kube.Issuer("pki-ca") {
+            metadata+: {
+                namespace: env.namespace,
+            },
+            spec: {
+                ca: {
+                    secretName: env.selfSignedCert.spec.secretName,
+                },
+            },
+        },
+    },
+
+    Client(name, server):: {
+        local client = self,
+        metadata:: {
+            namespace: server.cfg.namespace,
+        },
+        cert: kube.Certificate(name + "-cert") {
+                metadata+: client.metadata,
+        
+                spec: {
+                    secretName: name + "-cert",
+                    duration: "35040h0m0s", // 4 years
+                    issuerRef: {
+                        name: server.pki.issuer.metadata.name,
+                        kind: "Issuer",
+                    },
+                    commonName: "client-%s.%s" % [name, server.cfg.namespace],
+                },
+        },
+        
+    },
+
+    Server(name, port, pki):: {
+        local server = self,
+        local cfg = server.cfg,
+
+        pki: pki,
+
+        cfg:: {
+            namespace: error "namespace must be set",
+            storageClassName: "waw-hdd-redundant-3",
+
+            image: "nixery.dev/shell/openvpn",
+            configFile: error "configFile must be set",
+
+        },
+        namespace: kube.Namespace(cfg.namespace),
+
+        metadata:: {
+            namespace: cfg.namespace,
+        },
+
+        config: kube.ConfigMap(name + "-config") {
+            metadata+: server.metadata,
+            data: {
+                "openvpn.conf": cfg.configFile,
+            }
+        },
+
+        cert: kube.Certificate(name + "-cert") {
+                metadata+: server.metadata,
+        
+                spec: {
+                    secretName: name + "-cert",
+                    duration: "35040h0m0s", // 4 years
+                    issuerRef: {
+                        name: pki.issuer.metadata.name,
+                        kind: "Issuer",
+                    },
+                    commonName: "server.%s.%s" % [name, cfg.namespace],
+                    //dnsNames: [
+                        //"%s" % [component.svc.metadata.name ],
+                        //"%s.%s" % [component.svc.metadata.name, component.svc.metadata.namespace ],
+                        //"%s.%s.svc" % [component.svc.metadata.name, component.svc.metadata.namespace ],
+                        //"%s.%s.svc.cluster.local" % [component.svc.metadata.name, component.svc.metadata.namespace ],
+                        //"%s.%s.svc.%s" % [component.svc.metadata.name, component.svc.metadata.namespace, env.pkiClusterFQDN ],
+                    //],
+                },
+            },
+
+
+        deployment: kube.Deployment(name) {
+            metadata+: server.metadata,
+            spec+: {
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.ConfigMapVolume(server.config),
+                            pki: {
+                                secret: { secretName: server.cert.spec.secretName },
+                            },
+                        },
+
+                        containers_: {
+                            server: kube.Container("server") {
+                                image: cfg.image,
+                                env_: {
+                                },
+                                command: [
+                                    "/bin/openvpn", "--config", "/config/openvpn.conf"
+                                ],
+                                ports_: {
+                                    client: { containerPort: port },
+                                },
+                                volumeMounts_: {
+                                    config: { mountPath: "/config" },
+                                    pki: { mountPath: "/mnt/pki" },
+                                },
+                                resources: {
+                                    requests: {
+                                        cpu: "250m",
+                                        memory: "100Mi",
+                                    },
+                                    limits: {
+                                        cpu: "500m",
+                                        memory: "512Mi",
+                                    },
+                                },
+                                securityContext: {
+                                    privileged: true,
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+        svc: kube.Service(name) {
+            metadata+: server.metadata,
+            target_pod:: server.deployment.spec.template,
+            spec+: {
+                ports: [
+                    { name: "client", port: port, targetPort: port, protocol: "UDP" },
+                ],
+                type: "LoadBalancer",
+                externalTrafficPolicy: "Local",
+            },
+        },
+    },
+}