Skip to content

Commit 573ff20

Browse files
author
OpenShift Bot
authored
Merge pull request #11075 from sdminonne/SCC_check_API_REST
Merged by openshift-bot
2 parents 23b6f26 + 3c6fea4 commit 573ff20

File tree

13 files changed

+2111
-28
lines changed

13 files changed

+2111
-28
lines changed

api/swagger-spec/oapi-v1.json

+485
Large diffs are not rendered by default.

api/swagger-spec/openshift-openapi-spec.json

+445
Large diffs are not rendered by default.

pkg/cmd/server/bootstrappolicy/policy.go

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
quotaapi "github.com/openshift/origin/pkg/quota/api"
2424
routeapi "github.com/openshift/origin/pkg/route/api"
2525
sdnapi "github.com/openshift/origin/pkg/sdn/api"
26+
securityapi "github.com/openshift/origin/pkg/security/api"
2627
templateapi "github.com/openshift/origin/pkg/template/api"
2728
userapi "github.com/openshift/origin/pkg/user/api"
2829
)
@@ -38,6 +39,7 @@ var (
3839
certificatesGroup = certificates.GroupName
3940
extensionsGroup = extensions.GroupName
4041
policyGroup = policy.GroupName
42+
securityGroup = securityapi.GroupName
4143
storageGroup = storage.GroupName
4244
authzGroup = authorizationapi.GroupName
4345
buildGroup = buildapi.GroupName
@@ -165,6 +167,8 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
165167
authorizationapi.NewRule("create").Groups(authzGroup).Resources("localresourceaccessreviews", "localsubjectaccessreviews", "resourceaccessreviews",
166168
"selfsubjectrulesreviews", "subjectaccessreviews").RuleOrDie(),
167169
authorizationapi.NewRule("create").Groups("authentication.k8s.io").Resources("tokenreviews").RuleOrDie(),
170+
// permissions to check PSP, these creates are non-mutating
171+
authorizationapi.NewRule("create").Groups(securityGroup).Resources("podsecuritypolicysubjectreviews", "podsecuritypolicyselfsubjectreviews", "podsecuritypolicyreviews").RuleOrDie(),
168172
// Allow read access to node metrics
169173
authorizationapi.NewRule("get").Groups(kapiGroup).Resources(authorizationapi.NodeMetricsResource, authorizationapi.NodeSpecResource).RuleOrDie(),
170174
// Allow read access to stats
@@ -238,6 +242,7 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
238242

239243
authorizationapi.NewRule(readWrite...).Groups(authzGroup).Resources("roles", "rolebindings").RuleOrDie(),
240244
authorizationapi.NewRule("create").Groups(authzGroup).Resources("localresourceaccessreviews", "localsubjectaccessreviews").RuleOrDie(),
245+
authorizationapi.NewRule("create").Groups(securityGroup).Resources("podsecuritypolicysubjectreviews", "podsecuritypolicyselfsubjectreviews", "podsecuritypolicyreviews").RuleOrDie(),
241246
authorizationapi.NewRule(read...).Groups(authzGroup).Resources("policies", "policybindings").RuleOrDie(),
242247

243248
authorizationapi.NewRule(readWrite...).Groups(buildGroup).Resources("builds", "buildconfigs", "buildconfigs/webhooks").RuleOrDie(),

pkg/cmd/server/origin/master.go

+13
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"k8s.io/kubernetes/pkg/apiserver"
2828
"k8s.io/kubernetes/pkg/client/restclient"
2929
kclient "k8s.io/kubernetes/pkg/client/unversioned"
30+
clientadapter "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
3031
"k8s.io/kubernetes/pkg/genericapiserver"
3132
"k8s.io/kubernetes/pkg/genericapiserver/openapi"
3233
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
@@ -116,6 +117,10 @@ import (
116117
"github.com/openshift/origin/pkg/authorization/registry/subjectaccessreview"
117118
configapi "github.com/openshift/origin/pkg/cmd/server/api"
118119
routeplugin "github.com/openshift/origin/pkg/route/allocation/simple"
120+
"github.com/openshift/origin/pkg/security/registry/podsecuritypolicyreview"
121+
"github.com/openshift/origin/pkg/security/registry/podsecuritypolicyselfsubjectreview"
122+
"github.com/openshift/origin/pkg/security/registry/podsecuritypolicysubjectreview"
123+
oscc "github.com/openshift/origin/pkg/security/scc"
119124
)
120125

121126
const (
@@ -549,6 +554,10 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage {
549554
resourceAccessReviewRegistry := resourceaccessreview.NewRegistry(resourceAccessReviewStorage)
550555
localResourceAccessReviewStorage := localresourceaccessreview.NewREST(resourceAccessReviewRegistry)
551556

557+
podSecurityPolicyReviewStorage := podsecuritypolicyreview.NewREST(oscc.NewDefaultSCCMatcher(c.Informers.SecurityContextConstraints().Lister()), clientadapter.FromUnversionedClient(c.PrivilegedLoopbackKubernetesClient))
558+
podSecurityPolicySubjectStorage := podsecuritypolicysubjectreview.NewREST(oscc.NewDefaultSCCMatcher(c.Informers.SecurityContextConstraints().Lister()), clientadapter.FromUnversionedClient(c.PrivilegedLoopbackKubernetesClient))
559+
podSecurityPolicySelfSubjectReviewStorage := podsecuritypolicyselfsubjectreview.NewREST(oscc.NewDefaultSCCMatcher(c.Informers.SecurityContextConstraints().Lister()), clientadapter.FromUnversionedClient(c.PrivilegedLoopbackKubernetesClient))
560+
552561
imageStorage, err := imageetcd.NewREST(c.RESTOptionsGetter)
553562
checkStorageErr(err)
554563
imageRegistry := image.NewRegistry(imageStorage)
@@ -692,6 +701,10 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage {
692701
"localResourceAccessReviews": localResourceAccessReviewStorage,
693702
"selfSubjectRulesReviews": selfSubjectRulesReviewStorage,
694703

704+
"podSecurityPolicyReviews": podSecurityPolicyReviewStorage,
705+
"podSecurityPolicySubjectReviews": podSecurityPolicySubjectStorage,
706+
"podSecurityPolicySelfSubjectReviews": podSecurityPolicySelfSubjectReviewStorage,
707+
695708
"policies": policyStorage,
696709
"policyBindings": policyBindingStorage,
697710
"roles": roleStorage,

pkg/security/admission/admission_test.go

+7-28
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
oscache "github.com/openshift/origin/pkg/client/cache"
1818
allocator "github.com/openshift/origin/pkg/security"
19+
admissiontesting "github.com/openshift/origin/pkg/security/admission/testing"
1920
oscc "github.com/openshift/origin/pkg/security/scc"
2021
)
2122

@@ -129,8 +130,8 @@ func TestAdmitCaps(t *testing.T) {
129130
}
130131

131132
func testSCCAdmit(testCaseName string, sccs []*kapi.SecurityContextConstraints, pod *kapi.Pod, shouldPass bool, t *testing.T) {
132-
namespace := createNamespaceForTest()
133-
serviceAccount := createSAForTest()
133+
namespace := admissiontesting.CreateNamespaceForTest()
134+
serviceAccount := admissiontesting.CreateSAForTest()
134135
tc := clientsetfake.NewSimpleClientset(namespace, serviceAccount)
135136
cache := &oscache.IndexerToSecurityContextConstraintsLister{
136137
Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc,
@@ -155,8 +156,8 @@ func testSCCAdmit(testCaseName string, sccs []*kapi.SecurityContextConstraints,
155156

156157
func TestAdmit(t *testing.T) {
157158
// create the annotated namespace and add it to the fake client
158-
namespace := createNamespaceForTest()
159-
serviceAccount := createSAForTest()
159+
namespace := admissiontesting.CreateNamespaceForTest()
160+
serviceAccount := admissiontesting.CreateSAForTest()
160161

161162
// used for cases where things are preallocated
162163
defaultGroup := int64(2)
@@ -898,8 +899,8 @@ func TestAdmitWithPrioritizedSCC(t *testing.T) {
898899
// SCCs and ensure that they come out with the right annotation. This means admission
899900
// is using the sort strategy we expect.
900901

901-
namespace := createNamespaceForTest()
902-
serviceAccount := createSAForTest()
902+
namespace := admissiontesting.CreateNamespaceForTest()
903+
serviceAccount := admissiontesting.CreateSAForTest()
903904
serviceAccount.Namespace = namespace.Name
904905
tc := clientsetfake.NewSimpleClientset(namespace, serviceAccount)
905906

@@ -1112,28 +1113,6 @@ func restrictiveSCC() *kapi.SecurityContextConstraints {
11121113
}
11131114
}
11141115

1115-
func createNamespaceForTest() *kapi.Namespace {
1116-
return &kapi.Namespace{
1117-
ObjectMeta: kapi.ObjectMeta{
1118-
Name: "default",
1119-
Annotations: map[string]string{
1120-
allocator.UIDRangeAnnotation: "1/3",
1121-
allocator.MCSAnnotation: "s0:c1,c0",
1122-
allocator.SupplementalGroupsAnnotation: "2/3",
1123-
},
1124-
},
1125-
}
1126-
}
1127-
1128-
func createSAForTest() *kapi.ServiceAccount {
1129-
return &kapi.ServiceAccount{
1130-
ObjectMeta: kapi.ObjectMeta{
1131-
Name: "default",
1132-
Namespace: "default",
1133-
},
1134-
}
1135-
}
1136-
11371116
// goodPod is empty and should not be used directly for testing since we're providing
11381117
// two different SCCs. Since no values are specified it would be allowed to match any
11391118
// SCC when defaults are filled in.
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package testing
2+
3+
import (
4+
kapi "k8s.io/kubernetes/pkg/api"
5+
6+
allocator "github.com/openshift/origin/pkg/security"
7+
)
8+
9+
// CreateSAForTest Build and Initializes a ServiceAccount for tests
10+
func CreateSAForTest() *kapi.ServiceAccount {
11+
return &kapi.ServiceAccount{
12+
ObjectMeta: kapi.ObjectMeta{
13+
Name: "default",
14+
Namespace: "default",
15+
},
16+
}
17+
}
18+
19+
// CreateNamespaceForTest builds and initializes a Namespaces for tests
20+
func CreateNamespaceForTest() *kapi.Namespace {
21+
return &kapi.Namespace{
22+
ObjectMeta: kapi.ObjectMeta{
23+
Name: "default",
24+
Annotations: map[string]string{
25+
allocator.UIDRangeAnnotation: "1/3",
26+
allocator.MCSAnnotation: "s0:c1,c0",
27+
allocator.SupplementalGroupsAnnotation: "2/3",
28+
},
29+
},
30+
}
31+
}
32+
33+
// UserScc creates a SCC for a given user name
34+
func UserScc(user string) *kapi.SecurityContextConstraints {
35+
var uid int64 = 9999
36+
fsGroup := int64(1)
37+
return &kapi.SecurityContextConstraints{
38+
ObjectMeta: kapi.ObjectMeta{
39+
SelfLink: "/api/version/securitycontextconstraints/" + user,
40+
Name: user,
41+
},
42+
Users: []string{user},
43+
SELinuxContext: kapi.SELinuxContextStrategyOptions{
44+
Type: kapi.SELinuxStrategyRunAsAny,
45+
},
46+
RunAsUser: kapi.RunAsUserStrategyOptions{
47+
Type: kapi.RunAsUserStrategyMustRunAs,
48+
UID: &uid,
49+
},
50+
FSGroup: kapi.FSGroupStrategyOptions{
51+
Type: kapi.FSGroupStrategyMustRunAs,
52+
Ranges: []kapi.IDRange{
53+
{Min: fsGroup, Max: fsGroup},
54+
},
55+
},
56+
SupplementalGroups: kapi.SupplementalGroupsStrategyOptions{
57+
Type: kapi.SupplementalGroupsStrategyRunAsAny,
58+
},
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package podsecuritypolicyreview
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
7+
"github.com/golang/glog"
8+
9+
kapi "k8s.io/kubernetes/pkg/api"
10+
kapierrors "k8s.io/kubernetes/pkg/api/errors"
11+
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
12+
"k8s.io/kubernetes/pkg/runtime"
13+
kscc "k8s.io/kubernetes/pkg/securitycontextconstraints"
14+
"k8s.io/kubernetes/pkg/serviceaccount"
15+
kerrors "k8s.io/kubernetes/pkg/util/errors"
16+
17+
securityapi "github.com/openshift/origin/pkg/security/api"
18+
securityvalidation "github.com/openshift/origin/pkg/security/api/validation"
19+
"github.com/openshift/origin/pkg/security/registry/podsecuritypolicysubjectreview"
20+
oscc "github.com/openshift/origin/pkg/security/scc"
21+
)
22+
23+
// REST implements the RESTStorage interface in terms of an Registry.
24+
type REST struct {
25+
sccMatcher oscc.SCCMatcher
26+
client clientset.Interface
27+
}
28+
29+
// NewREST creates a new REST for policies..
30+
func NewREST(m oscc.SCCMatcher, c clientset.Interface) *REST {
31+
return &REST{sccMatcher: m, client: c}
32+
}
33+
34+
// New creates a new PodSecurityPolicyReview object
35+
func (r *REST) New() runtime.Object {
36+
return &securityapi.PodSecurityPolicyReview{}
37+
}
38+
39+
// Create registers a given new PodSecurityPolicyReview instance to r.registry.
40+
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
41+
pspr, ok := obj.(*securityapi.PodSecurityPolicyReview)
42+
if !ok {
43+
return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a PodSecurityPolicyReview: %#v", obj))
44+
}
45+
if errs := securityvalidation.ValidatePodSecurityPolicyReview(pspr); len(errs) > 0 {
46+
return nil, kapierrors.NewInvalid(kapi.Kind("PodSecurityPolicyReview"), "", errs)
47+
}
48+
ns, ok := kapi.NamespaceFrom(ctx)
49+
if !ok {
50+
return nil, kapierrors.NewBadRequest("namespace parameter required.")
51+
}
52+
serviceAccounts, err := getServiceAccounts(pspr.Spec, r.client, ns)
53+
if err != nil {
54+
return nil, kapierrors.NewBadRequest(err.Error())
55+
}
56+
57+
if len(serviceAccounts) == 0 {
58+
glog.Errorf("No service accounts for namespace %s", ns)
59+
return nil, kapierrors.NewBadRequest(fmt.Sprintf("unable to find ServiceAccount for namespace: %s", ns))
60+
}
61+
62+
errs := []error{}
63+
newStatus := securityapi.PodSecurityPolicyReviewStatus{}
64+
for _, sa := range serviceAccounts {
65+
userInfo := serviceaccount.UserInfo(ns, sa.Name, "")
66+
saConstraints, err := r.sccMatcher.FindApplicableSCCs(userInfo)
67+
if err != nil {
68+
errs = append(errs, fmt.Errorf("unable to find SecurityContextConstraints for ServiceAccount %s: %v", sa.Name, err))
69+
continue
70+
}
71+
oscc.DeduplicateSecurityContextConstraints(saConstraints)
72+
sort.Sort(oscc.ByPriority(saConstraints))
73+
var namespace *kapi.Namespace
74+
for _, constraint := range saConstraints {
75+
var (
76+
provider kscc.SecurityContextConstraintsProvider
77+
err error
78+
)
79+
pspsrs := securityapi.PodSecurityPolicySubjectReviewStatus{}
80+
if provider, namespace, err = oscc.CreateProviderFromConstraint(ns, namespace, constraint, r.client); err != nil {
81+
errs = append(errs, fmt.Errorf("unable to create provider for service account %s: %v", sa.Name, err))
82+
continue
83+
}
84+
_, err = podsecuritypolicysubjectreview.FillPodSecurityPolicySubjectReviewStatus(&pspsrs, provider, pspr.Spec.Template.Spec, constraint)
85+
if err != nil {
86+
glog.Errorf("unable to fill PodSecurityPolicyReviewStatus from constraint %v", err)
87+
continue
88+
}
89+
sapsprs := securityapi.ServiceAccountPodSecurityPolicyReviewStatus{pspsrs, sa.Name}
90+
newStatus.AllowedServiceAccounts = append(newStatus.AllowedServiceAccounts, sapsprs)
91+
}
92+
}
93+
if len(errs) > 0 {
94+
return nil, kapierrors.NewBadRequest(fmt.Sprintf("%s", kerrors.NewAggregate(errs)))
95+
}
96+
pspr.Status = newStatus
97+
return pspr, nil
98+
}
99+
100+
func getServiceAccounts(psprSpec securityapi.PodSecurityPolicyReviewSpec, client clientset.Interface, namespace string) ([]*kapi.ServiceAccount, error) {
101+
serviceAccounts := []*kapi.ServiceAccount{}
102+
// TODO: express 'all service accounts'
103+
//if serviceAccountList, err := client.Core().ServiceAccounts(namespace).List(kapi.ListOptions{}); err == nil {
104+
// serviceAccounts = serviceAccountList.Items
105+
// return serviceAccounts, fmt.Errorf("unable to retrieve service accounts: %v", err)
106+
//}
107+
108+
if len(psprSpec.ServiceAccountNames) > 0 {
109+
errs := []error{}
110+
for _, saName := range psprSpec.ServiceAccountNames {
111+
// TODO: use cache as soon ServiceAccount informer is ready
112+
sa, err := client.Core().ServiceAccounts(namespace).Get(saName)
113+
if err != nil {
114+
errs = append(errs, fmt.Errorf("unable to retrieve ServiceAccount %s: %v", saName, err))
115+
}
116+
serviceAccounts = append(serviceAccounts, sa)
117+
}
118+
return serviceAccounts, kerrors.NewAggregate(errs)
119+
}
120+
saName := "default"
121+
if len(psprSpec.Template.Spec.ServiceAccountName) > 0 {
122+
saName = psprSpec.Template.Spec.ServiceAccountName
123+
}
124+
sa, err := client.Core().ServiceAccounts(namespace).Get(saName)
125+
if err != nil {
126+
return serviceAccounts, fmt.Errorf("unable to retrieve ServiceAccount %s: %v", saName, err)
127+
}
128+
serviceAccounts = append(serviceAccounts, sa)
129+
return serviceAccounts, nil
130+
}

0 commit comments

Comments
 (0)