1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Feature] Add secured contatiners' features (#1287)

This commit is contained in:
Tomasz Mielech 2023-07-19 11:46:59 +02:00 committed by GitHub
parent 7ad5d275b4
commit a221ce9b6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 712 additions and 43 deletions

View file

@ -3,6 +3,7 @@
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
- (Feature) Backup lifetime - remove Backup once its lifetime has been reached
- (Feature) Add Feature dependency
- (Feature) Run secured containers as a feature
## [1.2.31](https://github.com/arangodb/kube-arangodb/tree/1.2.31) (2023-07-14)
- (Improvement) Block traffic on the services if there is more than 1 active leader in ActiveFailover mode

View file

@ -68,11 +68,13 @@ covers individual newer features separately.
| Graceful Restart | 1.2.5 | >= 3.6.0 | Community, Enterprise | 1.0.7 | Production | True | --deployment.feature.graceful-shutdown | N/A |
| Optional Graceful Restart | 1.2.25 | >= 3.6.0 | Community, Enterprise | 1.2.5 | Beta | True | --deployment.feature.optional-graceful-shutdown | N/A |
| Operator Internal Metrics Exporter | 1.2.0 | >= 3.6.0 | Community, Enterprise | 1.2.0 | Production | True | --deployment.feature.metrics-exporter | N/A |
| Operator Ephemeral Volumes | 1.2.2 | >= 3.7.0 | Community, Enterprise | 1.2.2 | Alpha | False | --deployment.feature.ephemeral-volumes | N/A |
| [Operator Ephemeral Volumes](docs/design/features/ephemeral_volumes.md) | 1.2.2 | >= 3.7.0 | Community, Enterprise | 1.2.2 | Alpha | False | --deployment.feature.ephemeral-volumes | N/A |
| [Operator Ephemeral Volumes](docs/design/features/ephemeral_volumes.md) | 1.2.2 | >= 3.7.0 | Community, Enterprise | 1.2.31 | Beta | False | --deployment.feature.ephemeral-volumes | N/A |
| [Failover Leader service](docs/design/features/failover_leader_service.md) | 1.2.13 | >= 3.7.0 | Community, Enterprise | 1.2.13 | Production | False | --deployment.feature.failover-leadership | N/A |
| [Spec Default Restore](docs/design/features/deployment_spec_defaults.md) | 1.2.21 | >= 3.7.0 | Community, Enterprise | 1.2.21 | Beta | True | --deployment.feature.deployment-spec-defaults-restore | If set to False Operator will not change ArangoDeployment Spec |
| [Force Rebuild Out Synced Shards](docs/design/features/rebuild_out_synced_shards.md) | 1.2.27 | >= 3.8.0 | Community, Enterprise | 1.2.27 | Beta | False | --deployment.feature.force-rebuild-out-synced-shards | It should be used only if user is aware of the risks. |
| [Rebalancer V2](docs/design/features/rebalancer_v2.md) | 1.2.31 | >= 3.10.0 | Community, Enterprise | 1.2.31 | Alpha | False | --deployment.feature.rebalancer-v2 | N/A |
| [Secured containers](docs/design/features/secured_containers.md) | 1.2.31 | >= 3.7.0 | Community, Enterprise | 1.2.31 | Alpha | False | --deployment.feature.secured-containers | If set to True Operator will run containers in secure mode |
## Operator Community Edition (CE)

View file

@ -0,0 +1,20 @@
# Operator Ephemeral Volumes
## Overview
Operator add 2 EmptyDir mounts to the ArangoDB Pods:
- `ephemeral-apps` which is mounted under `/ephemeral/app` and passed to the ArangoDB process via `--javascript.app-path` arg
- `ephemeral-tmp` which is mounted under `/ephemeral/tmp` and passed to the ArangoDB process via `--temp.path` arg
This adds possibility to enable ReadOnly FileSystem via PodSecurityContext configuration.
## How to use
To enable this feature use `--deployment.feature.ephemeral-volumes` arg, which needs be passed to the operator:
```shell
helm upgrade --install kube-arangodb \
https://github.com/arangodb/kube-arangodb/releases/download/$VER/kube-arangodb-$VER.tgz \
--set "operator.args={--deployment.feature.ephemeral-volumes}"
```

View file

@ -0,0 +1,28 @@
# Secured Containers
## Overview
Change Default settings of:
* PodSecurityContext
* `FSGroup` is set to `3000`
* SecurityContext (Container)
* `RunAsUser` is set to `1000`
* `RunAsGroup` is set to `2000`
* `RunAsNonRoot` is set to `true`
* `ReadOnlyRootFilesystem` is set to `true`
* `Capabilities.Drop` is set to `["ALL"]`
## Dependencies
- [Operator Ephemeral Volumes](./ephemeral_volumes.md) should be Enabled and Supported.
## How to use
To enable this feature use `--deployment.feature.secured-containers` arg, which needs be passed to the operator:
```shell
helm upgrade --install kube-arangodb \
https://github.com/arangodb/kube-arangodb/releases/download/$VER/kube-arangodb-$VER.tgz \
--set "operator.args={--deployment.feature.secured-containers}"
```

View file

@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -20,7 +20,17 @@
package v1
import core "k8s.io/api/core/v1"
import (
core "k8s.io/api/core/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
)
const (
defaultRunAsUser = 1000
defaultRunAsGroup = 2000
defaultFSGroup = 3000
)
// ServerGroupSpecSecurityContext contains specification for pod security context
type ServerGroupSpecSecurityContext struct {
@ -69,24 +79,31 @@ func (s *ServerGroupSpecSecurityContext) GetAddCapabilities() []core.Capability
return s.AddCapabilities
}
// NewSecurityContext creates new pod security context
func (s *ServerGroupSpecSecurityContext) NewPodSecurityContext() *core.PodSecurityContext {
if s == nil {
return nil
// NewPodSecurityContext creates new pod security context
func (s *ServerGroupSpecSecurityContext) NewPodSecurityContext(secured bool) *core.PodSecurityContext {
var psc *core.PodSecurityContext
if s != nil && (s.FSGroup != nil || len(s.SupplementalGroups) > 0) {
psc = &core.PodSecurityContext{
SupplementalGroups: s.SupplementalGroups,
FSGroup: s.FSGroup,
}
}
if s.FSGroup == nil && len(s.SupplementalGroups) == 0 {
return nil
if secured {
if psc == nil {
psc = &core.PodSecurityContext{}
}
if psc.FSGroup == nil {
psc.FSGroup = util.NewType[int64](defaultFSGroup)
}
}
return &core.PodSecurityContext{
SupplementalGroups: s.SupplementalGroups,
FSGroup: s.FSGroup,
}
return psc
}
// NewSecurityContext creates new security context
func (s *ServerGroupSpecSecurityContext) NewSecurityContext() *core.SecurityContext {
func (s *ServerGroupSpecSecurityContext) NewSecurityContext(secured ...bool) *core.SecurityContext {
r := &core.SecurityContext{}
if s != nil {
@ -115,6 +132,27 @@ func (s *ServerGroupSpecSecurityContext) NewSecurityContext() *core.SecurityCont
capabilities.Add = append(capabilities.Add, caps...)
}
if len(secured) > 0 && secured[0] {
if r.RunAsUser == nil {
r.RunAsUser = util.NewType[int64](defaultRunAsUser)
}
if r.RunAsGroup == nil {
r.RunAsGroup = util.NewType[int64](defaultRunAsGroup)
}
if r.RunAsNonRoot == nil {
r.RunAsNonRoot = util.NewType[bool](true)
}
if r.ReadOnlyRootFilesystem == nil {
r.ReadOnlyRootFilesystem = util.NewType[bool](true)
}
if capabilities.Drop == nil {
capabilities.Drop = []core.Capability{
"ALL",
}
}
}
r.Capabilities = capabilities
return r

View file

@ -0,0 +1,176 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package v1
import (
"testing"
"github.com/stretchr/testify/assert"
core "k8s.io/api/core/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
)
func TestServerGroupSpecSecurityContext_NewPodSecurityContext(t *testing.T) {
testCases := map[string]struct {
sc *ServerGroupSpecSecurityContext
secured bool
want *core.PodSecurityContext
}{
"default unsecured pod security": {
sc: nil,
want: nil,
},
"default secured pod security": {
sc: nil,
secured: true,
want: &core.PodSecurityContext{
FSGroup: util.NewType[int64](defaultFSGroup),
},
},
"user secured pod security takes precedence": {
sc: &ServerGroupSpecSecurityContext{
FSGroup: util.NewType[int64](3001),
},
secured: true,
want: &core.PodSecurityContext{
FSGroup: util.NewType[int64](3001),
},
},
"user secured pod security with FSGroup==nil": {
sc: &ServerGroupSpecSecurityContext{
SupplementalGroups: []int64{1},
},
secured: true,
want: &core.PodSecurityContext{
FSGroup: util.NewType[int64](defaultFSGroup),
SupplementalGroups: []int64{1},
},
},
"user unsecured pod security": {
sc: &ServerGroupSpecSecurityContext{
FSGroup: util.NewType[int64](3001),
SupplementalGroups: []int64{1},
},
secured: false,
want: &core.PodSecurityContext{
FSGroup: util.NewType[int64](3001),
SupplementalGroups: []int64{1},
},
},
}
for testName, testCase := range testCases {
t.Run(testName, func(t *testing.T) {
actual := testCase.sc.NewPodSecurityContext(testCase.secured)
assert.Equalf(t, testCase.want, actual, "NewPodSecurityContext(%v)", testCase.secured)
})
}
}
func TestServerGroupSpecSecurityContext_NewSecurityContext(t *testing.T) {
tests := map[string]struct {
sc *ServerGroupSpecSecurityContext
secured bool
want *core.SecurityContext
}{
"default unsecured context security": {
sc: nil,
secured: false,
want: &core.SecurityContext{
Capabilities: &core.Capabilities{
Drop: []core.Capability{"ALL"},
},
},
},
"default secured context security": {
sc: nil,
secured: true,
want: &core.SecurityContext{
Capabilities: &core.Capabilities{
Drop: []core.Capability{"ALL"},
},
ReadOnlyRootFilesystem: util.NewType(true),
RunAsGroup: util.NewType[int64](defaultRunAsGroup),
RunAsNonRoot: util.NewType(true),
RunAsUser: util.NewType[int64](defaultRunAsUser),
},
},
"user unsecured context security": {
sc: &ServerGroupSpecSecurityContext{
RunAsUser: util.NewType[int64](3001),
},
secured: false,
want: &core.SecurityContext{
Capabilities: &core.Capabilities{
Drop: []core.Capability{"ALL"},
},
RunAsUser: util.NewType[int64](3001),
},
},
"secured user setting RunAsUser takes precedence": {
sc: &ServerGroupSpecSecurityContext{
RunAsUser: util.NewType[int64](3001),
},
secured: true,
want: &core.SecurityContext{
Capabilities: &core.Capabilities{
Drop: []core.Capability{"ALL"},
},
ReadOnlyRootFilesystem: util.NewType(true),
RunAsGroup: util.NewType[int64](defaultRunAsGroup),
RunAsNonRoot: util.NewType(true),
RunAsUser: util.NewType[int64](3001),
},
},
"secured mixed users' settings takes precedence": {
sc: &ServerGroupSpecSecurityContext{
AddCapabilities: []core.Capability{"1"},
AllowPrivilegeEscalation: util.NewType(true),
DropAllCapabilities: util.NewType(false), // secured will turn it on
Privileged: util.NewType(false),
RunAsNonRoot: util.NewType(false),
RunAsUser: util.NewType[int64](3001),
},
secured: true,
want: &core.SecurityContext{
AllowPrivilegeEscalation: util.NewType(true),
Capabilities: &core.Capabilities{
Add: []core.Capability{"1"},
Drop: []core.Capability{"ALL"},
},
Privileged: util.NewType(false),
ReadOnlyRootFilesystem: util.NewType(true),
RunAsGroup: util.NewType[int64](defaultRunAsGroup),
RunAsNonRoot: util.NewType(false),
RunAsUser: util.NewType[int64](3001),
},
},
}
for testName, testCase := range tests {
t.Run(testName, func(t *testing.T) {
actual := testCase.sc.NewSecurityContext(testCase.secured)
assert.Equalf(t, testCase.want, actual, "NewSecurityContext(%v)", testCase.secured)
})
}
}

View file

@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -20,7 +20,17 @@
package v2alpha1
import core "k8s.io/api/core/v1"
import (
core "k8s.io/api/core/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
)
const (
defaultRunAsUser = 1000
defaultRunAsGroup = 2000
defaultFSGroup = 3000
)
// ServerGroupSpecSecurityContext contains specification for pod security context
type ServerGroupSpecSecurityContext struct {
@ -69,24 +79,31 @@ func (s *ServerGroupSpecSecurityContext) GetAddCapabilities() []core.Capability
return s.AddCapabilities
}
// NewSecurityContext creates new pod security context
func (s *ServerGroupSpecSecurityContext) NewPodSecurityContext() *core.PodSecurityContext {
if s == nil {
return nil
// NewPodSecurityContext creates new pod security context
func (s *ServerGroupSpecSecurityContext) NewPodSecurityContext(secured bool) *core.PodSecurityContext {
var psc *core.PodSecurityContext
if s != nil && (s.FSGroup != nil || len(s.SupplementalGroups) > 0) {
psc = &core.PodSecurityContext{
SupplementalGroups: s.SupplementalGroups,
FSGroup: s.FSGroup,
}
}
if s.FSGroup == nil && len(s.SupplementalGroups) == 0 {
return nil
if secured {
if psc == nil {
psc = &core.PodSecurityContext{}
}
if psc.FSGroup == nil {
psc.FSGroup = util.NewType[int64](defaultFSGroup)
}
}
return &core.PodSecurityContext{
SupplementalGroups: s.SupplementalGroups,
FSGroup: s.FSGroup,
}
return psc
}
// NewSecurityContext creates new security context
func (s *ServerGroupSpecSecurityContext) NewSecurityContext() *core.SecurityContext {
func (s *ServerGroupSpecSecurityContext) NewSecurityContext(secured ...bool) *core.SecurityContext {
r := &core.SecurityContext{}
if s != nil {
@ -115,6 +132,27 @@ func (s *ServerGroupSpecSecurityContext) NewSecurityContext() *core.SecurityCont
capabilities.Add = append(capabilities.Add, caps...)
}
if len(secured) > 0 && secured[0] {
if r.RunAsUser == nil {
r.RunAsUser = util.NewType[int64](defaultRunAsUser)
}
if r.RunAsGroup == nil {
r.RunAsGroup = util.NewType[int64](defaultRunAsGroup)
}
if r.RunAsNonRoot == nil {
r.RunAsNonRoot = util.NewType[bool](true)
}
if r.ReadOnlyRootFilesystem == nil {
r.ReadOnlyRootFilesystem = util.NewType[bool](true)
}
if capabilities.Drop == nil {
capabilities.Drop = []core.Capability{
"ALL",
}
}
}
r.Capabilities = capabilities
return r

View file

@ -0,0 +1,176 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package v2alpha1
import (
"testing"
"github.com/stretchr/testify/assert"
core "k8s.io/api/core/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
)
func TestServerGroupSpecSecurityContext_NewPodSecurityContext(t *testing.T) {
testCases := map[string]struct {
sc *ServerGroupSpecSecurityContext
secured bool
want *core.PodSecurityContext
}{
"default unsecured pod security": {
sc: nil,
want: nil,
},
"default secured pod security": {
sc: nil,
secured: true,
want: &core.PodSecurityContext{
FSGroup: util.NewType[int64](defaultFSGroup),
},
},
"user secured pod security takes precedence": {
sc: &ServerGroupSpecSecurityContext{
FSGroup: util.NewType[int64](3001),
},
secured: true,
want: &core.PodSecurityContext{
FSGroup: util.NewType[int64](3001),
},
},
"user secured pod security with FSGroup==nil": {
sc: &ServerGroupSpecSecurityContext{
SupplementalGroups: []int64{1},
},
secured: true,
want: &core.PodSecurityContext{
FSGroup: util.NewType[int64](defaultFSGroup),
SupplementalGroups: []int64{1},
},
},
"user unsecured pod security": {
sc: &ServerGroupSpecSecurityContext{
FSGroup: util.NewType[int64](3001),
SupplementalGroups: []int64{1},
},
secured: false,
want: &core.PodSecurityContext{
FSGroup: util.NewType[int64](3001),
SupplementalGroups: []int64{1},
},
},
}
for testName, testCase := range testCases {
t.Run(testName, func(t *testing.T) {
actual := testCase.sc.NewPodSecurityContext(testCase.secured)
assert.Equalf(t, testCase.want, actual, "NewPodSecurityContext(%v)", testCase.secured)
})
}
}
func TestServerGroupSpecSecurityContext_NewSecurityContext(t *testing.T) {
tests := map[string]struct {
sc *ServerGroupSpecSecurityContext
secured bool
want *core.SecurityContext
}{
"default unsecured context security": {
sc: nil,
secured: false,
want: &core.SecurityContext{
Capabilities: &core.Capabilities{
Drop: []core.Capability{"ALL"},
},
},
},
"default secured context security": {
sc: nil,
secured: true,
want: &core.SecurityContext{
Capabilities: &core.Capabilities{
Drop: []core.Capability{"ALL"},
},
ReadOnlyRootFilesystem: util.NewType(true),
RunAsGroup: util.NewType[int64](defaultRunAsGroup),
RunAsNonRoot: util.NewType(true),
RunAsUser: util.NewType[int64](defaultRunAsUser),
},
},
"user unsecured context security": {
sc: &ServerGroupSpecSecurityContext{
RunAsUser: util.NewType[int64](3001),
},
secured: false,
want: &core.SecurityContext{
Capabilities: &core.Capabilities{
Drop: []core.Capability{"ALL"},
},
RunAsUser: util.NewType[int64](3001),
},
},
"secured user setting RunAsUser takes precedence": {
sc: &ServerGroupSpecSecurityContext{
RunAsUser: util.NewType[int64](3001),
},
secured: true,
want: &core.SecurityContext{
Capabilities: &core.Capabilities{
Drop: []core.Capability{"ALL"},
},
ReadOnlyRootFilesystem: util.NewType(true),
RunAsGroup: util.NewType[int64](defaultRunAsGroup),
RunAsNonRoot: util.NewType(true),
RunAsUser: util.NewType[int64](3001),
},
},
"secured mixed users' settings takes precedence": {
sc: &ServerGroupSpecSecurityContext{
AddCapabilities: []core.Capability{"1"},
AllowPrivilegeEscalation: util.NewType(true),
DropAllCapabilities: util.NewType(false), // secured will turn it on
Privileged: util.NewType(false),
RunAsNonRoot: util.NewType(false),
RunAsUser: util.NewType[int64](3001),
},
secured: true,
want: &core.SecurityContext{
AllowPrivilegeEscalation: util.NewType(true),
Capabilities: &core.Capabilities{
Add: []core.Capability{"1"},
Drop: []core.Capability{"ALL"},
},
Privileged: util.NewType(false),
ReadOnlyRootFilesystem: util.NewType(true),
RunAsGroup: util.NewType[int64](defaultRunAsGroup),
RunAsNonRoot: util.NewType(false),
RunAsUser: util.NewType[int64](3001),
},
},
}
for testName, testCase := range tests {
t.Run(testName, func(t *testing.T) {
actual := testCase.sc.NewSecurityContext(testCase.secured)
assert.Equalf(t, testCase.want, actual, "NewSecurityContext(%v)", testCase.secured)
})
}
}

View file

@ -68,6 +68,7 @@ type Feature interface {
Hidden() bool
Supported(v driver.Version, enterprise bool) bool
ImageSupported(i *api.ImageInfo) bool
GetDependencies() []string
}
type feature struct {
@ -104,6 +105,20 @@ func (f feature) Hidden() bool {
return f.hidden
}
// GetDependencies returns direct dependencies' names of features.
func (f feature) GetDependencies() []string {
if len(f.dependencies) == 0 {
return nil
}
deps := make([]string, 0, len(f.dependencies))
for _, dependency := range f.dependencies {
deps = append(deps, dependency.Name())
}
return deps
}
func (f feature) Supported(v driver.Version, enterprise bool) bool {
return Supported(&f, v, enterprise)
}

View file

@ -30,6 +30,7 @@ import (
"github.com/arangodb/go-driver"
"github.com/arangodb/kube-arangodb/pkg/logging"
"github.com/arangodb/kube-arangodb/pkg/util"
)
@ -119,9 +120,54 @@ func Init(cmd *cobra.Command) error {
f.StringVar(&configMapName, "features-config-map-name", DefaultFeaturesConfigMap, "Name of the Feature Map ConfigMap")
checkDependencies(cmd)
return nil
}
func checkDependencies(cmd *cobra.Command) {
enableDeps := func(_ *cobra.Command, _ []string) {
// Turn on dependencies. This function will be called when all process's arguments are passed, so
// all required features are enabled and dependencies should be enabled too.
EnableDependencies()
// Log enabled features when process starts.
for _, f := range features {
if !f.Enabled() {
continue
}
l := logging.Global().RegisterAndGetLogger("features", logging.Info)
if deps := f.GetDependencies(); len(deps) > 0 {
l = l.Strs("dependencies", deps...)
}
l.Bool("enterpriseArangoDBRequired", f.EnterpriseRequired()).
Str("minArangoDBVersion", string(f.Version())).
Str("name", f.Name()).
Info("feature enabled")
}
}
// Wrap pre-run function if it set.
if cmd.PreRunE != nil {
local := cmd.PreRunE
cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
enableDeps(cmd, args)
return local(cmd, args)
}
} else if cmd.PreRun != nil {
local := cmd.PreRun
cmd.PreRun = func(cmd *cobra.Command, args []string) {
enableDeps(cmd, args)
local(cmd, args)
}
} else {
cmd.PreRun = enableDeps
}
}
func cmdRun(_ *cobra.Command, _ []string) {
featuresLock.Lock()
defer featuresLock.Unlock()
@ -151,6 +197,10 @@ func cmdRun(_ *cobra.Command, _ []string) {
println("ArangoDB Edition Required: Community, Enterprise")
}
if deps := feature.GetDependencies(); len(deps) > 0 {
println(fmt.Sprintf("Dependencies: %v", deps))
}
if ok, reason := feature.Deprecated(); ok {
println(fmt.Sprintf("Deprecated: %s", reason))
}
@ -202,3 +252,45 @@ func GetFeatureArgName(featureName string) string {
func isEnabledFeatureFromEnv(arg string) bool {
return os.Getenv(util.NormalizeEnv(arg)) == Enabled
}
// EnableDependencies enables dependencies for features if it is required.
func EnableDependencies() {
for {
var changed bool
for _, f := range features {
if !f.Enabled() {
continue
}
for _, depName := range f.GetDependencies() {
// Don't use `dependency.Enabled` here because `constValue` is involved here, and it can not be changed.
if enableDependencyByName(depName) {
// Dependency is changed so list of features must be iterated once again, because this
// dependency can turn on other dependencies.
changed = true
}
}
}
if !changed {
return
}
}
}
// enableDependencyByName enables dependency by name of a feature.
func enableDependencyByName(name string) bool {
for _, f := range features {
if name != f.Name() {
continue
}
if ep := f.EnabledPointer(); !*ep {
*ep = true
return true
}
}
return false
}

View file

@ -0,0 +1,44 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package features
import (
"fmt"
)
func init() {
registerFeature(securedContainers)
}
var securedContainers = &feature{
name: "secured-containers",
description: fmt.Sprintf("Create server's containers with non root privileges. "+
"It enables '%s' feature implicitly", ephemeralVolumes.Name()),
version: "3.7.0",
enterpriseRequired: false,
enabledByDefault: false,
dependencies: []Feature{ephemeralVolumes},
}
// SecuredContainers returns secured containers feature.
func SecuredContainers() Feature {
return securedContainers
}

View file

@ -388,7 +388,7 @@ func (i *ImageUpdatePod) Validate(_ interfaces.Inspector) error {
func (i *ImageUpdatePod) ApplyPodSpec(p *core.PodSpec) error {
if id := i.spec.ID; id != nil {
p.SecurityContext = i.spec.ID.SecurityContext.NewPodSecurityContext()
p.SecurityContext = k8sutil.CreatePodSecurityContext(i.spec.ID.SecurityContext)
}
return nil
}
@ -441,7 +441,7 @@ func (a *ContainerIdentity) GetResourceRequirements() core.ResourceRequirements
}
func (a *ContainerIdentity) GetSecurityContext() *core.SecurityContext {
return a.ID.Get().SecurityContext.NewSecurityContext()
return k8sutil.CreateSecurityContext(a.ID.Get().SecurityContext)
}
// GetVolumeMounts returns nil for the basic container identity.

View file

@ -39,8 +39,8 @@ func (s security) Args(i Input) k8sutil.OptionPairs {
opts := k8sutil.CreateOptionPairs()
if features.EphemeralVolumes().Enabled() {
opts.Add("--temp.path", "/ephemeral/app")
opts.Add("--javascript.app-path", "/ephemeral/tmp")
opts.Add("--temp.path", "/ephemeral/tmp")
opts.Add("--javascript.app-path", "/ephemeral/app")
}
return opts

View file

@ -1,5 +1,5 @@
//
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -55,8 +55,8 @@ func ArangodbInternalExporterContainer(image string, args []string, livenessProb
},
},
Resources: k8sutil.ExtractPodResourceRequirement(resources),
SecurityContext: k8sutil.CreateSecurityContext(groupSpec.SecurityContext),
ImagePullPolicy: core.PullIfNotPresent,
SecurityContext: groupSpec.SecurityContext.NewSecurityContext(),
VolumeMounts: []core.VolumeMount{k8sutil.LifecycleVolumeMount()},
}

View file

@ -133,7 +133,7 @@ func (a *ArangoDContainer) GetExecutor() string {
}
func (a *ArangoDContainer) GetSecurityContext() *core.SecurityContext {
return a.groupSpec.SecurityContext.NewSecurityContext()
return k8sutil.CreateSecurityContext(a.groupSpec.SecurityContext)
}
func (a *ArangoDContainer) GetProbes() (*core.Probe, *core.Probe, *core.Probe, error) {
@ -435,8 +435,8 @@ func (m *MemberArangoDPod) GetInitContainers(cachedStatus interfaces.Inspector)
}
{
c, err := k8sutil.InitLifecycleContainer(m.resources.context.GetOperatorImage(), &m.spec.Lifecycle.Resources,
m.groupSpec.SecurityContext.NewSecurityContext())
sc := k8sutil.CreateSecurityContext(m.groupSpec.SecurityContext)
c, err := k8sutil.InitLifecycleContainer(m.resources.context.GetOperatorImage(), &m.spec.Lifecycle.Resources, sc)
if err != nil {
return nil, err
}
@ -447,8 +447,9 @@ func (m *MemberArangoDPod) GetInitContainers(cachedStatus interfaces.Inspector)
engine := m.spec.GetStorageEngine().AsArangoArgument()
requireUUID := m.group == api.ServerGroupDBServers && m.status.IsInitialized
c := k8sutil.ArangodInitContainer(api.ServerGroupReservedInitContainerNameUUID, m.status.ID, engine, executable, m.resources.context.GetOperatorImage(), requireUUID,
m.groupSpec.SecurityContext.NewSecurityContext())
sc := k8sutil.CreateSecurityContext(m.groupSpec.SecurityContext)
c := k8sutil.ArangodInitContainer(api.ServerGroupReservedInitContainerNameUUID, m.status.ID, engine, executable,
m.resources.context.GetOperatorImage(), requireUUID, sc)
initContainers = append(initContainers, c)
}
@ -551,8 +552,7 @@ func (m *MemberArangoDPod) createMetricsExporterSidecarInternalExporter() (*core
}
func (m *MemberArangoDPod) ApplyPodSpec(p *core.PodSpec) error {
p.SecurityContext = m.groupSpec.SecurityContext.NewPodSecurityContext()
p.SecurityContext = k8sutil.CreatePodSecurityContext(m.groupSpec.SecurityContext)
if s := m.groupSpec.SchedulerName; s != nil {
p.SchedulerName = *s
}

View file

@ -110,7 +110,7 @@ func (a *ArangoSyncContainer) GetExecutor() string {
}
func (a *ArangoSyncContainer) GetSecurityContext() *core.SecurityContext {
return a.groupSpec.SecurityContext.NewSecurityContext()
return k8sutil.CreateSecurityContext(a.groupSpec.SecurityContext)
}
func (a *ArangoSyncContainer) GetProbes() (*core.Probe, *core.Probe, *core.Probe, error) {
@ -291,8 +291,8 @@ func (m *MemberSyncPod) GetInitContainers(cachedStatus interfaces.Inspector) ([]
}
{
c, err := k8sutil.InitLifecycleContainer(m.resources.context.GetOperatorImage(), &m.spec.Lifecycle.Resources,
m.groupSpec.SecurityContext.NewSecurityContext())
sc := k8sutil.CreateSecurityContext(m.groupSpec.SecurityContext)
c, err := k8sutil.InitLifecycleContainer(m.resources.context.GetOperatorImage(), &m.spec.Lifecycle.Resources, sc)
if err != nil {
return nil, err
}

View file

@ -0,0 +1,39 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package k8sutil
import (
core "k8s.io/api/core/v1"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
)
// CreateSecurityContext returns security context.
// If secured container's feature is enabled then default values will set on nil fields.
func CreateSecurityContext(spec *api.ServerGroupSpecSecurityContext) *core.SecurityContext {
return spec.NewSecurityContext(features.SecuredContainers().Enabled())
}
// CreatePodSecurityContext creates pod's security context.
func CreatePodSecurityContext(spec *api.ServerGroupSpecSecurityContext) *core.PodSecurityContext {
return spec.NewPodSecurityContext(features.SecuredContainers().Enabled())
}