Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Commit

Permalink
Add support for admission.k8s.io/v1 AdmissionReview and admissionregi…
Browse files Browse the repository at this point in the history
…stration.k8s.io/v1 MutatingWebhookConfiguration (in addition to v1beta1) (#49)
  • Loading branch information
asaintsever authored May 19, 2021
1 parent f9ed1d3 commit 5845650
Show file tree
Hide file tree
Showing 15 changed files with 681 additions and 321 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# Changelog for Vault Sidecar Injector

## Release v7.1.2 - 2021-XX-XX
## Release v7.2.0 - 2021-05-XX

This release comes with support for `admission.k8s.io/v1` AdmissionReview and `admissionregistration.k8s.io/v1` MutatingWebhookConfiguration on Kubernetes 1.16+. As a result, Vault Sidecar Injector now handles both v1 and v1beta1 versions of those resources.

*Note that `admission.k8s.io/v1beta1` AdmissionReview and `admissionregistration.k8s.io/v1beta1` MutatingWebhookConfiguration should not be supported (nor available) anymore on Kubernetes 1.22+*

**Changed**

- [VSI #48](https://github.com/Talend/vault-sidecar-injector/pull/48) - Minor chart updates (adjust CPU & memory for injected containers, add checks during chart install)
- [VSI #51](https://github.com/Talend/vault-sidecar-injector/pull/51) - Update base image to CentOS 7.9.2009

**Added**

- [VSI #49](https://github.com/Talend/vault-sidecar-injector/pull/49) - Add support for `admission.k8s.io/v1` AdmissionReview and `admissionregistration.k8s.io/v1` MutatingWebhookConfiguration (in addition to v1beta1)

## Release v7.1.1 - 2021-04-02

**Fixed**
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ ENV TALEND_USER=talend
ENV TALEND_USERGROUP=$TALEND_USER
ENV TALEND_UID=61000

# Update CentOS (note that --security flag does not work on CentOS: https://www.caseylabs.com/centos-automatic-security-updates-do-not-work/)
# Update CentOS (note that --security flag does not work on CentOS: https://forums.centos.org/viewtopic.php?t=59369)
RUN set -x \
&& yum -y update \
&& yum clean all \
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.local
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ENV TALEND_USER=talend
ENV TALEND_USERGROUP=$TALEND_USER
ENV TALEND_UID=61000

# Update CentOS (note that --security flag does not work on CentOS: https://www.caseylabs.com/centos-automatic-security-updates-do-not-work/)
# Update CentOS (note that --security flag does not work on CentOS: https://forums.centos.org/viewtopic.php?t=59369)
RUN set -x \
&& yum -y update \
&& yum clean all \
Expand Down
2 changes: 1 addition & 1 deletion VERSION_CHART
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.2.2
4.3.0
2 changes: 1 addition & 1 deletion VERSION_RELEASE
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.1.2
7.2.0
2 changes: 1 addition & 1 deletion VERSION_VSI
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.1.1
7.2.0
11 changes: 11 additions & 0 deletions deploy/helm/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,14 @@ Add Vault flag to skip verification of TLS certificates
-tls-skip-verify
{{- end -}}
{{- end -}}

{{/*
Return the appropriate apiVersion for MutatingWebhookConfiguration
*/}}
{{- define "mutatingwebhookconfiguration.apiversion" -}}
{{- if semverCompare ">=1.16" .Capabilities.KubeVersion.Version -}}
"admissionregistration.k8s.io/v1"
{{- else -}}
"admissionregistration.k8s.io/v1beta1"
{{- end -}}
{{- end -}}
6 changes: 5 additions & 1 deletion deploy/helm/templates/mutatingwebhookconfiguration.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
apiVersion: admissionregistration.k8s.io/v1beta1
apiVersion: {{ include "mutatingwebhookconfiguration.apiversion" . }}
kind: MutatingWebhookConfiguration
metadata:
name: {{ include "talend-vault-sidecar-injector.fullname" . }}
Expand All @@ -16,5 +16,9 @@ webhooks:
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
{{- if semverCompare ">=1.16" .Capabilities.KubeVersion.Version }}
admissionReviewVersions: ["v1", "v1beta1"]
sideEffects: None
{{- end }}
failurePolicy: {{ include "talend-vault-sidecar-injector.failurePolicy" .Values }}
{{ include "talend-vault-sidecar-injector.namespaceSelector" . | indent 4 }}
39 changes: 28 additions & 11 deletions pkg/k8s/k8s.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2019-2020 Talend - www.talend.com
// Copyright © 2019-2021 Talend - www.talend.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -71,6 +71,12 @@ func (k8sctl *K8SClient) CreateCertSecret(ca, cert, key []byte) error {
// Other way to get current namespace:
//ns, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")

// If secret already exists: log a warning before deleting it
if _, err := k8sctl.CoreV1().Secrets(strings.TrimSpace(string(ns))).Get(k8sctl.WebhookSecretName, metav1.GetOptions{}); err == nil {
klog.Warning("Webhook secret already exists: will be deleted then created again from new generated certificate")
k8sctl.DeleteCertSecret()
}

// Create Secret in same namespace as webhook
_, err := k8sctl.CoreV1().Secrets(strings.TrimSpace(string(ns))).Create(secret)
if err != nil {
Expand Down Expand Up @@ -111,17 +117,28 @@ func (k8sctl *K8SClient) PatchWebhookConfiguration(cacertfile string) error {
return err
}

webhookPatch := []byte(fmt.Sprintf(
`[{
"op": "add",
"path": "/webhooks/0/clientConfig/caBundle",
"value": %q
}]`, base64.StdEncoding.EncodeToString(caPEM)))

// Patch MutatingWebhookConfiguration resource with CA (should be base64-encoded PEM-encoded)
_, err = k8sctl.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Patch(
k8sctl.WebhookCfgName, types.JSONPatchType, []byte(fmt.Sprintf(
`[{
"op": "add",
"path": "/webhooks/0/clientConfig/caBundle",
"value": %q
}]`, base64.StdEncoding.EncodeToString(caPEM))))
if err != nil {
klog.Errorf("Error patching MutatingWebhookConfiguration's caBundle: %s", err)
return err
if _, err = k8sctl.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(k8sctl.WebhookCfgName, metav1.GetOptions{}); err == nil {
// v1 support
klog.Infof("Patching MutatingWebhookConfiguration v1 resource %v", k8sctl.WebhookCfgName)
if _, err = k8sctl.AdmissionregistrationV1().MutatingWebhookConfigurations().Patch(k8sctl.WebhookCfgName, types.JSONPatchType, webhookPatch); err != nil {
klog.Errorf("Error patching MutatingWebhookConfiguration's caBundle: %s", err)
return err
}
} else {
// v1beta1 support
klog.Infof("Patching MutatingWebhookConfiguration v1beta1 resource %v", k8sctl.WebhookCfgName)
if _, err = k8sctl.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Patch(k8sctl.WebhookCfgName, types.JSONPatchType, webhookPatch); err != nil {
klog.Errorf("Error patching MutatingWebhookConfiguration's caBundle: %s", err)
return err
}
}

return nil
Expand Down
89 changes: 89 additions & 0 deletions pkg/webhook/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright © 2019-2021 Talend - www.talend.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package webhook

import (
"unsafe"

admv1 "k8s.io/api/admission/v1"
admv1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

// Note:
// =====
// These conversions come from https://github.com/jetstack/cert-manager/blob/ab0cd57dc58fd73a76fd96bd9d1402bd5ae96582/pkg/webhook/server/util/convert.go
// (which are adapted from https://github.com/kubernetes/kubernetes/blob/03d322035d2f199f2163658d94a153ed2b9de667/pkg/apis/admission/v1beta1/zz_generated.conversion.go)

func Convert_v1beta1_AdmissionReview_To_admission_AdmissionReview(in *admv1beta1.AdmissionReview, out *admv1.AdmissionReview) {
if in.Request != nil {
if out.Request == nil {
out.Request = &admv1.AdmissionRequest{}
}
in, out := &in.Request, &out.Request
*out = new(admv1.AdmissionRequest)
Convert_v1beta1_AdmissionRequest_To_admission_AdmissionRequest(*in, *out)
} else {
out.Request = nil
}
out.Response = (*admv1.AdmissionResponse)(unsafe.Pointer(in.Response))
}

func Convert_v1beta1_AdmissionRequest_To_admission_AdmissionRequest(in *admv1beta1.AdmissionRequest, out *admv1.AdmissionRequest) {
out.UID = types.UID(in.UID)
out.Kind = in.Kind
out.Resource = in.Resource
out.SubResource = in.SubResource
out.RequestKind = (*metav1.GroupVersionKind)(unsafe.Pointer(in.RequestKind))
out.RequestResource = (*metav1.GroupVersionResource)(unsafe.Pointer(in.RequestResource))
out.RequestSubResource = in.RequestSubResource
out.Name = in.Name
out.Namespace = in.Namespace
out.Operation = admv1.Operation(in.Operation)
out.Object = in.Object
out.OldObject = in.OldObject
out.Options = in.Options
}

func Convert_admission_AdmissionReview_To_v1beta1_AdmissionReview(in *admv1.AdmissionReview, out *admv1beta1.AdmissionReview) {
if in.Request != nil {
if out.Request == nil {
out.Request = &admv1beta1.AdmissionRequest{}
}
in, out := &in.Request, &out.Request
*out = new(admv1beta1.AdmissionRequest)
Convert_admission_AdmissionRequest_To_v1beta1_AdmissionRequest(*in, *out)
} else {
out.Request = nil
}
out.Response = (*admv1beta1.AdmissionResponse)(unsafe.Pointer(in.Response))
}

func Convert_admission_AdmissionRequest_To_v1beta1_AdmissionRequest(in *admv1.AdmissionRequest, out *admv1beta1.AdmissionRequest) {
out.UID = types.UID(in.UID)
out.Kind = in.Kind
out.Resource = in.Resource
out.SubResource = in.SubResource
out.RequestKind = (*metav1.GroupVersionKind)(unsafe.Pointer(in.RequestKind))
out.RequestResource = (*metav1.GroupVersionResource)(unsafe.Pointer(in.RequestResource))
out.RequestSubResource = in.RequestSubResource
out.Name = in.Name
out.Namespace = in.Namespace
out.Operation = admv1beta1.Operation(in.Operation)
out.Object = in.Object
out.OldObject = in.OldObject
out.Options = in.Options
}
109 changes: 109 additions & 0 deletions pkg/webhook/mutate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright © 2019-2021 Talend - www.talend.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package webhook

import (
"encoding/json"
ctx "talend/vault-sidecar-injector/pkg/context"

admv1 "k8s.io/api/admission/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"
)

func (vaultInjector *VaultInjector) mutate(ar *admv1.AdmissionReview) *admv1.AdmissionResponse {
var pod corev1.Pod
var podName, podNamespace string

req := ar.Request

if err := json.Unmarshal(req.Object.Raw, &pod); err != nil {
klog.Errorf("Could not unmarshal raw object: %v", err)
return &admv1.AdmissionResponse{
UID: req.UID,
Result: &metav1.Status{
Message: err.Error(),
},
}
}

if klog.V(5) { // enabled by providing '-v=5' at least
klog.Infof("Pod=%+v", pod)
}

if pod.Name == "" {
podName = pod.GenerateName
} else {
podName = pod.Name
}

if pod.Namespace == "" {
podNamespace = metav1.NamespaceDefault
} else {
podNamespace = pod.Namespace
}

klog.Infof("AdmissionReview '%v' for '%+v', Namespace=%v Name='%v (%s/%s)' UID=%v patchOperation=%v",
ar.GroupVersionKind(), req.Kind, req.Namespace, req.Name, podNamespace, podName, req.UID, req.Operation)

// Determine whether to perform mutation
if !mutationRequired(ignoredNamespaces, vaultInjector.VaultInjectorAnnotationsFQ, &pod.ObjectMeta) {
klog.Infof("Skipping mutation for %s/%s due to policy check", podNamespace, podName)
return &admv1.AdmissionResponse{
UID: req.UID,
Allowed: true,
}
}

annotations := map[string]string{vaultInjector.VaultInjectorAnnotationsFQ[ctx.VaultInjectorAnnotationStatusKey]: ctx.VaultInjectorStatusInjected}
patchBytes, err := vaultInjector.createPatch(&pod, annotations)
if err != nil {
return &admv1.AdmissionResponse{
UID: req.UID,
Allowed: false,
Result: &metav1.Status{
Message: err.Error(),
},
}
}

klog.Infof("AdmissionResponse: patch=%v\n", string(patchBytes))
return &admv1.AdmissionResponse{
UID: req.UID,
Allowed: true,
Patch: patchBytes,
PatchType: func() *admv1.PatchType {
pt := admv1.PatchTypeJSONPatch
return &pt
}(),
}
}

// Create mutation patch for resources
func (vaultInjector *VaultInjector) createPatch(pod *corev1.Pod, annotations map[string]string) ([]byte, error) {

patchPodSpec, err := vaultInjector.updatePodSpec(pod)
if err != nil {
return nil, err
}

var patch []ctx.PatchOperation

patch = append(patch, patchPodSpec...)
patch = append(patch, updateAnnotation(pod.Annotations, annotations)...)

return json.Marshal(patch)
}
Loading

0 comments on commit 5845650

Please sign in to comment.