Skip to content

Commit

Permalink
Move pkg/clone/auth to API lib and introduce single dv.Authorize()
Browse files Browse the repository at this point in the history
This way other projects don't have to vendor in CDI, the API lib will be enough.
While it's not a huge deal at this point, each existing dependency is an entry door
for pulling in more stuff in the future by accident.

Signed-off-by: Alex Kalenyuk <akalenyu@redhat.com>
  • Loading branch information
akalenyu committed Mar 16, 2023
1 parent 1e96ca3 commit 6bcfbde
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 174 deletions.
2 changes: 0 additions & 2 deletions pkg/apiserver/webhooks/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//pkg/client/clientset/versioned:go_default_library",
"//pkg/clone:go_default_library",
"//pkg/common:go_default_library",
"//pkg/controller/common:go_default_library",
"//pkg/token:go_default_library",
"//staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1:go_default_library",
"//vendor/github.com/appscode/jsonpatch:go_default_library",
"//vendor/github.com/gorhill/cronexpr:go_default_library",
"//vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1:go_default_library",
"//vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned:go_default_library",
"//vendor/k8s.io/api/admission/v1:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1:go_default_library",
Expand Down
153 changes: 28 additions & 125 deletions pkg/apiserver/webhooks/datavolume-mutate.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,16 @@ import (
"context"
"encoding/json"

snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
admissionv1 "k8s.io/api/admission/v1"
authv1 "k8s.io/api/authorization/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sfield "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"

cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
cdiclient "kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned"
"kubevirt.io/containerized-data-importer/pkg/clone"
"kubevirt.io/containerized-data-importer/pkg/common"
cc "kubevirt.io/containerized-data-importer/pkg/controller/common"
"kubevirt.io/containerized-data-importer/pkg/token"
Expand All @@ -44,45 +42,31 @@ type dataVolumeMutatingWebhook struct {
k8sClient kubernetes.Interface
cdiClient cdiclient.Interface
tokenGenerator token.Generator
proxy clone.SubjectAccessReviewsProxy
proxy cdiv1.SubjectAccessReviewsProxy
}

type sarProxy struct {
client kubernetes.Interface
}

type cloneType int

const (
noClone cloneType = iota
pvcClone
snapshotClone
)
func (p *sarProxy) Create(sar *authv1.SubjectAccessReview) (*authv1.SubjectAccessReview, error) {
return p.client.AuthorizationV1().SubjectAccessReviews().Create(context.TODO(), sar, metav1.CreateOptions{})
}

type cloneSourceHandler struct {
cloneType cloneType
tokenResource metav1.GroupVersionResource
cloneAuthFunc clone.UserCloneAuthFunc
sourceName string
sourceNamespace string
type nsProxy struct {
client kubernetes.Interface
}

var (
tokenResourcePvc = metav1.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "persistentvolumeclaims",
}
func (p *nsProxy) Get(name string) (*corev1.Namespace, error) {
return p.client.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{})
}

tokenResourceSnapshot = metav1.GroupVersionResource{
Group: snapshotv1.GroupName,
Version: snapshotv1.SchemeGroupVersion.Version,
Resource: "volumesnapshots",
}
)
type dsProxy struct {
client cdiclient.Interface
}

func (p *sarProxy) Create(sar *authv1.SubjectAccessReview) (*authv1.SubjectAccessReview, error) {
return p.client.AuthorizationV1().SubjectAccessReviews().Create(context.TODO(), sar, metav1.CreateOptions{})
func (p *dsProxy) Get(namespace, name string) (*cdiv1.DataSource, error) {
return p.client.CdiV1beta1().DataSources(namespace).Get(context.TODO(), name, metav1.GetOptions{})
}

func (wh *dataVolumeMutatingWebhook) Admit(ar admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
Expand Down Expand Up @@ -120,10 +104,6 @@ func (wh *dataVolumeMutatingWebhook) Admit(ar admissionv1.AdmissionReview) *admi
}
}

_, prePopulated := dataVolume.Annotations[cc.AnnPrePopulated]
_, checkStaticVolume := dataVolume.Annotations[cc.AnnCheckStaticVolume]
noTokenOkay := prePopulated || checkStaticVolume

targetNamespace, targetName := dataVolume.Namespace, dataVolume.Name
if targetNamespace == "" {
targetNamespace = ar.Request.Namespace
Expand All @@ -133,52 +113,15 @@ func (wh *dataVolumeMutatingWebhook) Admit(ar admissionv1.AdmissionReview) *admi
targetName = ar.Request.Name
}

cloneSourceHandler, err := newCloneSourceHandler(dataVolume, wh.cdiClient)
ok, reason, response, err := modifiedDataVolume.Authorize(ar.Request.Namespace, ar.Request.Name, &sarProxy{client: wh.k8sClient}, &nsProxy{client: wh.k8sClient}, &dsProxy{client: wh.cdiClient}, ar.Request.UserInfo)
if err != nil {
if k8serrors.IsNotFound(err) && noTokenOkay {
// no token needed, likely since no datasource
klog.V(3).Infof("DataVolume %s/%s is pre/static populated, not adding token, no datasource", targetNamespace, targetName)
if err == cdiv1.ErrNoTokenOkay {
return toPatchResponse(dataVolume, modifiedDataVolume)
}
return toAdmissionResponseError(err)
}

if cloneSourceHandler.cloneType == noClone {
klog.V(3).Infof("DataVolume %s/%s not cloning", targetNamespace, targetName)
return toPatchResponse(dataVolume, modifiedDataVolume)
}

// only add token at create time
if ar.Request.Operation != admissionv1.Create {
return toPatchResponse(dataVolume, modifiedDataVolume)
}

sourceName, sourceNamespace := cloneSourceHandler.sourceName, cloneSourceHandler.sourceNamespace
if sourceNamespace == "" {
sourceNamespace = targetNamespace
}

_, err = wh.k8sClient.CoreV1().Namespaces().Get(context.TODO(), sourceNamespace, metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) && noTokenOkay {
// no token needed, likely since no source namespace
klog.V(3).Infof("DataVolume %s/%s is pre/static populated, not adding token, no source namespace", targetNamespace, targetName)
return toPatchResponse(dataVolume, modifiedDataVolume)
}
return toAdmissionResponseError(err)
}

ok, reason, err := cloneSourceHandler.cloneAuthFunc(wh.proxy, sourceNamespace, sourceName, targetNamespace, ar.Request.UserInfo)
if err != nil {
return toAdmissionResponseError(err)
}

if !ok {
if noTokenOkay {
klog.V(3).Infof("DataVolume %s/%s is pre/static populated, not adding token, auth failed", targetNamespace, targetName)
return toPatchResponse(dataVolume, modifiedDataVolume)
}

causes := []metav1.StatusCause{
{
Type: metav1.CauseTypeFieldValueInvalid,
Expand All @@ -189,11 +132,21 @@ func (wh *dataVolumeMutatingWebhook) Admit(ar admissionv1.AdmissionReview) *admi
return toRejectedAdmissionResponse(causes)
}

// only add token at create time
if ar.Request.Operation != admissionv1.Create {
return toPatchResponse(dataVolume, modifiedDataVolume)
}

sourceName, sourceNamespace := response.SourceName, response.SourceNamespace
if sourceNamespace == "" {
sourceNamespace = targetNamespace
}

tokenData := &token.Payload{
Operation: token.OperationClone,
Name: sourceName,
Namespace: sourceNamespace,
Resource: cloneSourceHandler.tokenResource,
Resource: response.TokenResource,
Params: map[string]string{
"targetNamespace": targetNamespace,
"targetName": targetName,
Expand All @@ -214,53 +167,3 @@ func (wh *dataVolumeMutatingWebhook) Admit(ar admissionv1.AdmissionReview) *admi

return toPatchResponse(dataVolume, modifiedDataVolume)
}

func newCloneSourceHandler(dataVolume *cdiv1.DataVolume, cdiClient cdiclient.Interface) (*cloneSourceHandler, error) {
var pvcSource *cdiv1.DataVolumeSourcePVC
var snapshotSource *cdiv1.DataVolumeSourceSnapshot

if dataVolume.Spec.Source != nil {
if dataVolume.Spec.Source.PVC != nil {
pvcSource = dataVolume.Spec.Source.PVC
} else if dataVolume.Spec.Source.Snapshot != nil {
snapshotSource = dataVolume.Spec.Source.Snapshot
}
} else if dataVolume.Spec.SourceRef != nil && dataVolume.Spec.SourceRef.Kind == cdiv1.DataVolumeDataSource {
ns := dataVolume.Namespace
if dataVolume.Spec.SourceRef.Namespace != nil && *dataVolume.Spec.SourceRef.Namespace != "" {
ns = *dataVolume.Spec.SourceRef.Namespace
}
dataSource, err := cdiClient.CdiV1beta1().DataSources(ns).Get(context.TODO(), dataVolume.Spec.SourceRef.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
if dataSource.Spec.Source.PVC != nil {
pvcSource = dataSource.Spec.Source.PVC
} else if dataSource.Spec.Source.Snapshot != nil {
snapshotSource = dataSource.Spec.Source.Snapshot
}
}

switch {
case pvcSource != nil:
return &cloneSourceHandler{
cloneType: pvcClone,
tokenResource: tokenResourcePvc,
cloneAuthFunc: clone.CanUserClonePVC,
sourceName: pvcSource.Name,
sourceNamespace: pvcSource.Namespace,
}, nil
case snapshotSource != nil:
return &cloneSourceHandler{
cloneType: snapshotClone,
tokenResource: tokenResourceSnapshot,
cloneAuthFunc: clone.CanUserCloneSnapshot,
sourceName: snapshotSource.Name,
sourceNamespace: snapshotSource.Namespace,
}, nil
default:
return &cloneSourceHandler{
cloneType: noClone,
}, nil
}
}
14 changes: 0 additions & 14 deletions pkg/clone/BUILD.bazel

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module kubevirt.io/containerized-data-importer-api
go 1.19

require (
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.1
github.com/openshift/api v0.0.0-20211217221424-8779abfbd571
k8s.io/api v0.23.5
k8s.io/apimachinery v0.23.5
k8s.io/klog/v2 v2.40.1
kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90
)

Expand All @@ -22,7 +24,6 @@ require (
golang.org/x/text v0.7.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.40.1 // indirect
k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.1 h1:OqBS3UAo3eGWplYXoMLaWnx/7Zj5Ogh0VO/FuVOL+/o=
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.1/go.mod h1:tnHiLn3P10N95fjn7O40QH5ovN0EFGAxqdTpUMrX6bU=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"authorize.go",
"authorize_utils.go",
"doc.go",
"register.go",
"types.go",
Expand All @@ -15,11 +17,16 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core:go_default_library",
"//vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1:go_default_library",
"//vendor/github.com/openshift/api/config/v1:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/api/authorization/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/klog/v2:go_default_library",
"//vendor/kubevirt.io/controller-lifecycle-operator-sdk/api:go_default_library",
],
)
Loading

0 comments on commit 6bcfbde

Please sign in to comment.