Skip to content

Commit

Permalink
Setup securitycontext for cassandra (#275)
Browse files Browse the repository at this point in the history
* Setup securitycontext for cassandra

* WIP - adding check for docker image versions for running as root

* WIP - more tests

* WIP - logic fix and hooked up the conditional

* Default to false if image name is unparseable

* Added DockerImageRunsAsCassandra field

* Use the override field, if it is defined

* added readme

* simpler logic

* doc update
  • Loading branch information
respringer authored Oct 13, 2020
1 parent 9c91d16 commit 4006e56
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ spec:
value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
dockerImageRunsAsCassandra:
description: Does the Server Docker image run as the Cassandra user?
type: boolean
dseWorkloads:
properties:
analyticsEnabled:
Expand Down
12 changes: 12 additions & 0 deletions docs/user/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ locally with minikube or another setup with a single Kubernetes worker node, you
reduce the `size` value accordingly, or set the `allowMultipleNodesPerWorker`
parameter to `true`.

## The server image user

If the server image runs as the "cassandra" user, then a PodSecurityContext for that user will be defined by cass-operator. Otherwise the server image is assumed to be running as the "root" user and a PodSecurityContext is not defined.

For serverType="dse", the server images run as the "cassandra" user.

For serverType="cassandra", the cass-operator follows these steps in order to determine which user the docker image is run as:

1. If the CassandraDatacenter.Spec.DockerImageRunsAsCassandra field is set, then that "true" or "false" value will be used.
2. If the serverVersion field is set to "3.11.6", "3.11.7", or "4.0.0", cass-operator assumes the image runs as the "root" user.
3. Otherwise, cass-operator assumes that the server is running as the "cassandra" user.

## Storage

Define the storage with a combination of the previously provisioned storage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ spec:
value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
dockerImageRunsAsCassandra:
description: Does the Server Docker image run as the Cassandra user?
type: boolean
dseWorkloads:
properties:
analyticsEnabled:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ type CassandraDatacenterSpec struct {
// +kubebuilder:validation:Enum=cassandra;dse
ServerType string `json:"serverType"`

// Does the Server Docker image run as the Cassandra user?
DockerImageRunsAsCassandra *bool `json:"dockerImageRunsAsCassandra,omitempty"`

// Config for the server, in YAML format
// +kubebuilder:pruning:PreserveUnknownFields
Config json.RawMessage `json:"config,omitempty"`
Expand Down
5 changes: 5 additions & 0 deletions operator/pkg/apis/cassandra/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 21 additions & 8 deletions operator/pkg/images/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
package images

import (
"fmt"
"os"
"strings"
"fmt"

corev1 "k8s.io/api/core/v1"
logf "sigs.k8s.io/controller-runtime/pkg/log"
Expand Down Expand Up @@ -67,7 +67,7 @@ const (
ImageEnumLength int = iota
)

var imageLookupMap map[Image]string = map[Image]string {
var imageLookupMap map[Image]string = map[Image]string{

Cassandra_3_11_6: "datastax/cassandra-mgmtapi-3_11_6:v0.1.5",
Cassandra_3_11_7: "datastax/cassandra-mgmtapi-3_11_7:v0.1.13",
Expand Down Expand Up @@ -96,27 +96,27 @@ var imageLookupMap map[Image]string = map[Image]string {
Reaper: "thelastpickle/cassandra-reaper:2.0.5",
}

var versionToOSSCassandra map[string]Image = map[string]Image {
var versionToOSSCassandra map[string]Image = map[string]Image{
"3.11.6": Cassandra_3_11_6,
"3.11.7": Cassandra_3_11_7,
"4.0.0": Cassandra_4_0_0,
}

var versionToUBIOSSCassandra map[string]Image = map[string]Image {
var versionToUBIOSSCassandra map[string]Image = map[string]Image{
"3.11.6": UBICassandra_3_11_6,
"3.11.7": UBICassandra_3_11_7,
"4.0.0": UBICassandra_4_0_0,
}

var versionToDSE map[string]Image = map[string]Image {
var versionToDSE map[string]Image = map[string]Image{
"6.8.0": DSE_6_8_0,
"6.8.1": DSE_6_8_1,
"6.8.2": DSE_6_8_2,
"6.8.3": DSE_6_8_3,
"6.8.4": DSE_6_8_4,
}

var versionToUBIDSE map[string]Image = map[string]Image {
var versionToUBIDSE map[string]Image = map[string]Image{
"6.8.0": UBIDSE_6_8_0,
"6.8.1": UBIDSE_6_8_1,
"6.8.2": UBIDSE_6_8_2,
Expand Down Expand Up @@ -148,6 +148,19 @@ func applyDefaultRegistryOverride(image string) string {
}
}

// Calculate if this Docker Image run as the cassandra user?
// This is meant to be used when the CassandraDatacenter does not
// explicitly set the DockerImageRunsAsCassandra field.
func CalculateDockerImageRunsAsCassandra(version string) bool {
if version == "3.11.6" || version == "3.11.7" || version == "4.0.0" {
return false
}

// Otherwise, we assume the image is running as the "cassandra" user

return true
}

func GetImage(name Image) string {
image, ok := imageLookupMap[name]
if !ok {
Expand Down Expand Up @@ -229,9 +242,9 @@ func AddDefaultRegistryImagePullSecrets(podSpec *corev1.PodSpec) bool {
secretName := os.Getenv(envDefaultRegistryOverridePullSecrets)
if secretName != "" {
podSpec.ImagePullSecrets = append(
podSpec.ImagePullSecrets,
podSpec.ImagePullSecrets,
corev1.LocalObjectReference{Name: secretName})
return true
}
return false
}
}
37 changes: 34 additions & 3 deletions operator/pkg/images/images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
package images

import (
"fmt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func tempSetEnv(name, value string) (func(), error) {
Expand All @@ -26,7 +27,7 @@ func tempSetEnv(name, value string) (func(), error) {
}

func Test_AllImageEnumValuesHaveImageDefined(t *testing.T) {
for i :=0; i < ImageEnumLength; i++ {
for i := 0; i < ImageEnumLength; i++ {
if Image(i) == BaseImageOS {
// the BaseImageOS is unique in that we get it's value from an
// environment variable, so if the environment variable is not
Expand Down Expand Up @@ -54,3 +55,33 @@ func Test_DefaultRegistryOverride(t *testing.T) {
image := GetConfigBuilderImage()
assert.True(t, strings.HasPrefix(image, "localhost:5000/"))
}

func Test_CalculateDockerImageRunsAsCassandra(t *testing.T) {
tests := []struct {
version string
want bool
}{
{
version: "3.11.6",
want: false,
},
{
version: "3.11.7",
want: false,
},
{
version: "4.0.0",
want: false,
},
// We default to true
{
version: "4.0.1",
want: true,
},
}
for _, tt := range tests {
got := CalculateDockerImageRunsAsCassandra(tt.version)

assert.Equal(t, got, tt.want, fmt.Sprintf("Version: %s should not have returned %v", tt.version, got))
}
}
18 changes: 17 additions & 1 deletion operator/pkg/reconciliation/construct_statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

api "github.com/datastax/cass-operator/operator/pkg/apis/cassandra/v1beta1"
"github.com/datastax/cass-operator/operator/pkg/httphelper"
"github.com/datastax/cass-operator/operator/pkg/images"
"github.com/datastax/cass-operator/operator/pkg/oplabels"
"github.com/datastax/cass-operator/operator/pkg/utils"

Expand Down Expand Up @@ -64,6 +65,20 @@ func newStatefulSetForCassandraDatacenter(
return newStatefulSetForCassandraDatacenterHelper(rackName, dc, replicaCount, false)
}

// Check if we need to define a SecurityContext.
// If the user defines the DockerImageRunsAsCassandra field, we trust that.
// Otherwise if ServerType is "dse", the answer is true.
// Otherwise we use the logic in CalculateDockerImageRunsAsCassandra
// to calculate a reasonable answer.
func shouldDefineSecurityContext(dc *api.CassandraDatacenter) bool {
// The override field always wins
if dc.Spec.DockerImageRunsAsCassandra != nil {
return *dc.Spec.DockerImageRunsAsCassandra
}

return dc.Spec.ServerType == "dse" || images.CalculateDockerImageRunsAsCassandra(dc.Spec.ServerVersion)
}

// Create a statefulset object for the Datacenter.
func newStatefulSetForCassandraDatacenterHelper(
rackName string,
Expand Down Expand Up @@ -125,7 +140,8 @@ func newStatefulSetForCassandraDatacenterHelper(
}

// workaround for https://cloud.google.com/kubernetes-engine/docs/security-bulletins#may-31-2019
if dc.Spec.ServerType == "dse" {

if shouldDefineSecurityContext(dc) {
var userID int64 = 999
template.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsUser: &userID,
Expand Down

0 comments on commit 4006e56

Please sign in to comment.