Skip to content

Commit 40d738b

Browse files
authored
FEATURE/allow-to-customize-security-context (#526)
1 parent 3d8b77e commit 40d738b

File tree

12 files changed

+286
-20
lines changed

12 files changed

+286
-20
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Change Log
22

33
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
4+
- Add Customizable SecurityContext for ArangoDeployment pods
5+
6+
## [0.4.4](https://github.com/arangodb/kube-arangodb/tree/0.4.4) (2020-02-27)
47
- Add new VolumeResize mode to be compatible with Azure flow
58
- Allow to customize probe configuration options
69
- Add new upgrade flag for ArangoDB 3.6.0<=

pkg/apis/deployment/v1/server_group_spec.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,71 @@ type ServerGroupSpec struct {
6868
VolumeResizeMode *PVCResizeMode `json:"pvcResizeMode,omitempty"`
6969
// Sidecars specifies a list of additional containers to be started
7070
Sidecars []v1.Container `json:"sidecars,omitempty"`
71+
// SecurityContext specifies security context for group
72+
SecurityContext *ServerGroupSpecSecurityContext `json:"securityContext,omitempty"`
73+
}
74+
75+
// ServerGroupSpecSecurityContext contains specification for pod security context
76+
type ServerGroupSpecSecurityContext struct {
77+
// DropAllCapabilities specifies if capabilities should be dropped for this pod containers
78+
//
79+
// Deprecated: This field is added for backward compatibility. Will be removed in 1.0.0.
80+
DropAllCapabilities *bool `json:"dropAllCapabilities,omitempty"`
81+
// AddCapabilities add new capabilities to containers
82+
AddCapabilities []v1.Capability `json:"addCapabilities,omitempty"`
83+
}
84+
85+
// GetDropAllCapabilities returns flag if capabilities should be dropped
86+
//
87+
// Deprecated: This function is added for backward compatibility. Will be removed in 1.0.0.
88+
func (s *ServerGroupSpecSecurityContext) GetDropAllCapabilities() bool {
89+
if s == nil {
90+
return true
91+
}
92+
93+
if s.DropAllCapabilities == nil {
94+
return true
95+
}
96+
97+
return *s.DropAllCapabilities
98+
}
99+
100+
// GetAddCapabilities add capabilities to pod context
101+
func (s *ServerGroupSpecSecurityContext) GetAddCapabilities() []v1.Capability {
102+
if s == nil {
103+
return nil
104+
}
105+
106+
if s.AddCapabilities == nil {
107+
return nil
108+
}
109+
110+
return s.AddCapabilities
111+
}
112+
113+
// NewSecurityContext creates new security context
114+
func (s *ServerGroupSpecSecurityContext) NewSecurityContext() *v1.SecurityContext {
115+
r := &v1.SecurityContext{}
116+
117+
capabilities := &v1.Capabilities{}
118+
119+
if s.GetDropAllCapabilities() {
120+
capabilities.Drop = []v1.Capability{
121+
"ALL",
122+
}
123+
}
124+
125+
if caps := s.GetAddCapabilities(); caps != nil {
126+
capabilities.Add = []v1.Capability{}
127+
128+
for _, capability := range caps {
129+
capabilities.Add = append(capabilities.Add, capability)
130+
}
131+
}
132+
133+
r.Capabilities = capabilities
134+
135+
return r
71136
}
72137

73138
// ServerGroupProbesSpec contains specification for probes for pods of the server group

pkg/apis/deployment/v1/zz_generated.deepcopy.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/deployment/images.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ func (i *ImageUpdatePod) GetTolerations() []v1.Toleration {
305305
return tolerations
306306
}
307307

308+
func (a *ArangoDImageUpdateContainer) GetSecurityContext() *v1.SecurityContext {
309+
return nil
310+
}
311+
308312
func (i *ImageUpdatePod) IsDeploymentMode() bool {
309313
return true
310314
}

pkg/deployment/reconcile/plan_builder.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@ func createPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
207207
plan = createRotateServerStoragePlan(log, apiObject, spec, status, context.GetPvc, context.CreateEvent)
208208
}
209209

210+
// Adjust security
211+
if plan.IsEmpty() {
212+
plan = createRotateServerSecurityPlan(log, spec, status, pods)
213+
}
214+
210215
// Check for the need to rotate TLS CA certificate and all members
211216
if plan.IsEmpty() {
212217
plan = createRotateTLSCAPlan(log, apiObject, spec, status, context.GetTLSCA, context.CreateEvent)
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2020 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Adam Janikowski
21+
//
22+
23+
package reconcile
24+
25+
import (
26+
"github.com/rs/zerolog"
27+
core "k8s.io/api/core/v1"
28+
29+
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
30+
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
31+
)
32+
33+
// createRotateServerStoragePlan creates plan to rotate a server and its volume because of a
34+
// different storage class or a difference in storage resource requirements.
35+
func createRotateServerSecurityPlan(log zerolog.Logger, spec api.DeploymentSpec, status api.DeploymentStatus,
36+
pods []core.Pod) api.Plan {
37+
var plan api.Plan
38+
status.Members.ForeachServerGroup(func(group api.ServerGroup, members api.MemberStatusList) error {
39+
for _, m := range members {
40+
if !plan.IsEmpty() {
41+
// Only 1 change at a time
42+
continue
43+
}
44+
45+
groupSpec := spec.GetServerGroupSpec(group)
46+
47+
pod, found := k8sutil.GetPodByName(pods, m.PodName)
48+
if !found {
49+
continue
50+
}
51+
52+
container, ok := getServerContainer(pod.Spec.Containers)
53+
if !ok {
54+
// We do not have server container in pod, which is not desired
55+
continue
56+
}
57+
58+
groupSC := groupSpec.SecurityContext.NewSecurityContext()
59+
containerSC := container.SecurityContext
60+
61+
if !compareSC(groupSC, containerSC) {
62+
log.Info().Str("member", m.ID).Str("group", group.AsRole()).Msg("Rotating security context")
63+
plan = append(plan,
64+
api.NewAction(api.ActionTypeRotateMember, group, m.ID),
65+
api.NewAction(api.ActionTypeWaitForMemberUp, group, m.ID),
66+
)
67+
}
68+
}
69+
return nil
70+
})
71+
return plan
72+
}
73+
74+
func getServerContainer(containers []core.Container) (core.Container, bool) {
75+
for _, container := range containers {
76+
if container.Name == k8sutil.ServerContainerName {
77+
return container, true
78+
}
79+
}
80+
81+
return core.Container{}, false
82+
}
83+
84+
func compareSC(a,b *core.SecurityContext) bool {
85+
if a == nil && b == nil {
86+
return true
87+
}
88+
89+
if a == nil || b == nil {
90+
return false
91+
}
92+
93+
if ok := compareCapabilities(a.Capabilities, b.Capabilities); !ok {
94+
return false
95+
}
96+
97+
return true
98+
}
99+
100+
func compareCapabilities(a,b *core.Capabilities) bool {
101+
if a == nil && b == nil {
102+
return true
103+
}
104+
105+
if a == nil || b == nil {
106+
return false
107+
}
108+
109+
if ok := compareCapabilityLists(a.Add, b.Add); !ok {
110+
return false
111+
}
112+
113+
if ok := compareCapabilityLists(a.Drop, b.Drop); !ok {
114+
return false
115+
}
116+
117+
return true
118+
}
119+
120+
func compareCapabilityLists(a, b []core.Capability) bool {
121+
if a == nil && b == nil {
122+
return true
123+
}
124+
125+
if a == nil || b == nil {
126+
return false
127+
}
128+
129+
if len(a) != len(b) {
130+
return false
131+
}
132+
133+
checked := map[core.Capability]bool{}
134+
135+
for _, capability := range a {
136+
checked[capability] = false
137+
}
138+
139+
for _, capability := range b {
140+
if _, ok := checked[capability]; !ok {
141+
return false
142+
}
143+
144+
checked[capability] = true
145+
}
146+
147+
for _, check := range checked {
148+
if !check {
149+
return false
150+
}
151+
}
152+
153+
return true
154+
}

pkg/deployment/resources/exporter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import (
3333

3434
// ArangodbExporterContainer creates metrics container
3535
func ArangodbExporterContainer(image string, args []string, livenessProbe *k8sutil.HTTPProbeConfig,
36-
resources v1.ResourceRequirements) v1.Container {
36+
resources v1.ResourceRequirements, securityContext *v1.SecurityContext) v1.Container {
3737

3838
c := v1.Container{
3939
Name: k8sutil.ExporterContainerName,
@@ -48,7 +48,7 @@ func ArangodbExporterContainer(image string, args []string, livenessProbe *k8sut
4848
},
4949
Resources: k8sutil.ExtractPodResourceRequirement(resources),
5050
ImagePullPolicy: v1.PullIfNotPresent,
51-
SecurityContext: k8sutil.SecurityContextWithoutCapabilities(),
51+
SecurityContext: securityContext,
5252
}
5353

5454
if livenessProbe != nil {

pkg/deployment/resources/pod_creator_arangod.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ func (a *ArangoDContainer) GetExecutor() string {
6161
return ArangoDExecutor
6262
}
6363

64+
func (a *ArangoDContainer) GetSecurityContext() *v1.SecurityContext {
65+
return a.groupSpec.SecurityContext.NewSecurityContext()
66+
}
67+
6468
func (a *ArangoDContainer) GetProbes() (*v1.Probe, *v1.Probe, error) {
6569
var liveness, readiness *v1.Probe
6670

@@ -169,7 +173,8 @@ func (m *MemberArangoDPod) GetSidecars(pod *v1.Pod) {
169173
}
170174

171175
c := ArangodbExporterContainer(image, createExporterArgs(m.spec.IsSecure()),
172-
createExporterLivenessProbe(m.spec.IsSecure()), m.spec.Metrics.Resources)
176+
createExporterLivenessProbe(m.spec.IsSecure()), m.spec.Metrics.Resources,
177+
m.groupSpec.SecurityContext.NewSecurityContext())
173178

174179
if m.spec.Metrics.GetJWTTokenSecretName() != "" {
175180
c.VolumeMounts = append(c.VolumeMounts, k8sutil.ExporterJWTVolumeMount())
@@ -253,7 +258,8 @@ func (m *MemberArangoDPod) GetInitContainers() ([]v1.Container, error) {
253258

254259
lifecycleImage := m.resources.context.GetLifecycleImage()
255260
if lifecycleImage != "" {
256-
c, err := k8sutil.InitLifecycleContainer(lifecycleImage, &m.spec.Lifecycle.Resources)
261+
c, err := k8sutil.InitLifecycleContainer(lifecycleImage, &m.spec.Lifecycle.Resources,
262+
m.groupSpec.SecurityContext.NewSecurityContext())
257263
if err != nil {
258264
return nil, err
259265
}
@@ -265,7 +271,8 @@ func (m *MemberArangoDPod) GetInitContainers() ([]v1.Container, error) {
265271
engine := m.spec.GetStorageEngine().AsArangoArgument()
266272
requireUUID := m.group == api.ServerGroupDBServers && m.status.IsInitialized
267273

268-
c := k8sutil.ArangodInitContainer("uuid", m.status.ID, engine, alpineImage, requireUUID)
274+
c := k8sutil.ArangodInitContainer("uuid", m.status.ID, engine, alpineImage, requireUUID,
275+
m.groupSpec.SecurityContext.NewSecurityContext())
269276
initContainers = append(initContainers, c)
270277
}
271278

pkg/deployment/resources/pod_creator_sync.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ func (a *ArangoSyncContainer) GetExecutor() string {
6060
return ArangoSyncExecutor
6161
}
6262

63+
func (a *ArangoSyncContainer) GetSecurityContext() *v1.SecurityContext {
64+
return a.groupSpec.SecurityContext.NewSecurityContext()
65+
}
66+
6367
func (a *ArangoSyncContainer) GetProbes() (*v1.Probe, *v1.Probe, error) {
6468
var liveness, readiness *v1.Probe
6569

@@ -206,7 +210,8 @@ func (m *MemberSyncPod) GetInitContainers() ([]v1.Container, error) {
206210

207211
lifecycleImage := m.resources.context.GetLifecycleImage()
208212
if lifecycleImage != "" {
209-
c, err := k8sutil.InitLifecycleContainer(lifecycleImage, &m.spec.Lifecycle.Resources)
213+
c, err := k8sutil.InitLifecycleContainer(lifecycleImage, &m.spec.Lifecycle.Resources,
214+
m.groupSpec.SecurityContext.NewSecurityContext())
210215
if err != nil {
211216
return nil, err
212217
}

0 commit comments

Comments
 (0)