Skip to content

Commit 0e2689d

Browse files
authored
Merge pull request #325 from arangodb/feature/bootstrap-root-pwd
Randomize Root Password
2 parents 1a82707 + e9f0469 commit 0e2689d

File tree

10 files changed

+424
-0
lines changed

10 files changed

+424
-0
lines changed

docs/Manual/Deployment/Kubernetes/DeploymentResource.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,18 @@ This setting specifies the name of a kubernetes `Secret` that contains
342342
the license key token used for enterprise images. This value is not used for
343343
the community edition.
344344

345+
### `spec.bootstrap.passwordSecretNames.root: string`
346+
347+
This setting specifies a secret name for the credentials of the root user.
348+
349+
When a deployment is created the operator will setup the root user account
350+
according to the credentials given by the secret. If the secret doesn't exist
351+
the operator creates a secret with a random password.
352+
353+
There are two magic values for the secret name:
354+
- `None` specifies no action. This disables root password randomization. This is the default value. (Thus the root password is empty - not recommended)
355+
- `Auto` specifies automatic name generation, which is `<deploymentname>-root-password`.
356+
345357
### `spec.<group>.count: number`
346358

347359
This setting specifies the number of servers to start for the given group.
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2018 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+
21+
package v1alpha
22+
23+
import (
24+
"fmt"
25+
26+
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
27+
)
28+
29+
const (
30+
// UserNameRoot root user name
31+
UserNameRoot = "root"
32+
)
33+
34+
// PasswordSecretName contains user password secret name
35+
type PasswordSecretName string
36+
37+
const (
38+
// PasswordSecretNameNone is magic value for no action
39+
PasswordSecretNameNone PasswordSecretName = "None"
40+
// PasswordSecretNameAuto is magic value for autogenerate name
41+
PasswordSecretNameAuto PasswordSecretName = "Auto"
42+
)
43+
44+
// PasswordSecretNameList is a map from username to secretnames
45+
type PasswordSecretNameList map[string]PasswordSecretName
46+
47+
// BootstrapSpec contains information for cluster bootstrapping
48+
type BootstrapSpec struct {
49+
// PasswordSecretNames contains a map of username to password-secret-name
50+
PasswordSecretNames PasswordSecretNameList `json:"passwordSecretNames,omitempty"`
51+
}
52+
53+
// IsNone returns true if p is None or p is empty
54+
func (p PasswordSecretName) IsNone() bool {
55+
return p == PasswordSecretNameNone || p == ""
56+
}
57+
58+
// IsAuto returns true if p is Auto
59+
func (p PasswordSecretName) IsAuto() bool {
60+
return p == PasswordSecretNameAuto
61+
}
62+
63+
// GetSecretName returns the secret name given by the specs. Or None if not set.
64+
func (s PasswordSecretNameList) GetSecretName(user string) PasswordSecretName {
65+
if s != nil {
66+
if secretname, ok := s[user]; ok {
67+
return secretname
68+
}
69+
}
70+
return PasswordSecretNameNone
71+
}
72+
73+
// getSecretNameForUserPassword returns the default secret name for the given user
74+
func getSecretNameForUserPassword(deploymentname, username string) PasswordSecretName {
75+
return PasswordSecretName(k8sutil.FixupResourceName(deploymentname + "-" + username + "-password"))
76+
}
77+
78+
// Validate the specification.
79+
func (b *BootstrapSpec) Validate() error {
80+
for username, secretname := range b.PasswordSecretNames {
81+
// Remove this restriction as soon as we can bootstrap databases
82+
if username != UserNameRoot {
83+
return fmt.Errorf("only username `root` allowed in passwordSecretNames")
84+
}
85+
86+
if secretname.IsNone() {
87+
if username != UserNameRoot {
88+
return fmt.Errorf("magic value None not allowed for %s", username)
89+
}
90+
} else {
91+
if err := k8sutil.ValidateResourceName(string(secretname)); err != nil {
92+
return maskAny(err)
93+
}
94+
}
95+
}
96+
97+
return nil
98+
}
99+
100+
// SetDefaults fills in default values when a field is not specified.
101+
func (b *BootstrapSpec) SetDefaults(deploymentname string) {
102+
if b.PasswordSecretNames == nil {
103+
b.PasswordSecretNames = make(map[string]PasswordSecretName)
104+
}
105+
106+
// If root is not set init with Auto
107+
if _, ok := b.PasswordSecretNames[UserNameRoot]; !ok {
108+
b.PasswordSecretNames[UserNameRoot] = PasswordSecretNameAuto
109+
}
110+
111+
// Replace Auto with generated secret name
112+
for user, secretname := range b.PasswordSecretNames {
113+
if secretname.IsAuto() {
114+
b.PasswordSecretNames[user] = getSecretNameForUserPassword(deploymentname, user)
115+
}
116+
}
117+
}
118+
119+
// NewPasswordSecretNameListOrNil returns nil if input is nil, otherwise returns a clone of the given value.
120+
func NewPasswordSecretNameListOrNil(list PasswordSecretNameList) PasswordSecretNameList {
121+
if list == nil {
122+
return nil
123+
}
124+
var newList = make(PasswordSecretNameList)
125+
for k, v := range list {
126+
newList[k] = v
127+
}
128+
return newList
129+
}
130+
131+
// SetDefaultsFrom fills unspecified fields with a value from given source spec.
132+
func (b *BootstrapSpec) SetDefaultsFrom(source BootstrapSpec) {
133+
if b.PasswordSecretNames == nil {
134+
b.PasswordSecretNames = NewPasswordSecretNameListOrNil(source.PasswordSecretNames)
135+
}
136+
}

pkg/apis/deployment/v1alpha/conditions.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const (
5353
ConditionTypeSecretsChanged ConditionType = "SecretsChanged"
5454
// ConditionTypeMemberOfCluster indicates that the member is a known member of the ArangoDB cluster.
5555
ConditionTypeMemberOfCluster ConditionType = "MemberOfCluster"
56+
// ConditionTypeBootstrapCompleted indicates that the initial cluster bootstrap has been completed.
57+
ConditionTypeBootstrapCompleted ConditionType = "BootstrapCompleted"
58+
// ConditionTypeBootstrapSucceded indicates that the initial cluster bootstrap completed successfully.
59+
ConditionTypeBootstrapSucceded ConditionType = "BootstrapSucceded"
5660
// ConditionTypeTerminating indicates that the member is terminating but not yet terminated.
5761
ConditionTypeTerminating ConditionType = "Terminating"
5862
)

pkg/apis/deployment/v1alpha/deployment_spec.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ type DeploymentSpec struct {
7070
SyncWorkers ServerGroupSpec `json:"syncworkers"`
7171

7272
Chaos ChaosSpec `json:"chaos"`
73+
74+
Bootstrap BootstrapSpec `json:"bootstrap",omitempty`
7375
}
7476

7577
// Equal compares two DeploymentSpec
@@ -188,6 +190,7 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) {
188190
s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode())
189191
s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode())
190192
s.Chaos.SetDefaults()
193+
s.Bootstrap.SetDefaults(deploymentName)
191194
}
192195

193196
// SetDefaultsFrom fills unspecified fields with a value from given source spec.
@@ -226,6 +229,7 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) {
226229
s.SyncMasters.SetDefaultsFrom(source.SyncMasters)
227230
s.SyncWorkers.SetDefaultsFrom(source.SyncWorkers)
228231
s.Chaos.SetDefaultsFrom(source.Chaos)
232+
s.Bootstrap.SetDefaultsFrom(source.Bootstrap)
229233
}
230234

231235
// Validate the specification.
@@ -285,6 +289,9 @@ func (s *DeploymentSpec) Validate() error {
285289
if err := s.License.Validate(); err != nil {
286290
return maskAny(errors.Wrap(err, "spec.licenseKey"))
287291
}
292+
if err := s.Bootstrap.Validate(); err != nil {
293+
return maskAny(err)
294+
}
288295
return nil
289296
}
290297

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

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

0 commit comments

Comments
 (0)