Skip to content

Commit

Permalink
Trident Now Uses its Own Security Context Constraint Instead of the B…
Browse files Browse the repository at this point in the history
…uilt-In Ones
  • Loading branch information
bpresnel authored Apr 21, 2020
1 parent 469a450 commit 83e2f26
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 126 deletions.
109 changes: 64 additions & 45 deletions cli/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -1228,26 +1228,21 @@ func createRBACObjects() (returnError error) {
log.WithFields(logFields).Info("Created cluster role binding.")

// If OpenShift, add Trident to security context constraint(s)
user := "trident"
labelVal := TridentLegacyLabelValue
if csi {
user = "trident-csi"
labelVal = TridentCSILabelValue
}
if client.Flavor() == k8sclient.FlavorOpenShift {
if csi {
if returnError = client.AddTridentUserToOpenShiftSCC("trident-csi", "privileged"); returnError != nil {
returnError = fmt.Errorf("could not modify security context constraint; %v", returnError)
return
}
log.WithFields(log.Fields{
"scc": "privileged",
"user": "trident-csi",
}).Info("Added security context constraint user.")
} else {
if returnError = client.AddTridentUserToOpenShiftSCC("trident", "anyuid"); returnError != nil {
returnError = fmt.Errorf("could not modify security context constraint; %v", returnError)
return
}
log.WithFields(log.Fields{
"scc": "anyuid",
"user": "trident",
}).Info("Added security context constraint user.")
if returnError = CreateOpenShiftTridentSCC(user, labelVal); returnError != nil {
returnError = fmt.Errorf("could not create security context constraint; %v", returnError)
return
}
log.WithFields(log.Fields{
"scc": "trident",
"user": user,
}).Info("Created Trident's security context constraint.")
}

return
Expand Down Expand Up @@ -1290,28 +1285,20 @@ func removeRBACObjects(logLevel log.Level) (anyErrors bool) {
logFunc(log.Fields{})("Deleted service account.")
}

// If OpenShift, remove Trident from security context constraint(s)
// If OpenShift, delete Trident's security context constraint
if client.Flavor() == k8sclient.FlavorOpenShift {
user := "trident"
if csi {
if err := client.RemoveTridentUserFromOpenShiftSCC("trident-csi", "privileged"); err != nil {
log.WithField("error", err).Warning("Could not modify security context constraint.")
anyErrors = true
} else {
logFunc(log.Fields{
"scc": "privileged",
"user": "trident-csi",
})("Removed security context constraint user.")
}
user = "trident-csi"
}
if err := DeleteOpenShiftTridentSCC(user); err != nil {
log.WithField("error", err).Warning("Could not delete security context constraint.")
anyErrors = true
} else {
if err := client.RemoveTridentUserFromOpenShiftSCC("trident", "anyuid"); err != nil {
log.WithField("error", err).Warning("Could not modify security context constraint.")
anyErrors = true
} else {
logFunc(log.Fields{
"scc": "anyuid",
"user": "trident",
})("Removed security context constraint user.")
}
logFunc(log.Fields{
"scc": "trident",
"user": user,
})("Deleted Trident's security context constraint.")
}
}

Expand Down Expand Up @@ -1898,6 +1885,38 @@ func createObjectsByYAML(objectName string, objectYAML string, errorMessage stri
return nil
}

// CreateOpenShiftTridentSCC creates an SCC solely for use with the trident user. This only works for OpenShift.
func CreateOpenShiftTridentSCC(user, appLabelVal string) error {
// Remove trident user from built-in SCC from previous installation
if user == "trident-installer" {
_ = client.RemoveTridentUserFromOpenShiftSCC("trident-installer", "privileged")
} else if strings.Contains(appLabelValue, "csi") {
_ = client.RemoveTridentUserFromOpenShiftSCC("trident-csi", "privileged")
} else {
_ = client.RemoveTridentUserFromOpenShiftSCC("trident", "anyuid")
}
err := client.CreateObjectByYAML(k8sclient.GetOpenShiftSCCYAML("trident", user, appLabelVal, TridentPodNamespace))
if err != nil {
return fmt.Errorf("cannot create trident's scc; %v", err)
}
return nil
}

// DeleteOpenShiftTridentSCC deletes the trident-only SCC that the trident user uses. This only works for OpenShift.
func DeleteOpenShiftTridentSCC(user string) error {
labelVal := TridentLegacyLabelValue
if csi {
labelVal = TridentCSILabelValue
}
err := client.DeleteObjectByYAML(k8sclient.GetOpenShiftSCCYAML("trident", user, labelVal, TridentPodNamespace),
true)

if err != nil {
return fmt.Errorf("%s; %v", "could not delete trident's scc", err)
}
return nil
}

func createInstallerRBACObjects() error {

// Create service account
Expand Down Expand Up @@ -1928,14 +1947,14 @@ func createInstallerRBACObjects() error {

// If OpenShift, add Trident to security context constraint(s)
if client.Flavor() == k8sclient.FlavorOpenShift {
if returnError = client.AddTridentUserToOpenShiftSCC("trident-installer", "privileged"); returnError != nil {
returnError = fmt.Errorf("could not modify security context constraint; %v", returnError)
if returnError = CreateOpenShiftTridentSCC("trident-installer", TridentInstallerLabelValue); returnError != nil {
returnError = fmt.Errorf("could not create security context constraint; %v", returnError)
return returnError
}
log.WithFields(log.Fields{
"scc": "privileged",
"scc": "trident",
"user": "trident-installer",
}).Info("Added security context constraint user.")
}).Info("created Trident security context constraint.")
}

return nil
Expand Down Expand Up @@ -1980,14 +1999,14 @@ func removeInstallerRBACObjects(logLevel log.Level) (anyErrors bool) {

// If OpenShift, remove Trident from security context constraint(s)
if client.Flavor() == k8sclient.FlavorOpenShift {
if err := client.RemoveTridentUserFromOpenShiftSCC("trident-installer", "privileged"); err != nil {
log.WithField("error", err).Warning("Could not modify security context constraint.")
if err := DeleteOpenShiftTridentSCC("trident-installer"); err != nil {
log.WithField("error", err).Warning("Could not delete security context constraint.")
anyErrors = true
} else {
logFunc(log.Fields{
"scc": "privileged",
"scc": "trident",
"user": "trident-installer",
})("Removed security context constraint user.")
})("Removed Trident's security context constraint.")
}
}

Expand Down
28 changes: 1 addition & 27 deletions cli/k8s_client/k8s_cli_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1227,7 +1227,6 @@ func (c *KubectlClient) CreateObjectByFile(filePath string) error {
// CreateObjectByYAML creates one or more objects on the server from a YAML/JSON document.
func (c *KubectlClient) CreateObjectByYAML(yamlData string) error {
for _, yamlDocument := range regexp.MustCompile(YAMLSeparator).Split(yamlData, -1) {

checkCreateObjectByYAML := func() error {
if returnError := c.createObjectByYAML(yamlDocument); returnError != nil {
log.WithFields(log.Fields{
Expand Down Expand Up @@ -1361,32 +1360,7 @@ func (c *KubectlClient) updateObjectByYAML(yaml string) error {
return nil
}

// AddTridentUserToOpenShiftSCC adds the specified user (typically a service account) to the 'anyuid'
// security context constraint. This only works for OpenShift.
func (c *KubectlClient) AddTridentUserToOpenShiftSCC(user, scc string) error {

if c.flavor != FlavorOpenShift {
return errors.New("the current client context is not OpenShift")
}

// This command appears to be idempotent, so no need to call isTridentUserInOpenShiftSCC() first.
args := []string{
fmt.Sprintf("--namespace=%s", c.namespace),
"adm",
"policy",
"add-scc-to-user",
scc,
"-z",
user,
}
out, err := exec.Command(c.cli, args...).CombinedOutput()
if err != nil {
return fmt.Errorf("%s; %v", string(out), err)
}
return nil
}

// RemoveTridentUserFromOpenShiftSCC removes the specified user (typically a service account) from the 'anyuid'
// RemoveTridentUserFromOpenShiftSCC removes the specified user (typically a service account) from the
// security context constraint. This only works for OpenShift.
func (c *KubectlClient) RemoveTridentUserFromOpenShiftSCC(user, scc string) error {

Expand Down
54 changes: 0 additions & 54 deletions cli/k8s_client/k8s_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ type Interface interface {
CreateObjectByYAML(yaml string) error
DeleteObjectByFile(filePath string, ignoreNotFound bool) error
DeleteObjectByYAML(yaml string, ignoreNotFound bool) error
AddTridentUserToOpenShiftSCC(user, scc string) error
RemoveTridentUserFromOpenShiftSCC(user, scc string) error
FollowPodLogs(pod, container, namespace string, logLineCallback LogLineCallback)
AddFinalizerToCRD(crdName string) error
Expand Down Expand Up @@ -1455,59 +1454,6 @@ func (k *KubeClient) getDynamicResource(gvk *schema.GroupVersionKind) (*schema.G
return nil, false, errors.New("API resource not found")
}

// AddTridentUserToOpenShiftSCC adds the specified user (typically a service account) to the 'anyuid'
// security context constraint. This only works for OpenShift.
func (k *KubeClient) AddTridentUserToOpenShiftSCC(user, scc string) error {

sccUser := fmt.Sprintf("system:serviceaccount:%s:%s", k.namespace, user)

// Read the SCC object from the server
openShiftSCCQueryYAML := GetOpenShiftSCCQueryYAML(scc)
unstruct, err := k.getUnstructuredObjectByYAML(openShiftSCCQueryYAML)
if err != nil {
return err
}

// Ensure the user isn't already present
found := false
if users, ok := unstruct.Object["users"]; ok {
if usersSlice, ok := users.([]interface{}); ok {
for _, userIntf := range usersSlice {
if user, ok := userIntf.(string); ok && user == sccUser {
found = true
break
}
}
} else {
return fmt.Errorf("users type is %T", users)
}
}

// Maintain idempotency by returning success if user already present
if found {
log.WithField("user", sccUser).Debug("SCC user already present, ignoring.")
return nil
}

// Add the user
if users, ok := unstruct.Object["users"]; ok {
if usersSlice, ok := users.([]interface{}); ok {
unstruct.Object["users"] = append(usersSlice, sccUser)
} else {
return fmt.Errorf("users type is %T", users)
}
}

// Convert to JSON
jsonData, err := unstruct.MarshalJSON()
if err != nil {
return err
}

// Update the object on the server
return k.updateObjectByYAML(string(jsonData))
}

// RemoveTridentUserFromOpenShiftSCC removes the specified user (typically a service account) from the 'anyuid'
// security context constraint. This only works for OpenShift, and it must be idempotent.
func (k *KubeClient) RemoveTridentUserFromOpenShiftSCC(user, scc string) error {
Expand Down
96 changes: 96 additions & 0 deletions cli/k8s_client/yaml_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,102 @@ metadata:
namespace: {NAMESPACE}
`

func GetOpenShiftSCCYAML(sccName, user, appLabelValue, k8s_namespace string) string {
sccYAML := openShiftPrivilegedSCCYAML
if !strings.Contains(appLabelValue, "csi") && user != "trident-installer" {
sccYAML = openShiftUnprivilegedSCCYAML
}
sccYAML = strings.ReplaceAll(sccYAML, "{APP_LABEL_VALUE}", appLabelValue)
sccYAML = strings.ReplaceAll(sccYAML, "{SCC}", sccName)
sccYAML = strings.ReplaceAll(sccYAML, "{NAMESPACE}", k8s_namespace)
sccYAML = strings.ReplaceAll(sccYAML, "{USER}", user)
return sccYAML
}

const openShiftPrivilegedSCCYAML = `
apiVersion: security.openshift.io/v1
kind: SecurityContextConstraints
metadata:
annotations:
kubernetes.io/description: '{SCC} is a clone of the privileged built-in, and is meant just for use with trident.'
name: {SCC}
labels:
app: "{APP_LABEL_VALUE}"
allowHostDirVolumePlugin: true
allowHostIPC: true
allowHostNetwork: true
allowHostPID: true
allowHostPorts: true
allowPrivilegeEscalation: true
allowPrivilegedContainer: true
allowedCapabilities:
- '*'
allowedUnsafeSysctls:
- '*'
defaultAddCapabilities: null
fsGroup:
type: RunAsAny
groups: []
priority: null
readOnlyRootFilesystem: false
requiredDropCapabilities: null
runAsUser:
type: RunAsAny
seLinuxContext:
type: RunAsAny
seccompProfiles:
- '*'
supplementalGroups:
type: RunAsAny
users:
- system:serviceaccount:{NAMESPACE}:{USER}
volumes:
- '*'
`

const openShiftUnprivilegedSCCYAML = `
apiVersion: security.openshift.io/v1
kind: SecurityContextConstraints
metadata:
annotations:
kubernetes.io/description: '{SCC} is a clone of the anyuid built-in, and is meant just for use with trident.'
name: {SCC}
labels:
app: "{APP_LABEL_VALUE}"
allowHostDirVolumePlugin: false
allowHostIPC: false
allowHostNetwork: false
allowHostPID: false
allowHostPorts: false
allowPrivilegeEscalation: true
allowPrivilegedContainer: false
allowedCapabilities: null
apiVersion: security.openshift.io/v1
defaultAddCapabilities: null
fsGroup:
type: RunAsAny
groups: []
priority: 10
readOnlyRootFilesystem: false
requiredDropCapabilities:
- MKNOD
runAsUser:
type: RunAsAny
seLinuxContext:
type: MustRunAs
supplementalGroups:
type: RunAsAny
users:
- system:serviceaccount:{NAMESPACE}:{USER}
volumes:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- projected
- secret
`

func GetOpenShiftSCCQueryYAML(scc string) string {
return strings.Replace(openShiftSCCQueryYAMLTemplate, "{SCC}", scc, 1)
}
Expand Down

0 comments on commit 83e2f26

Please sign in to comment.