Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
CoreyCook8 authored and ccojocar committed Jan 23, 2024
1 parent 019a58c commit bc4735a
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 12 deletions.
2 changes: 2 additions & 0 deletions api/profilebinding/v1alpha1/profilebinding_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ type ProfileBindingKind string
const (
ProfileBindingKindSeccompProfile ProfileBindingKind = "SeccompProfile"
ProfileBindingKindSelinuxProfile ProfileBindingKind = "SelinuxProfile"
SelectAllContainersImage string = "*"
)

// ProfileBindingSpec defines the desired state of ProfileBinding.
type ProfileBindingSpec struct {
// ProfileRef references a SeccompProfile or other profile type in the current namespace.
ProfileRef ProfileRef `json:"profileRef"`
// Image name within pod containers to match to the profile.
// Use the "*" string to bind the profile to all pods.
Image string `json:"image"`
}

Expand Down
15 changes: 15 additions & 0 deletions installation-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,21 @@ spec:
image: nginx:1.19.1
```

You can enable a default profile binding by using the string "*" as the image name.
This will only apply a profile binding if no other profile binding matches a container in the pod.

```yaml
apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: ProfileBinding
metadata:
name: nginx-binding
spec:
profileRef:
kind: SeccompProfile
name: profile-complain
image: *
```

If the Pod is already running, it will need to be restarted in order to pick up
the profile binding. Once the binding is created and the Pod is created or
recreated, the SeccompProfile should be applied to the container whose image
Expand Down
95 changes: 84 additions & 11 deletions internal/pkg/webhooks/binding/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ func (p *podBinder) Handle(ctx context.Context, req admission.Request) admission
podChanged := false
podID := req.Namespace + "/" + req.Name
pod := &corev1.Pod{}
applyToAllContainers = false
var podBindProfile *interface{}
var podProfileBinding *profilebindingv1alpha1.ProfileBinding

var containers sync.Map
if req.Operation != "DELETE" {
Expand Down Expand Up @@ -147,15 +148,6 @@ func (p *podBinder) Handle(ctx context.Context, req admission.Request) admission
}
continue
}
value, ok := containers.Load(profilebindings[i].Spec.Image)
if !ok {
continue
}
containers, ok := value.(containerList)
if !ok {
continue
}

namespacedName := types.NamespacedName{Namespace: req.Namespace, Name: profileName}
var bindProfile interface{}
var err error
Expand All @@ -173,6 +165,20 @@ func (p *podBinder) Handle(ctx context.Context, req admission.Request) admission
return admission.Errored(http.StatusInternalServerError, err)
}

if profilebindings[i].Spec.Image == profilebindingv1alpha1.SelectAllContainersImage {
podBindProfile = &bindProfile
podProfileBinding = &profilebindings[i]
continue
}
value, ok := containers.Load(profilebindings[i].Spec.Image)
if !ok {
continue
}
containers, ok := value.(containerList)
if !ok {
continue
}

for j := range containers {
podChanged = p.addSecurityContext(containers[j], bindProfile)
}
Expand All @@ -183,7 +189,15 @@ func (p *podBinder) Handle(ctx context.Context, req admission.Request) admission
}
}
if !podChanged {
return admission.Allowed("pod unchanged")
if podBindProfile == nil || podProfileBinding == nil {
return admission.Allowed("pod unchanged")
}
podChanged = p.addPodSecurityContext(pod, *podBindProfile)
if podChanged {
if err := p.addPodToBinding(ctx, podID, podProfileBinding); err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}
}
}
marshaledPod, err := json.Marshal(pod)
if err != nil {
Expand Down Expand Up @@ -295,6 +309,65 @@ func (p *podBinder) addSelinuxContext(
return podChanged
}

func (p *podBinder) addPodSecurityContext(
pod *corev1.Pod, bindProfile interface{},
) bool {
var podChanged bool

switch v := bindProfile.(type) {
case *seccompprofileapi.SeccompProfile:
podChanged = p.addPodSeccompContext(pod, v)
case *selinuxprofileapi.SelinuxProfile:
podChanged = p.addPodSelinuxContext(pod, v)
default:
p.log.Info("Unexpected Profile Type")
return false
}
return podChanged
}

func (p *podBinder) addPodSeccompContext(
pod *corev1.Pod, seccompProfile *seccompprofileapi.SeccompProfile,
) bool {
podChanged := false
profileRef := seccompProfile.Status.LocalhostProfile
sp := corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeLocalhost,
LocalhostProfile: &profileRef,
}
if pod.Spec.SecurityContext == nil {
pod.Spec.SecurityContext = &corev1.PodSecurityContext{}
}
if pod.Spec.SecurityContext.SeccompProfile != nil {
p.log.Info("cannot override existing seccomp profile for pod or container")
} else {
pod.Spec.SecurityContext.SeccompProfile = &sp
podChanged = true
}
return podChanged
}

func (p *podBinder) addPodSelinuxContext(
pod *corev1.Pod, selinuxProfile *selinuxprofileapi.SelinuxProfile,
) bool {
podChanged := false
usage := selinuxProfile.Status.Usage
sl := corev1.SELinuxOptions{
Type: usage,
}

if pod.Spec.SecurityContext == nil {
pod.Spec.SecurityContext = &corev1.PodSecurityContext{}
}
if pod.Spec.SecurityContext.SELinuxOptions != nil {
p.log.Info("cannot override existing selinux profile for pod or container")
} else {
pod.Spec.SecurityContext.SELinuxOptions = &sl
podChanged = true
}
return podChanged
}

func (p *podBinder) addPodToBinding(
ctx context.Context,
podID string,
Expand Down
85 changes: 84 additions & 1 deletion internal/pkg/webhooks/binding/binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ var (
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container",
Name: "container",
Image: "foo",
},
},
},
Expand Down Expand Up @@ -101,6 +102,46 @@ func TestHandle(t *testing.T) {
ProfileRef: v1alpha1.ProfileRef{
Kind: v1alpha1.ProfileBindingKindSeccompProfile,
},
Image: "foo",
},
},
},
}, nil)
mock.DecodePodReturns(testPod.DeepCopy(), nil)
mock.GetSeccompProfileReturns(&seccompprofileapi.SeccompProfile{
Status: seccompprofileapi.SeccompProfileStatus{
StatusBase: profilebasev1alpha1.StatusBase{
Status: secprofnodestatusv1alpha1.ProfileStateInstalled,
},
},
}, nil)
},
request: admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: func() []byte {
b, err := json.Marshal(testPod.DeepCopy())
require.Nil(t, err)
return b
}(),
},
},
},
assert: func(resp admission.Response) {
require.True(t, resp.AdmissionResponse.Allowed)
require.Len(t, resp.Patches, 1)
},
},
{ // success pod changed with * image
prepare: func(mock *bindingfakes.FakeImpl) {
mock.ListProfileBindingsReturns(&v1alpha1.ProfileBindingList{
Items: []v1alpha1.ProfileBinding{
{
Spec: v1alpha1.ProfileBindingSpec{
ProfileRef: v1alpha1.ProfileRef{
Kind: v1alpha1.ProfileBindingKindSeccompProfile,
},
Image: v1alpha1.SelectAllContainersImage,
},
},
},
Expand Down Expand Up @@ -139,6 +180,46 @@ func TestHandle(t *testing.T) {
ProfileRef: v1alpha1.ProfileRef{
Kind: v1alpha1.ProfileBindingKindSelinuxProfile,
},
Image: "foo",
},
},
},
}, nil)
mock.DecodePodReturns(testPod.DeepCopy(), nil)
mock.GetSelinuxProfileReturns(&selinuxprofileapi.SelinuxProfile{
Status: selinuxprofileapi.SelinuxProfileStatus{
StatusBase: profilebasev1alpha1.StatusBase{
Status: "Installed",
},
},
}, nil)
},
request: admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: func() []byte {
b, err := json.Marshal(testPod.DeepCopy())
require.Nil(t, err)
return b
}(),
},
},
},
assert: func(resp admission.Response) {
require.True(t, resp.AdmissionResponse.Allowed)
require.Len(t, resp.Patches, 1)
},
},
{ // selinux success pod changed with * image
prepare: func(mock *bindingfakes.FakeImpl) {
mock.ListProfileBindingsReturns(&v1alpha1.ProfileBindingList{
Items: []v1alpha1.ProfileBinding{
{
Spec: v1alpha1.ProfileBindingSpec{
ProfileRef: v1alpha1.ProfileRef{
Kind: v1alpha1.ProfileBindingKindSelinuxProfile,
},
Image: v1alpha1.SelectAllContainersImage,
},
},
},
Expand Down Expand Up @@ -280,6 +361,7 @@ func TestHandle(t *testing.T) {
ProfileRef: v1alpha1.ProfileRef{
Kind: v1alpha1.ProfileBindingKindSeccompProfile,
},
Image: "foo",
},
},
},
Expand Down Expand Up @@ -319,6 +401,7 @@ func TestHandle(t *testing.T) {
ProfileRef: v1alpha1.ProfileRef{
Kind: v1alpha1.ProfileBindingKindSeccompProfile,
},
Image: "foo",
},
},
},
Expand Down

0 comments on commit bc4735a

Please sign in to comment.