Skip to content

Commit

Permalink
show trampoline pod fix
Browse files Browse the repository at this point in the history
  • Loading branch information
deads2k committed Mar 27, 2024
1 parent 0d6231f commit 9e430ae
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 0 deletions.
28 changes: 28 additions & 0 deletions test/extended/authorization/per_node_pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apiVersion: v1
kind: Pod
metadata:
name: sa-token
namespace: e2e-ns
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: sleeper
image: registry.build03.ci.openshift.org/ci-ln-i4f498b/stable@sha256:9f9772bb3afa8877a2d58de06d35e75e213bc75df692ee2223746ff3cdf9ced1
command:
- sleep
args:
- "1200"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
13 changes: 13 additions & 0 deletions test/extended/authorization/per_node_rolebinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: sa-token
namespace: e2e-ns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: edit
subjects:
- kind: ServiceAccount
name: default
namespace: e2e-ns
119 changes: 119 additions & 0 deletions test/extended/authorization/per_node_update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package authorization

import (
"context"
_ "embed"
"fmt"
o "github.com/onsi/gomega"
"github.com/openshift/library-go/pkg/operator/resource/resourceread"
authenticationv1 "k8s.io/api/authentication/v1"
"k8s.io/apiserver/pkg/authentication/serviceaccount"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/test/e2e/framework/pod"
imageutils "k8s.io/kubernetes/test/utils/image"
"strings"

g "github.com/onsi/ginkgo/v2"
exutil "github.com/openshift/origin/test/extended/util"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
//go:embed per_node_pod.yaml
perNodeCheckPod string
//go:embed per_node_rolebinding.yaml
perNodeRoleBinding string
//go:embed per_node_validatingadmissionpolicy.yaml
perNodeCheckValidatingAdmissionPolicy string
//go:embed per_node_validatingadmissionpolicybinding.yaml
perNodeCheckValidatingAdmissionPolicyBinding string
)

var _ = g.Describe("[sig-auth][Feature:ServiceAccountTokenNodeBinding][OCPFeatureGate:ValidatingAdmissionPolicy] per-node SA tokens", func() {
defer g.GinkgoRecover()
oc := exutil.NewCLI("by-node-access")

g.It(fmt.Sprintf("can restrict access by-node"), func() {
ctx := context.Background()
podYaml := strings.ReplaceAll(perNodeCheckPod, "e2e-ns", oc.Namespace())
podYaml = strings.ReplaceAll(podYaml, "registry.build03.ci.openshift.org/ci-ln-i4f498b/stable@sha256:9f9772bb3afa8877a2d58de06d35e75e213bc75df692ee2223746ff3cdf9ced1", imageutils.GetE2EImage(imageutils.Agnhost))
podToCreate := resourceread.ReadPodV1OrDie([]byte(podYaml))
rolebindingYaml := strings.ReplaceAll(perNodeRoleBinding, "e2e-ns", oc.Namespace())
roleBindingToCreate := resourceread.ReadRoleBindingV1OrDie([]byte(rolebindingYaml))
admission := strings.ReplaceAll(perNodeCheckValidatingAdmissionPolicy, "e2e-ns", oc.Namespace())
admissionToCreate := resourceread.ReadValidatingAdmissionPolicyV1beta1OrDie([]byte(admission))
admissionBinding := strings.ReplaceAll(perNodeCheckValidatingAdmissionPolicyBinding, "e2e-ns", oc.Namespace())
admissionBindingToCreate := resourceread.ReadValidatingAdmissionPolicyBindingV1beta1OrDie([]byte(admissionBinding))

defer func() {
oc.AdminKubeClient().AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Delete(context.Background(), admissionToCreate.Name, metav1.DeleteOptions{})
oc.AdminKubeClient().AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Delete(context.Background(), admissionBindingToCreate.Name, metav1.DeleteOptions{})
}()

var err error
_, err = oc.AdminKubeClient().AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.Background(), admissionToCreate, metav1.CreateOptions{})
o.Expect(err).ToNot(o.HaveOccurred())
_, err = oc.AdminKubeClient().AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(context.Background(), admissionBindingToCreate, metav1.CreateOptions{})
o.Expect(err).ToNot(o.HaveOccurred())
_, err = oc.AdminKubeClient().RbacV1().RoleBindings(oc.Namespace()).Create(context.Background(), roleBindingToCreate, metav1.CreateOptions{})
o.Expect(err).ToNot(o.HaveOccurred())
actualPod, err := oc.KubeClient().CoreV1().Pods(oc.Namespace()).Create(ctx, podToCreate, metav1.CreateOptions{})
o.Expect(err).ToNot(o.HaveOccurred())

err = pod.WaitForPodNameRunningInNamespace(ctx, oc.KubeClient(), actualPod.Name, actualPod.Namespace)
o.Expect(err).ToNot(o.HaveOccurred())
actualPod, err = oc.KubeClient().CoreV1().Pods(oc.Namespace()).Get(ctx, actualPod.Name, metav1.GetOptions{})
o.Expect(err).ToNot(o.HaveOccurred())

nodeScopedSAToken, err := exutil.ExecInPodWithResult(
oc.KubeClient().CoreV1(),
oc.UserConfig(),
actualPod.Namespace,
actualPod.Name,
"sleeper",
[]string{"cat", "/var/run/secrets/kubernetes.io/serviceaccount/token"},
)
o.Expect(err).ToNot(o.HaveOccurred())

nodeScopedClientConfig := rest.AnonymousClientConfig(oc.UserConfig())
nodeScopedClientConfig.BearerToken = nodeScopedSAToken
nodeScopedClient, err := kubernetes.NewForConfig(nodeScopedClientConfig)
o.Expect(err).ToNot(o.HaveOccurred())
saUser, err := nodeScopedClient.AuthenticationV1().SelfSubjectReviews().Create(ctx, &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{})
o.Expect(err).ToNot(o.HaveOccurred())
expectedUser := serviceaccount.MakeUsername(oc.Namespace(), "default")
o.Expect(saUser.Status.UserInfo.Username).To(o.Equal(expectedUser))
expectedNode := authenticationv1.ExtraValue([]string{actualPod.Spec.NodeName})
o.Expect(saUser.Status.UserInfo.Extra["authentication.kubernetes.io/node-name"]).To(o.Equal(expectedNode))

allowedConfigMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: oc.Namespace(),
Name: actualPod.Spec.NodeName,
},
}
disallowedConfigMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: oc.Namespace(),
Name: "unlikely-node",
},
}
actualAllowedConfigMap, err := nodeScopedClient.CoreV1().ConfigMaps(oc.Namespace()).Create(ctx, allowedConfigMap, metav1.CreateOptions{})
o.Expect(err).ToNot(o.HaveOccurred())
_, err = nodeScopedClient.CoreV1().ConfigMaps(oc.Namespace()).Create(ctx, disallowedConfigMap, metav1.CreateOptions{})
o.Expect(err).To(o.HaveOccurred())
o.Expect(err.Error()).To(o.ContainSubstring("this user may only modify configmaps that belong to the node the pod is running on"))

// now create so we can see the update cases
actualDisallowedConfigMap, err := oc.AdminKubeClient().CoreV1().ConfigMaps(oc.Namespace()).Create(ctx, disallowedConfigMap, metav1.CreateOptions{})
o.Expect(err).ToNot(o.HaveOccurred())

actualAllowedConfigMap, err = nodeScopedClient.CoreV1().ConfigMaps(oc.Namespace()).Update(ctx, actualAllowedConfigMap, metav1.UpdateOptions{})
o.Expect(err).ToNot(o.HaveOccurred())
_, err = nodeScopedClient.CoreV1().ConfigMaps(oc.Namespace()).Update(ctx, actualDisallowedConfigMap, metav1.UpdateOptions{})
o.Expect(err).To(o.HaveOccurred())
o.Expect(err.Error()).To(o.ContainSubstring("this user may only modify configmaps that belong to the node the pod is running on"))
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
name: "only-allow-name-matching-node-configmaps"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
# notice you cannot restrict DELETEs. Your per-node actor should not have deletion powers.
# it probably shouldn't have create powers either, but we can control that
operations: ["CREATE", "UPDATE"]
resources: ["configmaps"]
variables:
- name: hasNodeName
expression: ('authentication.kubernetes.io/node-name' in request.userInfo.extra)
- name: isPartitionedServiceAccount
expression: request.userInfo.username == "system:serviceaccount:e2e-ns:default"
validations:
- expression: "!variables.isPartitionedServiceAccount || (!variables.hasNodeName) || object.metadata.name == request.userInfo.extra[\"authentication.kubernetes.io/node-name\"][0]"
message: "this user may only modify configmaps that belong to the node the pod is running on"
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "only-allow-name-matching-node-configmaps"
spec:
policyName: "only-allow-name-matching-node-configmaps"
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: "e2e-ns"

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9e430ae

Please sign in to comment.