diff --git a/cluster/kube/k0.libsonnet b/cluster/kube/k0.libsonnet index f393dbeb..b5feb05f 100644 --- a/cluster/kube/k0.libsonnet +++ b/cluster/kube/k0.libsonnet @@ -7,6 +7,7 @@ local policies = import "../../kube/policies.libsonnet"; local cluster = import "cluster.libsonnet"; +local admitomatic = import "lib/admitomatic.libsonnet"; local cockroachdb = import "lib/cockroachdb.libsonnet"; local registry = import "lib/registry.libsonnet"; local rook = import "lib/rook.libsonnet"; @@ -308,5 +309,57 @@ local rook = import "lib/rook.libsonnet"; # TODO(implr): restricted policy with CAP_NET_ADMIN and tuntap, but no full root policies.AllowNamespaceInsecure("implr-vpn"), ], + + # Admission controller that permits non-privileged users to manage + # their namespaces without danger of hijacking important URLs. + admitomatic: admitomatic.Environment { + cfg+: { + proto: { + // Domains allowed in given namespaces. If a domain exists + // anywhere, ingresses will only be permitted to be created + // within namespaces in which it appears here. This works + // the same way for wildcards, if a wildcard exists in this + // list it blocks all unauthorized uses of that domain + // elsewhere. + // + // See //cluster/admitomatic for more information. + // + // Or, tl;dr: + // + // If you do a wildcard CNAME onto the k0 ingress, you + // should explicitly state *.your.name.com here. + // + // If you just want to protect your host from being + // hijacked by other cluster users, you should also state + // it here (either as a wildcard, or unary domains). + allow_domain: [ + { namespace: "covid-formity", dns: "covid19.hackerspace.pl" }, + { namespace: "covid-formity", dns: "covid.hackerspace.pl" }, + { namespace: "covid-formity", dns: "www.covid.hackerspace.pl" }, + { namespace: "devtools-prod", dns: "hackdoc.hackerspace.pl" }, + { namespace: "devtools-prod", dns: "cs.hackerspace.pl" }, + { namespace: "engelsystem-prod", dns: "engelsystem.hackerspace.pl" }, + { namespace: "gerrit", dns: "gerrit.hackerspace.pl" }, + { namespace: "gitea-prod", dns: "gitea.hackerspace.pl" }, + { namespace: "hswaw-prod", dns: "*.hackerspace.pl" }, + { namespace: "internet", dns: "internet.hackerspace.pl" }, + { namespace: "matrix", dns: "matrix.hackerspace.pl" }, + { namespace: "onlyoffice-prod", dns: "office.hackerspace.pl" }, + { namespace: "redmine", dns: "issues.hackerspace.pl" }, + { namespace: "speedtest", dns: "speedtest.hackerspace.pl" }, + { namespace: "sso", dns: "sso.hackerspace.pl" }, + + { namespace: "ceph-waw3", dns: "ceph-waw3.hswaw.net" }, + { namespace: "ceph-waw3", dns: "object.ceph-waw3.hswaw.net" }, + { namespace: "monitoring-global-k0", dns: "*.hswaw.net" }, + { namespace: "registry", dns: "*.hswaw.net" }, + + // q3k's legacy namespace (pre-prodvider) + { namespace: "q3k", dns: "*.q3k.org" }, + { namespace: "personal-q3k", dns: "*.q3k.org" }, + ], + }, + }, + }, }, } diff --git a/cluster/kube/lib/admitomatic.libsonnet b/cluster/kube/lib/admitomatic.libsonnet new file mode 100644 index 00000000..36ea5efa --- /dev/null +++ b/cluster/kube/lib/admitomatic.libsonnet @@ -0,0 +1,94 @@ +// Deploys admitomatic, a validating admission webhook. It is used in +// conjunction with Kubernetes' RBAC to provide a level of multitenancy to the +// cluster, adding extra restrictions to resources created by non-administrative +// users. +// +// For more information about admitomatic , see //cluster/admitomatic . +// +// As with every Kubernetes admission webhook, the Kubernetes control plane +// (ie. apiserver) needs to be able to dial the deployed admitomatic service. +// The authentication story for this is unfortunately quite sad and requires +// the use of a pre-generated one-shot CA and certificate. +// +// .---- self-signed -. +// v | +// Admitomatic CA ----------' <-- caBundle used by apiserver, +// | set in ValidatingWebhookConfiguration +// v +// Admitomatic Cert <-- admitomatic_tls_cert used by admitomatic +// +// This CA needs to be provisioned ahead of time by ourselves. In order to keep +// things simple (as admitomatic being an admission webhook becomes a core +// component of the k8s control plane), we generate this CA as plain text +// secrets, and store them with secretstore in git. This is done via clustercfg. + +local kube = import "../../../kube/kube.libsonnet"; +local prototext = import "../../../kube/prototext.libsonnet"; + +{ + Environment: { + local env = self, + local cfg = env.cfg, + + cfg:: { + namespace: "admitomatic", + image: "registry.k0.hswaw.net/q3k/admitomatic:1612618063-0b68e233116f733fb3ec9016c9d3b7decb86f192", + + proto: {}, + }, + + namespace: kube.Namespace(cfg.namespace), + local ns = self.namespace, + + config: ns.Contain(kube.ConfigMap("admitomatic")) { + data: { + "config.pb.text": prototext.manifestProtoText(cfg.proto), + }, + }, + + secret: ns.Contain(kube.Secret("admitomatic")) { + data_: { + "webhook.key": importstr "../../secrets/plain/admitomatic-webhook.key", + "webhook.crt": importstr "../../certs/admitomatic-webhook.cert", + }, + }, + + daemonset: ns.Contain(kube.DaemonSet("admitomatic")) { + spec+: { + template+: { + spec+: { + containers_: { + default: kube.Container("default") { + image: cfg.image, + args: [ + "/cluster/admitomatic/admitomatic", + "-admitomatic_config", "/admitomatic/config/config.pb.text", + "-admitomatic_listen", "0.0.0.0:8443", + "-admitomatic_tls_cert", "/admitomatic/secret/webhook.crt", + "-admitomatic_tls_key", "/admitomatic/secret/webhook.key", + // doesn't serve anything over gRPC. + "-hspki_disable" + ], + volumeMounts_: { + config: { mountPath: "/admitomatic/config" }, + secret: { mountPath: "/admitomatic/secret" }, + }, + ports_: { + public: { containerPort: 8443 }, + }, + }, + }, + volumes_: { + config: kube.ConfigMapVolume(env.config), + secret: kube.SecretVolume(env.secret), + }, + }, + }, + }, + }, + + svc: ns.Contain(kube.Service("admitomatic")) { + target_pod:: env.daemonset.spec.template, + }, + }, +}