Remove apparmor dependency on pkg/kubelet/lifecycle

This commit is contained in:
Tim St. Clair 2016-08-16 15:44:50 -07:00
parent 413677110b
commit f94df59791
No known key found for this signature in database
GPG Key ID: 434D16BCEF479EAB
4 changed files with 57 additions and 55 deletions

View File

@ -73,7 +73,6 @@ import (
"k8s.io/kubernetes/pkg/kubelet/util/queue" "k8s.io/kubernetes/pkg/kubelet/util/queue"
"k8s.io/kubernetes/pkg/kubelet/volumemanager" "k8s.io/kubernetes/pkg/kubelet/volumemanager"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/security/apparmor"
"k8s.io/kubernetes/pkg/securitycontext" "k8s.io/kubernetes/pkg/securitycontext"
"k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/bandwidth" "k8s.io/kubernetes/pkg/util/bandwidth"
@ -601,7 +600,7 @@ func NewMainKubelet(
klet.AddPodSyncLoopHandler(activeDeadlineHandler) klet.AddPodSyncLoopHandler(activeDeadlineHandler)
klet.AddPodSyncHandler(activeDeadlineHandler) klet.AddPodSyncHandler(activeDeadlineHandler)
klet.AddPodAdmitHandler(apparmor.NewValidator(containerRuntime)) klet.AddPodAdmitHandler(lifecycle.NewAppArmorAdmitHandler(containerRuntime))
// apply functional Option's // apply functional Option's
for _, opt := range kubeOptions { for _, opt := range kubeOptions {

View File

@ -30,6 +30,7 @@ import (
kubetypes "k8s.io/kubernetes/pkg/kubelet/types" kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/kubelet/util/format" "k8s.io/kubernetes/pkg/kubelet/util/format"
"k8s.io/kubernetes/pkg/kubelet/util/ioutils" "k8s.io/kubernetes/pkg/kubelet/util/ioutils"
"k8s.io/kubernetes/pkg/security/apparmor"
"k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/intstr"
) )
@ -142,3 +143,25 @@ func getHttpRespBody(resp *http.Response) string {
} }
return "" return ""
} }
func NewAppArmorAdmitHandler(runtime string) PodAdmitHandler {
return &appArmorAdmitHandler{
Validator: apparmor.NewValidator(runtime),
}
}
type appArmorAdmitHandler struct {
apparmor.Validator
}
func (a *appArmorAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult {
err := a.Validate(attrs.Pod)
if err == nil {
return PodAdmitResult{Admit: true}
}
return PodAdmitResult{
Admit: false,
Reason: "AppArmor",
Message: fmt.Sprintf("Cannot enforce AppArmor: %v", err),
}
}

View File

@ -26,7 +26,6 @@ import (
"strings" "strings"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
) )
@ -34,11 +33,12 @@ import (
// Set to true if the wrong build tags are set (see validate_disabled.go). // Set to true if the wrong build tags are set (see validate_disabled.go).
var isDisabledBuild bool var isDisabledBuild bool
const ( // Interface for validating that a pod with with an AppArmor profile can be run by a Node.
rejectReason = "AppArmor" type Validator interface {
) Validate(pod *api.Pod) error
}
func NewValidator(runtime string) lifecycle.PodAdmitHandler { func NewValidator(runtime string) Validator {
if err := validateHost(runtime); err != nil { if err := validateHost(runtime); err != nil {
return &validator{validateHostErr: err} return &validator{validateHostErr: err}
} }
@ -58,21 +58,7 @@ type validator struct {
appArmorFS string appArmorFS string
} }
// TODO(timstclair): Refactor the PodAdmitInterface to return a (Admit, Reason Message) rather than func (v *validator) Validate(pod *api.Pod) error {
// the PodAdmitResult struct so that the interface can be implemented without importing lifecycle.
func (v *validator) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
err := v.validate(attrs.Pod)
if err == nil {
return lifecycle.PodAdmitResult{Admit: true}
}
return lifecycle.PodAdmitResult{
Admit: false,
Reason: rejectReason,
Message: fmt.Sprintf("Cannot enforce AppArmor: %v", err),
}
}
func (v *validator) validate(pod *api.Pod) error {
if !isRequired(pod) { if !isRequired(pod) {
return nil return nil
} }
@ -122,18 +108,27 @@ func validateHost(runtime string) error {
// Verify that the profile is valid and loaded. // Verify that the profile is valid and loaded.
func validateProfile(profile string, loadedProfiles map[string]bool) error { func validateProfile(profile string, loadedProfiles map[string]bool) error {
if err := ValidateProfileFormat(profile); err != nil {
return err
}
if strings.HasPrefix(profile, ProfileNamePrefix) {
profileName := strings.TrimPrefix(profile, ProfileNamePrefix)
if !loadedProfiles[profileName] {
return fmt.Errorf("profile %q is not loaded", profileName)
}
}
return nil
}
func ValidateProfileFormat(profile string) error {
if profile == "" || profile == ProfileRuntimeDefault { if profile == "" || profile == ProfileRuntimeDefault {
return nil return nil
} }
if !strings.HasPrefix(profile, ProfileNamePrefix) { if !strings.HasPrefix(profile, ProfileNamePrefix) {
return fmt.Errorf("invalid AppArmor profile name: %q", profile) return fmt.Errorf("invalid AppArmor profile name: %q", profile)
} }
profileName := strings.TrimPrefix(profile, ProfileNamePrefix)
if !loadedProfiles[profileName] {
return fmt.Errorf("profile %q is not loaded", profileName)
}
return nil return nil
} }

View File

@ -21,7 +21,6 @@ import (
"testing" "testing"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -37,7 +36,7 @@ func TestGetAppArmorFS(t *testing.T) {
assert.Equal(t, expectedPath, actualPath) assert.Equal(t, expectedPath, actualPath)
} }
func TestAdmitHost(t *testing.T) { func TestValidateHost(t *testing.T) {
// This test only passes on systems running AppArmor with the default configuration. // This test only passes on systems running AppArmor with the default configuration.
// The test should be manually run if modifying the getAppArmorFS function. // The test should be manually run if modifying the getAppArmorFS function.
t.Skip() t.Skip()
@ -46,7 +45,7 @@ func TestAdmitHost(t *testing.T) {
assert.Error(t, validateHost("rkt")) assert.Error(t, validateHost("rkt"))
} }
func TestAdmitProfile(t *testing.T) { func TestValidateProfile(t *testing.T) {
loadedProfiles := map[string]bool{ loadedProfiles := map[string]bool{
"docker-default": true, "docker-default": true,
"foo-bar": true, "foo-bar": true,
@ -79,7 +78,7 @@ func TestAdmitProfile(t *testing.T) {
} }
} }
func TestAdmitBadHost(t *testing.T) { func TestValidateBadHost(t *testing.T) {
hostErr := errors.New("expected host error") hostErr := errors.New("expected host error")
v := &validator{ v := &validator{
validateHostErr: hostErr, validateHostErr: hostErr,
@ -95,20 +94,16 @@ func TestAdmitBadHost(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
result := v.Admit(&lifecycle.PodAdmitAttributes{ err := v.Validate(getPodWithProfile(test.profile))
Pod: getPodWithProfile(test.profile),
})
if test.expectValid { if test.expectValid {
assert.True(t, result.Admit, "Pod with profile %q should be admitted", test.profile) assert.NoError(t, err, "Pod with profile %q should be valid", test.profile)
} else { } else {
assert.False(t, result.Admit, "Pod with profile %q should be rejected", test.profile) assert.Equal(t, hostErr, err, "Pod with profile %q should trigger a host validation error", test.profile)
assert.Equal(t, rejectReason, result.Reason, "Pod with profile %q", test.profile)
assert.Contains(t, result.Message, hostErr.Error(), "Pod with profile %q", test.profile)
} }
} }
} }
func TestAdmitValidHost(t *testing.T) { func TestValidateValidHost(t *testing.T) {
v := &validator{ v := &validator{
appArmorFS: "./testdata/", appArmorFS: "./testdata/",
} }
@ -128,15 +123,11 @@ func TestAdmitValidHost(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
result := v.Admit(&lifecycle.PodAdmitAttributes{ err := v.Validate(getPodWithProfile(test.profile))
Pod: getPodWithProfile(test.profile),
})
if test.expectValid { if test.expectValid {
assert.True(t, result.Admit, "Pod with profile %q should be admitted", test.profile) assert.NoError(t, err, "Pod with profile %q should be valid", test.profile)
} else { } else {
assert.False(t, result.Admit, "Pod with profile %q should be rejected", test.profile) assert.Error(t, err, "Pod with profile %q should trigger a validation error", test.profile)
assert.Equal(t, rejectReason, result.Reason, "Pod with profile %q", test.profile)
assert.NotEmpty(t, result.Message, "Pod with profile %q", test.profile)
} }
} }
@ -160,16 +151,10 @@ func TestAdmitValidHost(t *testing.T) {
}, },
}, },
} }
assert.True(t, v.Admit(&lifecycle.PodAdmitAttributes{Pod: pod}).Admit, assert.NoError(t, v.Validate(pod), "Multi-container pod should validate")
"Multi-container pod should be admitted")
for k, val := range pod.Annotations { for k, val := range pod.Annotations {
pod.Annotations[k] = val + "-bad" pod.Annotations[k] = val + "-bad"
assert.Error(t, v.Validate(pod), "Multi-container pod with invalid profile %s:%s", k, pod.Annotations[k])
result := v.Admit(&lifecycle.PodAdmitAttributes{Pod: pod})
assert.False(t, result.Admit, "Multi-container pod with invalid profile should be rejected")
assert.Equal(t, rejectReason, result.Reason, "Multi-container pod with invalid profile")
assert.NotEmpty(t, result.Message, "Multi-container pod with invalid profile")
pod.Annotations[k] = val // Restore. pod.Annotations[k] = val // Restore.
} }
} }