Skip to content

Instantly share code, notes, and snippets.

@theodesp
Last active May 13, 2023 15:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save theodesp/74c2b86c119c5ddaf1e1750429193cef to your computer and use it in GitHub Desktop.
Save theodesp/74c2b86c119c5ddaf1e1750429193cef to your computer and use it in GitHub Desktop.
Custom Admission Controller K8s
apiVersion: apps/v1
kind: Deployment
metadata:
name: webhook-server
namespace: webhook-demo
labels:
app: webhook-server
spec:
replicas: 1
selector:
matchLabels:
app: webhook-server
template:
metadata:
labels:
app: webhook-server
spec:
containers:
- name: server
image: localhost:5001/webhook:latest
imagePullPolicy: Always
ports:
- containerPort: 8443
name: webhook-api
volumeMounts:
- name: webhook-tls-certs
mountPath: /run/secrets/tls
readOnly: true
volumes:
- name: webhook-tls-certs
secret:
secretName: webhook-server-tls
---
apiVersion: v1
kind: Service
metadata:
name: webhook-server
namespace: webhook-demo
spec:
selector:
app: webhook-server
ports:
- port: 443
targetPort: webhook-api
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: demo-webhook
webhooks:
- name: webhook-server.webhook-demo.svc
clientConfig:
service:
name: webhook-server
namespace: webhook-demo
path: "/validate"
caBundle: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMyakNDQWNJQ0NRQ1RxM3EvWGJTRUV6QU5CZ2txaGtpRzl3MEJBUXNGQURBdk1TMHdLd1lEVlFRRERDUkIKWkcxcGMzTnBiMjRnUTI5dWRISnZiR3hsY2lCWFpXSm9iMjlySUVSbGJXOGdRMEV3SGhjTk1qTXdOVEV6TVRRMApOalE1V2hjTk1qTXdOakV5TVRRME5qUTVXakF2TVMwd0t3WURWUVFERENSQlpHMXBjM05wYjI0Z1EyOXVkSEp2CmJHeGxjaUJYWldKb2IyOXJJRVJsYlc4Z1EwRXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUsKQW9JQkFRQ3dneVZhWTdFdzZoK09TTk5KV1hjdmFLVWt5RGtuVEx6RzFOWjcwK256N3N6U0YyWUMxOFFPMm1GegovalJOZnRPbU4zQTFaWFhCcFFBcGVScWdsWTl4dHYwV1BKdzlvcWlDY1AvdGVybDdmdzZNNStzTnBEN0h3Y3hxClkzOFFQUWF0dlQrcnhWb2VPRnlSM0NQYmRDMmxpSE9SbE9zU0g3UThybldadXNSaGV3ZFVkQ1R1Q3VSaEdpNmMKTUJRSU84dHZKNmlqTFU3b3VXSmxwckU4T0xqV3lGeFVWeFBnMzlUWFZyK2duZ2tDQ2xWakwvSHNHOGZHUE9KMApSdWI5WXRPK0FKNDhIblFHaHUrRURZNTcwdHIwK3hUY09JZXcrQTVOTktzY0VQNjI5eXc1VWcwMEp4YmNKNkszCndrSWp3UjQ4NFpTc1ZWd0VIemZXdlNQYUJJMUxBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFCR3gKODBvc2d3SXdQT1pELzEzY3BlQWtFaGNKKzRlbHlnb09IbWxkbFNJRkVtcFdjQ01FWkxkTG9rSnNDNklqWU05NApwRVgwRTVxelR3aDBOZEVwelR2eHB3RjRPMHQyZDFzSG5hU0QvT1czVjNsdDJTdXFWREp0YlNSeXdpTks1YS9RCk5ac2RKbkM2N3FEV01rY2xEZ0NpWjZBMU4xRUxWVm5OU1dzN0psSkVGM1FaRDFmSldoNy9ycmszdmhVTm5yT1oKTnhyeDI3amVuNGdWK1VZNnJOT2FkOEkzaG9HcEMrYnVMMTBTUEJNRGxXbHBKZStlM0I1WWdqbVFyQmNFeEtQTgpzMjVsVHBvNVpZVjhKeHJJTFJyaDJOdGdBVWdhYkxxR1BIQk9nZ0prMEJMWC8vYnZMQWx5RFRFZWppcWczVTJFClJxUGR5N2IwZWpGYjlGT1dmb1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
rules:
- operations: [ "CREATE", "UPDATE" ]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
# build stage
FROM golang:1.20 AS build-env
RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN useradd -u 10001 webhook
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o webhook
FROM scratch
COPY --from=build-env /app .
COPY --from=build-env /etc/passwd /etc/passwd
USER webhook
ENTRYPOINT ["/webhook"]
package main
import (
"context"
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/golang/glog"
"k8s.io/api/admission/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var (
tlscert, tlskey string
)
func main() {
flag.StringVar(&tlscert, "tlsCertFile", "/etc/certs/ca.crt", "File containing the x509 Certificate for HTTPS.")
flag.StringVar(&tlskey, "tlsKeyFile", "/etc/certs/ca.key", "File containing the x509 private key to --tlsCertFile.")
flag.Parse()
certs, err := tls.LoadX509KeyPair(tlscert, tlskey)
if err != nil {
glog.Errorf("Filed to load key pair: %v", err)
}
server := &http.Server{
Addr: ":8443",
TLSConfig: &tls.Config{Certificates: []tls.Certificate{certs}},
}
web := WebhookServerHandler{}
mux := http.NewServeMux()
mux.HandleFunc("/validate", web.serve)
server.Handler = mux
go func() {
if err := server.ListenAndServeTLS("", ""); err != nil {
glog.Errorf("Failed to listen and serve webhook server: %v", err)
}
}()
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
<-signalChan
server.Shutdown(context.Background())
}
type WebhookServerHandler struct {
}
func (web *WebhookServerHandler) serve(w http.ResponseWriter, r *http.Request) {
var body []byte
if r.Body != nil {
if data, err := ioutil.ReadAll(r.Body); err == nil {
body = data
}
}
if len(body) == 0 {
http.Error(w, "empty body", http.StatusBadRequest)
return
}
if r.URL.Path != "/validate" {
http.Error(w, "no validate", http.StatusBadRequest)
return
}
req := v1beta1.AdmissionReview{}
if err := json.Unmarshal(body, &req := v1beta1.AdmissionReview{}); err != nil {
http.Error(w, "incorrect body", http.StatusBadRequest)
}
raw := req.Request.Object.Raw
pod := v1.Pod{}
if err := json.Unmarshal(raw, &pod); err != nil {
return
}
// perform validation check here
if _, ok := pod.Labels["foo"]; ok {
return
}
review := v1beta1.AdmissionReview{
Response: &v1beta1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Message: "Missing Pod Label!",
},
},
}
resp, err := json.Marshal(review)
if err != nil {
http.Error(w, fmt.Sprintf("could not encode response: %v", err), http.StatusInternalServerError)
}
if _, err := w.Write(resp); err != nil {
http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment