Skip to content

Commit 0e10f95

Browse files
authored
Merge pull request #687 from cozystack/kube-ovn-webhook
Move source-ip validation from cilium to kube-ovn side
2 parents d266bde + 478a3f5 commit 0e10f95

19 files changed

Lines changed: 988 additions & 0 deletions

File tree

‎Makefile‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ build:
1111
make -C packages/system/cozystack-controller image
1212
make -C packages/system/cilium image
1313
make -C packages/system/kubeovn image
14+
make -C packages/system/kubeovn-webhook image
1415
make -C packages/system/dashboard image
1516
make -C packages/system/kamaji image
1617
make -C packages/system/bucket image

‎packages/core/platform/bundles/paas-full.yaml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ releases:
5050
SVC_CIDR: "{{ index $cozyConfig.data "ipv4-svc-cidr" }}"
5151
JOIN_CIDR: "{{ index $cozyConfig.data "ipv4-join-cidr" }}"
5252

53+
- name: kubeovn-webhook
54+
releaseName: kubeovn-webhook
55+
chart: cozy-kubeovn-webhook
56+
namespace: cozy-kubeovn
57+
privileged: true
58+
dependsOn: [cilium,kubeovn,cert-manager]
59+
5360
- name: cozy-proxy
5461
releaseName: cozystack
5562
chart: cozy-cozy-proxy

‎packages/system/cilium/values-kubeovn.yaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
cilium:
2+
daemon:
3+
enableSourceIPVerification: false
24
sctp:
35
enabled: true
46
autoDirectNodeRoutes: false
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v2
2+
name: cozy-kubeovn-webhook
3+
description: Helm chart for a mutating admission webhook that copies Namespace annotations into Pods
4+
type: application
5+
version: 0.1.0
6+
appVersion: "1.0.0"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export NAME=kubeovn-webhook
2+
export NAMESPACE=cozy-kubeovn
3+
4+
include ../../../scripts/common-envs.mk
5+
include ../../../scripts/package.mk
6+
7+
image:
8+
docker buildx build images/kubeovn-webhook \
9+
--provenance false \
10+
--tag $(REGISTRY)/kubeovn-webhook:$(call settag,$(TAG)) \
11+
--cache-from type=registry,ref=$(REGISTRY)/kubeovn-webhook:latest \
12+
--cache-to type=inline \
13+
--metadata-file images/kubeovn-webhook.json \
14+
--push=$(PUSH) \
15+
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack"
16+
--load=$(LOAD)
17+
IMAGE="$(REGISTRY)/kubeovn-webhook:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/kubeovn-webhook.json -o json -r)" \
18+
yq -i '.image = strenv(IMAGE)' values.yaml
19+
rm -f images/kubeovn-webhook.json
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM golang:1.23 as builder
2+
3+
WORKDIR /app
4+
COPY go.mod go.sum ./
5+
RUN go mod download
6+
7+
COPY . .
8+
RUN CGO_ENABLED=0 GOOS=linux go build -o webhook .
9+
10+
FROM alpine:3.21.3
11+
WORKDIR /app
12+
13+
COPY --from=builder /app/webhook /app/webhook
14+
15+
EXPOSE 8443
16+
USER 65534
17+
ENTRYPOINT ["/app/webhook", "--tls-cert-file=/etc/webhook/certs/tls.crt", "--tls-key-file=/etc/webhook/certs/tls.key"]
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"log"
9+
"net/http"
10+
11+
admissionv1 "k8s.io/api/admission/v1"
12+
corev1 "k8s.io/api/core/v1"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
15+
"github.com/mattbaird/jsonpatch"
16+
"k8s.io/client-go/kubernetes"
17+
"k8s.io/client-go/rest"
18+
)
19+
20+
const (
21+
AnnotationRoutes = "ovn.kubernetes.io/routes"
22+
AnnotationPortSecurity = "ovn.kubernetes.io/port_security"
23+
)
24+
25+
func HandleMutatePods(w http.ResponseWriter, r *http.Request) {
26+
body, err := io.ReadAll(r.Body)
27+
if err != nil {
28+
http.Error(w, "could not read request body", http.StatusBadRequest)
29+
return
30+
}
31+
defer r.Body.Close()
32+
33+
var admissionReviewReq admissionv1.AdmissionReview
34+
if err := json.Unmarshal(body, &admissionReviewReq); err != nil {
35+
http.Error(w, "could not unmarshal request", http.StatusBadRequest)
36+
return
37+
}
38+
39+
admissionReviewResp := admissionv1.AdmissionReview{
40+
TypeMeta: admissionReviewReq.TypeMeta,
41+
}
42+
admissionResponse := &admissionv1.AdmissionResponse{
43+
UID: admissionReviewReq.Request.UID,
44+
}
45+
admissionReviewResp.Response = admissionResponse
46+
47+
if admissionReviewReq.Request.Operation != admissionv1.Create ||
48+
admissionReviewReq.Request.Kind.Kind != "Pod" {
49+
admissionResponse.Allowed = true
50+
writeResponse(w, admissionReviewResp)
51+
return
52+
}
53+
54+
raw := admissionReviewReq.Request.Object.Raw
55+
var pod corev1.Pod
56+
if err := json.Unmarshal(raw, &pod); err != nil {
57+
admissionResponse.Allowed = true
58+
writeResponse(w, admissionReviewResp)
59+
return
60+
}
61+
62+
ns := admissionReviewReq.Request.Namespace
63+
nsAnnotations, err := getNamespaceAnnotations(ns)
64+
if err != nil {
65+
log.Printf("Failed to get namespace %q: %v", ns, err)
66+
admissionResponse.Allowed = true
67+
writeResponse(w, admissionReviewResp)
68+
return
69+
}
70+
71+
mergedAnnotations := map[string]string{}
72+
if pod.Annotations != nil {
73+
for k, v := range pod.Annotations {
74+
mergedAnnotations[k] = v
75+
}
76+
}
77+
78+
nsRoutes, nsHasRoutes := nsAnnotations[AnnotationRoutes]
79+
if nsHasRoutes {
80+
if _, alreadySet := mergedAnnotations[AnnotationRoutes]; !alreadySet {
81+
mergedAnnotations[AnnotationRoutes] = nsRoutes
82+
}
83+
} else if RoutesGlobal != "" {
84+
if _, alreadySet := mergedAnnotations[AnnotationRoutes]; !alreadySet {
85+
mergedAnnotations[AnnotationRoutes] = RoutesGlobal
86+
}
87+
}
88+
89+
nsPortSec, nsHasPortSec := nsAnnotations[AnnotationPortSecurity]
90+
finalPortSec := ""
91+
if nsHasPortSec {
92+
finalPortSec = nsPortSec
93+
} else if PortSecurityGlobal {
94+
finalPortSec = "true"
95+
}
96+
97+
if finalPortSec != "" {
98+
if _, alreadySet := mergedAnnotations[AnnotationPortSecurity]; !alreadySet {
99+
mergedAnnotations[AnnotationPortSecurity] = finalPortSec
100+
}
101+
}
102+
103+
if len(mergedAnnotations) == len(pod.Annotations) {
104+
admissionResponse.Allowed = true
105+
writeResponse(w, admissionReviewResp)
106+
return
107+
}
108+
109+
op := "replace"
110+
if pod.Annotations == nil {
111+
op = "add"
112+
}
113+
114+
patches := []jsonpatch.JsonPatchOperation{{
115+
Operation: op,
116+
Path: "/metadata/annotations",
117+
Value: mergedAnnotations,
118+
}}
119+
120+
patchBytes, err := json.Marshal(patches)
121+
if err != nil {
122+
log.Printf("Failed to marshal patch: %v", err)
123+
admissionResponse.Allowed = true
124+
writeResponse(w, admissionReviewResp)
125+
return
126+
}
127+
128+
admissionResponse.Allowed = true
129+
admissionResponse.Patch = patchBytes
130+
pt := admissionv1.PatchTypeJSONPatch
131+
admissionResponse.PatchType = &pt
132+
133+
writeResponse(w, admissionReviewResp)
134+
}
135+
136+
func getNamespaceAnnotations(namespace string) (map[string]string, error) {
137+
config, err := rest.InClusterConfig()
138+
if err != nil {
139+
return nil, err
140+
}
141+
clientset, err := kubernetes.NewForConfig(config)
142+
if err != nil {
143+
return nil, err
144+
}
145+
146+
ns, err := clientset.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
147+
if err != nil {
148+
return nil, err
149+
}
150+
return ns.Annotations, nil
151+
}
152+
153+
func writeResponse(w http.ResponseWriter, review admissionv1.AdmissionReview) {
154+
resp, err := json.Marshal(review)
155+
if err != nil {
156+
http.Error(w, fmt.Sprintf("could not marshal response: %v", err), http.StatusInternalServerError)
157+
return
158+
}
159+
w.Header().Set("Content-Type", "application/json")
160+
_, _ = w.Write(resp)
161+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module kube-ovn-webhook
2+
3+
go 1.23
4+
5+
require (
6+
github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38
7+
k8s.io/api v0.26.0
8+
k8s.io/apimachinery v0.26.0
9+
k8s.io/client-go v0.26.0
10+
)
11+
12+
require (
13+
github.com/davecgh/go-spew v1.1.1 // indirect
14+
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
15+
github.com/go-logr/logr v1.2.3 // indirect
16+
github.com/go-openapi/jsonpointer v0.19.5 // indirect
17+
github.com/go-openapi/jsonreference v0.20.0 // indirect
18+
github.com/go-openapi/swag v0.19.14 // indirect
19+
github.com/gogo/protobuf v1.3.2 // indirect
20+
github.com/golang/protobuf v1.5.2 // indirect
21+
github.com/google/gnostic v0.5.7-v3refs // indirect
22+
github.com/google/go-cmp v0.5.9 // indirect
23+
github.com/google/gofuzz v1.1.0 // indirect
24+
github.com/josharian/intern v1.0.0 // indirect
25+
github.com/json-iterator/go v1.1.12 // indirect
26+
github.com/mailru/easyjson v0.7.6 // indirect
27+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
28+
github.com/modern-go/reflect2 v1.0.2 // indirect
29+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
30+
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect
31+
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
32+
golang.org/x/sys v0.3.0 // indirect
33+
golang.org/x/term v0.3.0 // indirect
34+
golang.org/x/text v0.5.0 // indirect
35+
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
36+
google.golang.org/appengine v1.6.7 // indirect
37+
google.golang.org/protobuf v1.28.1 // indirect
38+
gopkg.in/inf.v0 v0.9.1 // indirect
39+
gopkg.in/yaml.v2 v2.4.0 // indirect
40+
gopkg.in/yaml.v3 v3.0.1 // indirect
41+
k8s.io/klog/v2 v2.80.1 // indirect
42+
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
43+
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
44+
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
45+
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
46+
sigs.k8s.io/yaml v1.3.0 // indirect
47+
)

0 commit comments

Comments
 (0)