package policy import ( "errors" "fmt" "io" "github.com/spf13/cobra" kapi "k8s.io/kubernetes/pkg/api" kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned" adapter "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" authorizationapi "github.com/openshift/origin/pkg/authorization/api" "github.com/openshift/origin/pkg/cmd/templates" "github.com/openshift/origin/pkg/cmd/util/clientcmd" uservalidation "github.com/openshift/origin/pkg/user/api/validation" ) const ( AddSCCToGroupRecommendedName = "add-scc-to-group" AddSCCToUserRecommendedName = "add-scc-to-user" RemoveSCCFromGroupRecommendedName = "remove-scc-from-group" RemoveSCCFromUserRecommendedName = "remove-scc-from-user" ) var ( addSCCToUserExample = templates.Examples(` # Add the 'restricted' security context contraint to user1 and user2 %[1]s restricted user1 user2 # Add the 'privileged' security context contraint to the service account serviceaccount1 in the current namespace %[1]s privileged -z serviceaccount1`) ) type SCCModificationOptions struct { SCCName string SCCInterface kcoreclient.SecurityContextConstraintsGetter DefaultSubjectNamespace string Subjects []kapi.ObjectReference } func NewCmdAddSCCToGroup(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command { options := &SCCModificationOptions{} cmd := &cobra.Command{ Use: name + " SCC GROUP [GROUP ...]", Short: "Add groups to a security context constraint", Long: `Add groups to a security context constraint`, Run: func(cmd *cobra.Command, args []string) { if err := options.CompleteGroups(f, args); err != nil { kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error())) } if err := options.AddSCC(); err != nil { kcmdutil.CheckErr(err) } }, } return cmd } func NewCmdAddSCCToUser(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command { options := &SCCModificationOptions{} saNames := []string{} cmd := &cobra.Command{ Use: name + " SCC (USER | -z SERVICEACCOUNT) [USER ...]", Short: "Add users or serviceaccount to a security context constraint", Long: `Add users or serviceaccount to a security context constraint`, Example: fmt.Sprintf(addSCCToUserExample, fullName), Run: func(cmd *cobra.Command, args []string) { if err := options.CompleteUsers(f, args, saNames); err != nil { kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error())) } if err := options.AddSCC(); err != nil { kcmdutil.CheckErr(err) } }, } cmd.Flags().StringSliceVarP(&saNames, "serviceaccount", "z", saNames, "service account in the current namespace to use as a user") return cmd } func NewCmdRemoveSCCFromGroup(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command { options := &SCCModificationOptions{} cmd := &cobra.Command{ Use: name + " SCC GROUP [GROUP ...]", Short: "Remove group from scc", Long: `Remove group from scc`, Run: func(cmd *cobra.Command, args []string) { if err := options.CompleteGroups(f, args); err != nil { kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error())) } if err := options.RemoveSCC(); err != nil { kcmdutil.CheckErr(err) } }, } return cmd } func NewCmdRemoveSCCFromUser(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command { options := &SCCModificationOptions{} saNames := []string{} cmd := &cobra.Command{ Use: name + " SCC USER [USER ...]", Short: "Remove user from scc", Long: `Remove user from scc`, Run: func(cmd *cobra.Command, args []string) { if err := options.CompleteUsers(f, args, saNames); err != nil { kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error())) } if err := options.RemoveSCC(); err != nil { kcmdutil.CheckErr(err) } }, } cmd.Flags().StringSliceVarP(&saNames, "serviceaccount", "z", saNames, "service account in the current namespace to use as a user") return cmd } func (o *SCCModificationOptions) CompleteUsers(f *clientcmd.Factory, args []string, saNames []string) error { if len(args) < 1 { return errors.New("you must specify a scc") } o.SCCName = args[0] o.Subjects = authorizationapi.BuildSubjects(args[1:], []string{}, uservalidation.ValidateUserName, uservalidation.ValidateGroupName) if (len(o.Subjects) == 0) && (len(saNames) == 0) { return errors.New("you must specify at least one user or service account") } _, kc, _, err := f.Clients() if err != nil { return err } o.SCCInterface = adapter.FromUnversionedClient(kc).Core() o.DefaultSubjectNamespace, _, err = f.DefaultNamespace() if err != nil { return err } for _, sa := range saNames { o.Subjects = append(o.Subjects, kapi.ObjectReference{Namespace: o.DefaultSubjectNamespace, Name: sa, Kind: "ServiceAccount"}) } return nil } func (o *SCCModificationOptions) CompleteGroups(f *clientcmd.Factory, args []string) error { if len(args) < 2 { return errors.New("you must specify at least two arguments: <scc> <group> [group]...") } o.SCCName = args[0] o.Subjects = authorizationapi.BuildSubjects([]string{}, args[1:], uservalidation.ValidateUserName, uservalidation.ValidateGroupName) _, kc, _, err := f.Clients() if err != nil { return err } o.SCCInterface = adapter.FromUnversionedClient(kc).Core() o.DefaultSubjectNamespace, _, err = f.DefaultNamespace() if err != nil { return err } return nil } func (o *SCCModificationOptions) AddSCC() error { scc, err := o.SCCInterface.SecurityContextConstraints().Get(o.SCCName) if err != nil { return err } users, groups := authorizationapi.StringSubjectsFor(o.DefaultSubjectNamespace, o.Subjects) usersToAdd, _ := diff(users, scc.Users) groupsToAdd, _ := diff(groups, scc.Groups) scc.Users = append(scc.Users, usersToAdd...) scc.Groups = append(scc.Groups, groupsToAdd...) _, err = o.SCCInterface.SecurityContextConstraints().Update(scc) if err != nil { return err } return nil } func (o *SCCModificationOptions) RemoveSCC() error { scc, err := o.SCCInterface.SecurityContextConstraints().Get(o.SCCName) if err != nil { return err } users, groups := authorizationapi.StringSubjectsFor(o.DefaultSubjectNamespace, o.Subjects) _, remainingUsers := diff(users, scc.Users) _, remainingGroups := diff(groups, scc.Groups) scc.Users = remainingUsers scc.Groups = remainingGroups _, err = o.SCCInterface.SecurityContextConstraints().Update(scc) if err != nil { return err } return nil } func diff(lhsSlice, rhsSlice []string) (lhsOnly []string, rhsOnly []string) { return singleDiff(lhsSlice, rhsSlice), singleDiff(rhsSlice, lhsSlice) } func singleDiff(lhsSlice, rhsSlice []string) (lhsOnly []string) { for _, lhs := range lhsSlice { found := false for _, rhs := range rhsSlice { if lhs == rhs { found = true break } } if !found { lhsOnly = append(lhsOnly, lhs) } } return lhsOnly }