Example #1
0
func init() {
	admission.RegisterPlugin(api.PluginName, func(client clientset.Interface, input io.Reader) (admission.Interface, error) {
		obj, err := configlatest.ReadYAML(input)
		if err != nil {
			return nil, err
		}
		if obj == nil {
			return nil, nil
		}
		config, ok := obj.(*api.ImagePolicyConfig)
		if !ok {
			return nil, fmt.Errorf("unexpected config object: %#v", obj)
		}
		if errs := validation.Validate(config); len(errs) > 0 {
			return nil, errs.ToAggregate()
		}
		glog.V(5).Infof("%s admission controller loaded with config: %#v", api.PluginName, config)
		return newImagePolicyPlugin(client, config)
	})
}
Example #2
0
func TestDefaultPolicy(t *testing.T) {
	input, err := os.Open("api/v1/default-policy.yaml")
	if err != nil {
		t.Fatal(err)
	}
	obj, err := configlatest.ReadYAML(input)
	if err != nil {
		t.Fatal(err)
	}
	if obj == nil {
		t.Fatal(obj)
	}
	config, ok := obj.(*api.ImagePolicyConfig)
	if !ok {
		t.Fatal(config)
	}
	if errs := validation.Validate(config); len(errs) > 0 {
		t.Fatal(errs.ToAggregate())
	}

	plugin, err := newImagePolicyPlugin(nil, config)
	if err != nil {
		t.Fatal(err)
	}

	goodImage := &imageapi.Image{
		ObjectMeta:           kapi.ObjectMeta{Name: "sha256:good"},
		DockerImageReference: "integrated.registry/goodns/goodimage:good",
	}
	badImage := &imageapi.Image{
		ObjectMeta: kapi.ObjectMeta{
			Name: "sha256:bad",
			Annotations: map[string]string{
				"images.openshift.io/deny-execution": "true",
			},
		},
		DockerImageReference: "integrated.registry/badns/badimage:bad",
	}

	client := testclient.NewSimpleFake(
		goodImage,
		badImage,

		// respond to image stream tag in this order:
		&unversioned.Status{
			Reason: unversioned.StatusReasonNotFound,
			Code:   404,
			Details: &unversioned.StatusDetails{
				Kind: "ImageStreamTag",
			},
		},
		&imageapi.ImageStreamTag{
			ObjectMeta: kapi.ObjectMeta{Name: "mysql:goodtag", Namespace: "repo"},
			Image:      *goodImage,
		},
		&imageapi.ImageStreamTag{
			ObjectMeta: kapi.ObjectMeta{Name: "mysql:badtag", Namespace: "repo"},
			Image:      *badImage,
		},
	)

	store := setDefaultCache(plugin)
	plugin.SetOpenshiftClient(client)
	plugin.SetDefaultRegistryFunc(func() (string, bool) {
		return "integrated.registry", true
	})
	if err := plugin.Validate(); err != nil {
		t.Fatal(err)
	}

	originalNowFn := now
	defer (func() { now = originalNowFn })()
	now = func() time.Time { return time.Unix(1, 0) }

	// should allow a non-integrated image
	attrs := admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql:latest"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err != nil {
		t.Fatal(err)
	}

	// should resolve the non-integrated image and allow it
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:good"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err != nil {
		t.Fatal(err)
	}

	// should resolve the integrated image by digest and allow it
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "integrated.registry/repo/mysql@sha256:good"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err != nil {
		t.Fatal(err)
	}

	// should attempt resolve the integrated image by tag and fail because tag not found
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "integrated.registry/repo/mysql:missingtag"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err != nil {
		t.Fatal(err)
	}

	// should attempt resolve the integrated image by tag and allow it
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "integrated.registry/repo/mysql:goodtag"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err != nil {
		t.Fatal(err)
	}

	// should attempt resolve the integrated image by tag and forbid it
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "integrated.registry/repo/mysql:badtag"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	t.Logf("%#v", plugin.accepter)
	if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) {
		t.Fatal(err)
	}

	// should reject the non-integrated image due to the annotation
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:bad"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) {
		t.Fatal(err)
	}

	// should reject the non-integrated image due to the annotation on an init container
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{InitContainers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:bad"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) {
		t.Fatal(err)
	}

	// should reject the non-integrated image due to the annotation for a build
	attrs = admission.NewAttributesRecord(
		&buildapi.Build{Spec: buildapi.BuildSpec{CommonSpec: buildapi.CommonSpec{Source: buildapi.BuildSource{Images: []buildapi.ImageSource{
			{From: kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"}},
		}}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Build"},
		"default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "builds"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) {
		t.Fatal(err)
	}
	attrs = admission.NewAttributesRecord(
		&buildapi.Build{Spec: buildapi.BuildSpec{CommonSpec: buildapi.CommonSpec{Strategy: buildapi.BuildStrategy{DockerStrategy: &buildapi.DockerBuildStrategy{
			From: &kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"},
		}}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Build"},
		"default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "builds"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) {
		t.Fatal(err)
	}
	attrs = admission.NewAttributesRecord(
		&buildapi.Build{Spec: buildapi.BuildSpec{CommonSpec: buildapi.CommonSpec{Strategy: buildapi.BuildStrategy{SourceStrategy: &buildapi.SourceBuildStrategy{
			From: kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"},
		}}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Build"},
		"default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "builds"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) {
		t.Fatal(err)
	}
	attrs = admission.NewAttributesRecord(
		&buildapi.Build{Spec: buildapi.BuildSpec{CommonSpec: buildapi.CommonSpec{Strategy: buildapi.BuildStrategy{CustomStrategy: &buildapi.CustomBuildStrategy{
			From: kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"},
		}}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Build"},
		"default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "builds"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) {
		t.Fatal(err)
	}

	// should allow the non-integrated image due to the annotation for a build config because it's not in the list, even though it has
	// a valid spec
	attrs = admission.NewAttributesRecord(
		&buildapi.BuildConfig{Spec: buildapi.BuildConfigSpec{CommonSpec: buildapi.CommonSpec{Source: buildapi.BuildSource{Images: []buildapi.ImageSource{
			{From: kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"}},
		}}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "BuildConfig"},
		"default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "buildconfigs"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err != nil {
		t.Fatal(err)
	}

	// should hit the cache on the previously good image and continue to allow it (the copy in cache was previously safe)
	goodImage.Annotations = map[string]string{"images.openshift.io/deny-execution": "true"}
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:good"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err != nil {
		t.Fatal(err)
	}

	// moving 2 minutes in the future should bypass the cache and deny the image
	now = func() time.Time { return time.Unix(1, 0).Add(2 * time.Minute) }
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:good"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) {
		t.Fatal(err)
	}

	// setting a namespace annotation should allow the rule to be skipped immediately
	store.Add(&kapi.Namespace{
		ObjectMeta: kapi.ObjectMeta{
			Namespace: "",
			Name:      "default",
			Annotations: map[string]string{
				api.IgnorePolicyRulesAnnotation: "execution-denied",
			},
		},
	})
	attrs = admission.NewAttributesRecord(
		&kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:good"}}}},
		nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"},
		"default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"},
		"", admission.Create, nil,
	)
	if err := plugin.Admit(attrs); err != nil {
		t.Fatal(err)
	}
}