Skip to content

Commit

Permalink
Mutating webhook updates (#1368)
Browse files Browse the repository at this point in the history
Mutating webhook updates
  • Loading branch information
devdattakulkarni authored Nov 14, 2024
1 parent c498ba1 commit 771845b
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 40 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ KubePlus takes an application Helm chart and wraps it under a Kubernetes API (CR

### Isolation

KubePlus takes an application Helm chart and wraps it in a Kubernetes API (CRD). This API is used to provision application instances on a cluster. KubePlus isolates each application instance in a separate namespace. It adds a safety perimeter around such namespaces using Kubernetes network policies and non-shared persistent volumes ensuring that each application instance is appropriately isolated from other instances. Additionally, it provides controls for application providers to deploy different tenant application instances on different worker nodes for node isolation.
KubePlus takes an application Helm chart and wraps it in a Kubernetes API (CRD). This API is used to provision application instances on a cluster. KubePlus isolates each application instance in a separate namespace. It adds a safety perimeter around such namespaces using Kubernetes network policies and non-shared persistent volumes ensuring that each application instance is appropriately isolated from other instances.

### Security

The KubePlus Operator does not need any admin-level permissions on a cluster for application providers. This allows application providers to offer their managed services on any K8s clusters including those owned by their customers. KubePlus comes with a small utility that allows you to create provider specific kubeconfig on a cluster in order to enable application deployments and management. Providers have an ability to create a consumer specific further limited kubeconfig to allow for self-service provisioning of application instances as well.
Expand Down Expand Up @@ -203,4 +204,4 @@ Please join us in our meetings. Your participation is welcome.
Subscribe to [KubePlus mailing list](https://groups.google.com/g/kubeplus).

Join #kubeplus channel on [CNCF Slack](https://cloud-native.slack.com/archives/C06U6MP24PN).
If you don't have an account on the CNCF workspace, get your invitation [here](https://communityinviter.com/apps/cloud-native/cncf). You can join the `#kubeplus` channel once your invitation is active.
If you don't have an account on the CNCF workspace, get your invitation [here](https://communityinviter.com/apps/cloud-native/cncf). You can join the `#kubeplus` channel once your invitation is active.
2 changes: 1 addition & 1 deletion deploy/kubeplus-chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 4.0.0
version: 4.0.1

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
Expand Down
2 changes: 1 addition & 1 deletion deploy/kubeplus-chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CHECK_KYVERNO_POLICIES: NO
# Containers
WEBHOOK_INIT_CONTAINER: gcr.io/cloudark-kubeplus/webhook-tls-getter:3.0.28
CRD_REGISTRATION_HELPER: gcr.io/cloudark-kubeplus/kubeconfiggenerator:3.0.29
MUTATING_WEBHOOK: gcr.io/cloudark-kubeplus/pac-mutating-admission-webhook:3.0.17
MUTATING_WEBHOOK: gcr.io/cloudark-kubeplus/pac-mutating-admission-webhook:3.0.18
PLATFORM_OPERATOR: gcr.io/cloudark-kubeplus/platform-operator:3.0.7
CONSUMERUI: gcr.io/cloudark-kubeplus/consumerui:0.0.7
HELMER: gcr.io/cloudark-kubeplus/helm-pod:3.0.22
Expand Down
1 change: 1 addition & 0 deletions mutating-webhook/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/googleapis/gnostic v0.4.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
Expand Down
2 changes: 2 additions & 0 deletions mutating-webhook/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.4.0 h1:BXDUo8p/DaxC+4FJY/SSx3gvnx9C1VdHNgaUkiEL5mk=
github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
Expand Down
37 changes: 34 additions & 3 deletions mutating-webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,40 @@ func main() {
whsvr := &WebhookServer{
server: &http.Server{
Addr: fmt.Sprintf(":%v", parameters.port),
TLSConfig: &tls.Config{Certificates: []tls.Certificate{pair}},
},
// client: kubeClient,
TLSConfig: &tls.Config{MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{pair},
CipherSuites: []uint16{
// TLS 1.0 - 1.2 cipher suites.
tls.TLS_RSA_WITH_RC4_128_SHA,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
// TLS 1.3 cipher suites.
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
},
},
},
}

// define http server and server handler
Expand Down
1 change: 1 addition & 0 deletions mutating-webhook/versions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@
3.0.15
3.0.16
3.0.17
3.0.18
80 changes: 47 additions & 33 deletions mutating-webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ func (whsvr *WebhookServer) mutate(ar *v1.AdmissionReview, httpMethod string) *v
return errResponse
}

/*
// Deprecating support for Pod-level policies.
if req.Kind.Kind == "Pod" {
customAPI, rootKind, rootName, rootNamespace := checkServiceLevelPolicyApplicability(ar)
var podResourcePatches []patchOperation
Expand Down Expand Up @@ -303,6 +305,7 @@ func (whsvr *WebhookServer) mutate(ar *v1.AdmissionReview, httpMethod string) *v
patchOperations = append(patchOperations, podPatch)
}
}
*/

if req.Kind.Kind == "Namespace" {

Expand Down Expand Up @@ -368,7 +371,7 @@ func handleDelete(ar *v1.AdmissionReview) *v1.AdmissionResponse {
//fmt.Printf("Body:%v\n", body)
apiv1 := req.Kind
apiv2 := req.Resource
fmt.Printf("&&&&&\n")
//fmt.Printf("&&&&&\n")
fmt.Printf("APIv1:%s, APIv2:%s\n", apiv1, apiv2)
group := req.Resource.Group
version := req.Resource.Version
Expand Down Expand Up @@ -510,9 +513,9 @@ func checkAndApplyNSPolicies(ar *v1.AdmissionReview) []patchOperation {
// come from arSaved.

podNamespace := req.Namespace
fmt.Printf("Pod Namespace:%s\n", podNamespace)
//fmt.Printf("Pod Namespace:%s\n", podNamespace)
releaseName := namespaceHelmAnnotationMap[podNamespace]
fmt.Printf("Release Name:%s\n", releaseName)
//fmt.Printf("Release Name:%s\n", releaseName)

customAPI := ""
serviceKind := ""
Expand Down Expand Up @@ -610,10 +613,10 @@ func saveResourcePolicy(ar *v1.AdmissionReview) {
*/

var resourcePolicy platformworkflowv1alpha1.ResourcePolicy
err = json.Unmarshal(body, &resourcePolicy)
if err != nil {
json.Unmarshal(body, &resourcePolicy)
/*if err != nil {
fmt.Println(err)
}
}*/

kind := resourcePolicy.Spec.Resource.Kind
lowercaseKind := strings.ToLower(kind)
Expand Down Expand Up @@ -681,12 +684,12 @@ func checkServiceLevelPolicyApplicability(ar *v1.AdmissionReview) (string, strin
//fmt.Printf("All Annotations:%v\n", annotations1)
}
releaseName := annotations1["meta.helm.sh/release-name"]
fmt.Printf("Helm release name:%s\n", releaseName)
//fmt.Printf("Helm release name:%s\n", releaseName)
capiGroup := ""
capiVersion := ""
rootKind, rootName, capiGroup, capiVersion = getAPIDetailsFromHelmReleaseAnnotation(releaseName)
rootAPIVersion = capiGroup + "/" + capiVersion
fmt.Printf("RK:%s, RN:%s, RAPI:%s\n", rootKind, rootName, rootAPIVersion)
//fmt.Printf("RK:%s, RN:%s, RAPI:%s\n", rootKind, rootName, rootAPIVersion)
} else {
rootKind, rootName, rootAPIVersion = findRoot(namespace, ownerKindS, ownerNameS, ownerAPIVersionS)
}
Expand Down Expand Up @@ -754,8 +757,8 @@ func findRoot(namespace, kind, name, apiVersion string) (string, string, string)
name,
metav1.GetOptions{})
if err2 != nil {
fmt.Printf("Error 2:%v\n", err2)
fmt.Println(err2)
//fmt.Printf("Error 2:%v\n", err2)
//fmt.Println(err2)
return rootKind, rootName, rootAPIVersion
}

Expand Down Expand Up @@ -822,8 +825,8 @@ func getAPIDetailsFromHelmReleaseAnnotation(releaseName string) (string, string,
}

func applyPolicies(ar *v1.AdmissionReview, customAPI, rootKind, rootName, rootNamespace string) []patchOperation {
req := ar.Request
body := req.Object.Raw
//req := ar.Request
//body := req.Object.Raw

/*
podName, _ := jsonparser.GetUnsafeString(req.Object.Raw, "metadata", "name")
Expand All @@ -839,12 +842,11 @@ func applyPolicies(ar *v1.AdmissionReview, customAPI, rootKind, rootName, rootNa
}*/

// TODO: Defaulting to the first container. Take input for additional containers
/*
_, _, _, err1 := jsonparser.Get(body, "spec", "containers", "[0]", "resources")
if err1 != nil {
fmt.Printf("Error:%v\n", err1)
} /*else {
//fmt.Printf("Resources:%v\n", string(res))
}*/
} */

var operation string
//fmt.Printf("DataType:%s\n", dataType)
Expand Down Expand Up @@ -880,6 +882,7 @@ func applyPolicies(ar *v1.AdmissionReview, customAPI, rootKind, rootName, rootNa
podResRequest["cpu"] = cpuRequest
podResRequest["memory"] = memRequest

//TODO: Defaulting to the first container. Take input for additional containers
patch1 := patchOperation{
Op: operation,
Path: "/spec/containers/0/resources/requests",
Expand All @@ -899,6 +902,7 @@ func applyPolicies(ar *v1.AdmissionReview, customAPI, rootKind, rootName, rootNa
podResLimits["cpu"] = cpuLimit
podResLimits["memory"] = memLimit

//TODO: Defaulting to the first container. Take input for additional containers
patch2 := patchOperation{
Op: operation,
Path: "/spec/containers/0/resources/limits",
Expand Down Expand Up @@ -939,12 +943,21 @@ func getFieldValueFromInstance(fieldName, rootKind, rootName string) string {
//rootkey := lowercaseRootKind + "/" + rootNamespace + "/" + rootName
rootkey := lowercaseRootKind + "-" + rootName
//fmt.Printf("Root Key:%s\n", rootkey)
arSaved := resourceNameObjMap[rootkey].(*v1.AdmissionReview)
reqObject := arSaved.Request
reqspec := reqObject.Object.Raw

fieldValue, _, _, _ := jsonparser.Get(reqspec, "spec", field)
fieldValueS := string(fieldValue)
/*fmt.Printf("Printing resourceNameObjMap -- \n")
for key, value := range resourceNameObjMap {
fmt.Println(key, ":", value)
}
fmt.Printf("--\n")*/

val := resourceNameObjMap[rootkey]
fieldValueS := ""
if val != nil {
arSaved := resourceNameObjMap[rootkey].(*v1.AdmissionReview)
reqObject := arSaved.Request
reqspec := reqObject.Object.Raw
fieldValue, _, _, _ := jsonparser.Get(reqspec, "spec", field)
fieldValueS = string(fieldValue)
}
/*if err2 != nil {
fmt.Printf("Error:%v\n", err2)
} else {
Expand Down Expand Up @@ -972,18 +985,19 @@ func getObjectDetails(ar *v1.AdmissionReview) (string, string, string) {
}

func trackCustomAPIs(ar *v1.AdmissionReview) *v1.AdmissionResponse {
fmt.Printf("Inside trackCustomAPIs...")
req := ar.Request
body := req.Object.Raw

var platformWorkflow platformworkflowv1alpha1.ResourceComposition
err := json.Unmarshal(body, &platformWorkflow)
if err != nil {
json.Unmarshal(body, &platformWorkflow)
/*if err != nil {
fmt.Println(err)
}
}*/

platformWorkflowName, _ := jsonparser.GetUnsafeString(body, "metadata", "name")

namespace1, _, _, err := jsonparser.Get(body, "metadata", "namespace")
namespace1, _, _, _ := jsonparser.Get(body, "metadata", "namespace")
namespace := string(namespace1)
if namespace == "" {
namespace = "default"
Expand Down Expand Up @@ -1221,11 +1235,11 @@ func getPaCAnnotation(ar *v1.AdmissionReview) map[string]string {
crdplural, _ := jsonparser.GetUnsafeString(body, "spec", "names", "plural")
crdversion, _ := jsonparser.GetUnsafeString(body, "spec", "versions","[0]","name")
crdgroup, _ := jsonparser.GetUnsafeString(body, "spec", "group")
fmt.Printf("CRDKind:%s, CRDPlural:%s, CRDVersion:%s\n", crdkind, crdplural, crdversion)
//fmt.Printf("CRDKind:%s, CRDPlural:%s, CRDVersion:%s\n", crdkind, crdplural, crdversion)
customAPI := crdgroup + "/" + crdversion + "/" + crdkind
apiVersion := crdgroup + "/" + crdversion
platformWorkflowName, ok := customAPIPlatformWorkflowMap[customAPI]
fmt.Printf("PlatformWorkflowName:%s, ok:%v\n", platformWorkflowName, ok)
//fmt.Printf("PlatformWorkflowName:%s, ok:%v\n", platformWorkflowName, ok)
chartKinds := ""
if ok {

Expand All @@ -1234,7 +1248,7 @@ func getPaCAnnotation(ar *v1.AdmissionReview) map[string]string {
chartKindsB := DryRunChart(platformWorkflowName, namespace)
chartKinds = string(chartKindsB)*/
chartKinds = chartKindMap[platformWorkflowName]
fmt.Printf("Chart Kinds:%v\n", chartKinds)
//fmt.Printf("Chart Kinds:%v\n", chartKinds)

// If no kinds are found in the dry run then there is nothing to be done.
if chartKinds == "" {
Expand All @@ -1258,7 +1272,7 @@ func getPaCAnnotation(ar *v1.AdmissionReview) map[string]string {

//fmt.Printf("Unique kinds:%v\n", uniqueKinds)
chartKinds = strings.Join(uniqueKinds, ";")
fmt.Printf("Annotating %s\n", chartKinds)
//fmt.Printf("Annotating %s\n", chartKinds)

allAnnotations, _, _, err := jsonparser.Get(req.Object.Raw, "metadata", "annotations")
if err == nil {
Expand All @@ -1278,7 +1292,7 @@ func getPaCAnnotation(ar *v1.AdmissionReview) map[string]string {

namespace = GetNamespace()
manpageConfigMapName := registerManPage(crdkind, apiVersion, platformWorkflowName, namespace)
fmt.Printf("### ManPage ConfigMap Name:%s ####\n", manpageConfigMapName)
//fmt.Printf("### ManPage ConfigMap Name:%s ####\n", manpageConfigMapName)

manPageAnnotation := "resource/usage"
manPageAnnotationValue := manpageConfigMapName
Expand All @@ -1302,9 +1316,9 @@ func checkCRDNameValidity(ar *v1.AdmissionReview) string {
fmt.Errorf("Error:%s\n", err)
}

crname, err := jsonparser.GetUnsafeString(req.Object.Raw, "metadata", "name")
fmt.Printf("CR Name:%s\n", crname)
fmt.Printf("Kind:%s\n", kind)
//crname, err := jsonparser.GetUnsafeString(req.Object.Raw, "metadata", "name")
//fmt.Printf("CR Name:%s\n", crname)
//fmt.Printf("Kind:%s\n", kind)

message1 := "";
if strings.Contains(kind, ".") {
Expand Down

0 comments on commit 771845b

Please sign in to comment.