Implementing a Webhook for Authorization
Learn how to implement a webhook authorization service.
We'll cover the following...
Implement a webhook authorization service
A webhook authorization service is a web server, because the kube-apiserver invokes it through HTTPS POST requests.
Now, let’s implement such a service step by step.
Step 1: Write a simple HTTP server
Let’s write a simple HTTP server that responds with the mock authenticated user mock when requested for the /authorize resource over port 443.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
authorizationapi "k8s.io/api/authorization/v1beta1"
)
func authZ(sar *authorizationapi.SubjectAccessReview) {
// now we do some mock for demo
// Please replace this with your logic
if sar.Spec.User == "demo-user" {
sar.Status.Allowed = true
} else {
sar.Status.Reason = fmt.Sprintf("User %q is not allowed to access %q",
sar.Spec.User, sar.Spec.ResourceAttributes.Resource)
}
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Receiving %s", r.Method)
if r.Method != "POST" {
http.Error(w, "Only Accept POST requests", http.StatusMethodNotAllowed)
return
}
// Read body of POST request
payload, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Printf("Receiving SubjectAccessReview: %s\n", string(payload))
// Unmarshal JSON from POST request to SubjectAccessReview object
sar := &authorizationapi.SubjectAccessReview{}
err = json.Unmarshal(payload, sar)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
authZ(sar)
// Marshal the SubjectAccessReview to JSON and send it back
result, err := json.Marshal(*sar)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(result)
w.Header().Set("Content-Type", "application/json")
}
func main() {
// Set up a /authorize resource handler
http.HandleFunc("/authorize", helloHandler)
// Listen to port 443 and wait
log.Println("Listening on port 443 for requests...")
log.Fatal(http.ListenAndServe(":443", nil))
}In a real-world scenario, we only need to replace the mock codes in the function authZ() with our actual implementations to determine user privileges. This function should set Status.Allowed to true if the request performed by the user is allowed and false for invalid requests. For the denied reason, it can be set to the field Status.Reason.
The rest of the code is a simple HTTP handler that handles requests ...