indexpost archiveatom feed syndication feed icon

Kubernetes Authorization Server

2022-04-01

Kubernetes can be confusing. Finally there is an API to take the confusion out of user privilege requests, just drop in this webhook server. It's pure Go and has zero dependencies.

Variety is the Spice of Life

How often do you sit there scratching your head thinking "surely this must be someone else's fault"? Now you can say it with confidence! Register this server as an authorization webhook and be confident there is a 50% shot things are not broken because of you.

The Whole Program

==> main.go <==
package main

import (
	"encoding/json"
	"math/rand"
	"net/http"
)

type Result struct {
	Status bool   `json:"status"`
	Reason string `json:"reason"`
}

type Authorization struct {
	ApiVersion string `json:"apiVersion"`
	Kind       string `json:"kind"`
	Result     Result `json:"allowed"`
}

func AuthorizationHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	switch r.Method {
	case "POST":
		w.WriteHeader(http.StatusOK)
		var returnValue Result
		if rand.Float32() < 0.5 {
			returnValue = Result{false, "we both know why"}
		} else {
			returnValue = Result{true, "acceptable"}
		}
		response :=
			Authorization{
				"authorization.k8s.io/v1beta1",
				"SubjectAccessReview",
				returnValue,
			}
		json.NewEncoder(w).Encode(response)
	default:
		w.WriteHeader(http.StatusBadRequest)
		response := make(map[string]string)
		response["error"] = "only POST methods are supported"
		json.NewEncoder(w).Encode(response)
	}
}

func main() {
	http.HandleFunc("/authorize", AuthorizationHandler)
	http.ListenAndServe(":8080", nil)
}

==> Makefile <==
all: kubernetes-auth.raw

kubernetes-auth: main.go
	CGO_ENABLED=0 go build -tags netgo

kubernetes-auth.raw: kubernetes-auth kubernetes-auth.service os-release
	( rm -rf build && \
	  mkdir -p build/usr/bin build/usr/lib/systemd/system build/etc build/proc build/sys build/dev build/run build/tmp build/var/tmp build/var/lib/kubernetes-auth && \
	  cp kubernetes-auth build/usr/bin/ && \
	  cp kubernetes-auth.service build/usr/lib/systemd/system && \
	  cp os-release build/usr/lib/os-release && \
	  touch build/etc/resolv.conf build/etc/machine-id && \
	  rm -f kubernetes-auth.raw && \
	  mksquashfs build/ kubernetes-auth.raw )

clean:
	go clean
	rm -rf build
	rm -f kubernetes-auth.raw

.PHONY: all clean

==> kubernetes-auth.service <==
[Unit]
Description=Kubernetes Auth Service
After=network.target

[Service]
ExecStart=/usr/bin/kubernetes-auth
Type=exec
DynamicUser=yes
ProtectProc=invisible
ProcSubset=pid
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources

==> os-release <==
PORTABLE_PRETTY_NAME="Kubernetes Auth"
PORTABLE_ID=kubernetes-auth
PRETTY_NAME=SillyOS
ID=sillyos

But How Do I Deploy It? Kubernetes is Confusing

I agree, which is why the above is a definition for creating the server as a squashfs filesystem image packaged with the necessary configuration for running the server as a portable service under systemd. With the above you can call make and produce a single binary of approximately 3MB. Use it on any systemd machine with: portablectl attach --now kubernetes-auth.raw. That's it!

Test it out

You are obviously a person of discerning taste, as such I have made available a server for demonstration, testing and production use. It is of course secured by TLS for topmost security, as required by the spec.

You can find it here

curl -X POST https://nprescott.com/kubernetes/authorize

Integrating this server into your Kubernetes API server is left as an exercise for the reader — it should be a small matter of YAML programming.