Skip to content
This repository was archived by the owner on Apr 11, 2024. It is now read-only.

Commit 9cad889

Browse files
authored
Merge pull request #4 from dlipovetsky/users
feat: Add user configuration for all providers
2 parents 92b06d4 + dfd5cbe commit 9cad889

File tree

12 files changed

+620
-1
lines changed

12 files changed

+620
-1
lines changed

api/v1alpha1/clusterconfig_types.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ type GenericClusterConfig struct {
9898

9999
// +optional
100100
Addons *Addons `json:"addons,omitempty"`
101+
102+
// +optional
103+
Users Users `json:"users,omitempty"`
101104
}
102105

103106
func (s GenericClusterConfig) VariableSchema() clusterv1.VariableSchema { //nolint:gocritic,lll // Passed by value for no potential side-effect.
@@ -116,6 +119,7 @@ func (s GenericClusterConfig) VariableSchema() clusterv1.VariableSchema { //noli
116119
OpenAPIV3Schema,
117120
"imageRegistries": ImageRegistries{}.VariableSchema().OpenAPIV3Schema,
118121
"globalImageRegistryMirror": GlobalImageRegistryMirror{}.VariableSchema().OpenAPIV3Schema,
122+
"users": Users{}.VariableSchema().OpenAPIV3Schema,
119123
},
120124
},
121125
}
@@ -344,6 +348,83 @@ func (ImageRegistries) VariableSchema() clusterv1.VariableSchema {
344348
}
345349
}
346350

351+
type Users []User
352+
353+
func (Users) VariableSchema() clusterv1.VariableSchema {
354+
return clusterv1.VariableSchema{
355+
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
356+
Description: "Users to add to the machine",
357+
Type: "array",
358+
Items: ptr.To(User{}.VariableSchema().OpenAPIV3Schema),
359+
},
360+
}
361+
}
362+
363+
// User defines the input for a generated user in cloud-init.
364+
type User struct {
365+
// Name specifies the user name.
366+
Name string `json:"name"`
367+
368+
// HashedPassword is a hashed password for the user, formatted as described
369+
// by the crypt(5) man page. See your distribution's documentation for
370+
// instructions to create a hashed password.
371+
// An empty string is not marshalled, because it is not a valid value.
372+
// +optional
373+
HashedPassword string `json:"hashedPassword,omitempty"`
374+
375+
// SSHAuthorizedKeys is a list of public SSH keys to write to the
376+
// machine. Use the corresponding private SSH keys to authenticate. See SSH
377+
// documentation for instructions to create a key pair.
378+
// +optional
379+
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"`
380+
381+
// Sudo is a sudo user specification, formatted as described in the sudo
382+
// documentation.
383+
// An empty string is not marshalled, because it is not a valid value.
384+
// +optional
385+
Sudo string `json:"sudo,omitempty"`
386+
}
387+
388+
func (User) VariableSchema() clusterv1.VariableSchema {
389+
return clusterv1.VariableSchema{
390+
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
391+
Type: "object",
392+
Required: []string{"name"},
393+
Properties: map[string]clusterv1.JSONSchemaProps{
394+
"name": {
395+
Description: "The username",
396+
Type: "string",
397+
},
398+
"hashedPassword": {
399+
Description: "The hashed password for the user. Must be in the format of some hash function supported by the OS.",
400+
Type: "string",
401+
// The crypt (5) man page lists regexes for supported hash
402+
// functions. We could validate input against a set of
403+
// regexes, but because the set may be different from the
404+
// set supported by the chosen OS, we might return a false
405+
// negative or positive. For this reason, we do not validate
406+
// the input.
407+
},
408+
"sshAuthorizedKeys": {
409+
Description: "A list of SSH authorized keys for this user",
410+
Type: "array",
411+
Items: &clusterv1.JSONSchemaProps{
412+
// No description, because the one for the parent array is enough.
413+
Type: "string",
414+
},
415+
},
416+
"sudo": {
417+
Description: "The sudo rule that applies to this user",
418+
Type: "string",
419+
// A sudo rule is defined using an EBNF grammar, and must be
420+
// parsed to be validated. We have decided to not integrate
421+
// a sudo rule parser, so we do not validate the input.
422+
},
423+
},
424+
},
425+
}
426+
}
427+
347428
func init() {
348429
SchemeBuilder.Register(&ClusterConfig{})
349430
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
+++
2+
title = "Users"
3+
+++
4+
5+
Configure users for all machines in the cluster, the user's superuser capabilities using `sudo` user specifications, and
6+
the login authentication mechanism.
7+
8+
> - SSH _authorized keys_ are just public SSH keys that are used to authenticate a login. See the [SSH man
9+
> page](https://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT) for more information.
10+
>
11+
> - For information on sudo user specifications, see the [sudo
12+
> documentation](https://www.sudo.ws/docs/man/sudoers.man/#User_specification).
13+
>
14+
> - Local password authentication is disabled for the user by default. It is enabled only when a hashed password is
15+
> provided.
16+
17+
## Examples
18+
19+
### Admin user with SSH public key login
20+
21+
Creates a user with the name `admin`, grants the user the ability to run any command as the superuser, and allows you to
22+
login via SSH using the username and private key corresponding to the authorized public key.
23+
24+
```yaml
25+
apiVersion: cluster.x-k8s.io/v1beta1
26+
kind: Cluster
27+
metadata:
28+
name: <NAME>
29+
spec:
30+
topology:
31+
variables:
32+
- name: clusterConfig
33+
value:
34+
users:
35+
- name: admin
36+
- sshAuthorizedKeys:
37+
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAua0lo8BiGWgvIiDCKnQDKL5uERHfnehm0ns5CEJpJw optionalcomment"
38+
sudo: "ALL=(ALL) NOPASSWD:ALL"
39+
```
40+
41+
### Admin user with serial console password login
42+
43+
Creates a user with the name `admin,` grants the user the ability to run any command as the superuser, and allows you to
44+
login via serial console using the username and password.
45+
46+
> Note that this does not allow you to login via SSH using the username and password; in most cases, you must also
47+
> configure the SSH server to allow password authentication.
48+
49+
```yaml
50+
apiVersion: cluster.x-k8s.io/v1beta1
51+
kind: Cluster
52+
metadata:
53+
name: <NAME>
54+
spec:
55+
topology:
56+
variables:
57+
- name: clusterConfig
58+
value:
59+
users:
60+
- name: admin
61+
hashedPassword: "$y$j9T$UraH8eN4XvapXBmmSaUrP0$Nyxdf1cJDGZcp0WDKu.CFHprrkPG4ubirqSqiD43Ix3"
62+
sudo: "ALL=(ALL) NOPASSWD:ALL"
63+
```

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api v0.0.0-00010101000000-000000000000
1616
github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common v0.0.0-00010101000000-000000000000
1717
github.com/go-logr/logr v1.4.1
18+
github.com/google/go-cmp v0.6.0
1819
github.com/onsi/ginkgo/v2 v2.16.0
1920
github.com/onsi/gomega v1.31.1
2021
github.com/spf13/pflag v1.0.5
@@ -71,7 +72,6 @@ require (
7172
github.com/golang/protobuf v1.5.4 // indirect
7273
github.com/google/cel-go v0.17.7 // indirect
7374
github.com/google/gnostic-models v0.6.8 // indirect
74-
github.com/google/go-cmp v0.6.0 // indirect
7575
github.com/google/go-github/v53 v53.2.0 // indirect
7676
github.com/google/go-querystring v1.1.0 // indirect
7777
github.com/google/gofuzz v1.2.0 // indirect

pkg/handlers/aws/mutation/metapatch_handler_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import (
3737
kubernetesimagerepositorytests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository/tests"
3838
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors"
3939
globalimageregistrymirrortests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors/tests"
40+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/users"
41+
userstests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/users/tests"
4042
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig"
4143
)
4244

@@ -188,4 +190,11 @@ func TestGeneratePatches(t *testing.T) {
188190
v1alpha1.AWSVariableName,
189191
controlplaneloadbalancer.VariableName,
190192
)
193+
194+
userstests.TestGeneratePatches(
195+
t,
196+
metaPatchGeneratorFunc(mgr),
197+
clusterconfig.MetaVariableName,
198+
users.VariableName,
199+
)
191200
}

pkg/handlers/docker/mutation/metapatch_handler_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
kubernetesimagerepositorytests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository/tests"
2828
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors"
2929
globalimageregistrymirrortests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors/tests"
30+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/users"
31+
userstests "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/users/tests"
3032
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/workerconfig"
3133
)
3234

@@ -112,4 +114,11 @@ func TestGeneratePatches(t *testing.T) {
112114
clusterconfig.MetaVariableName,
113115
mirrors.GlobalMirrorVariableName,
114116
)
117+
118+
userstests.TestGeneratePatches(
119+
t,
120+
metaPatchGeneratorFunc(mgr),
121+
clusterconfig.MetaVariableName,
122+
users.VariableName,
123+
)
115124
}

pkg/handlers/generic/mutation/handlers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries/credentials"
1616
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository"
1717
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors"
18+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/users"
1819
)
1920

2021
// MetaMutators returns all generic patch handlers.
@@ -28,5 +29,6 @@ func MetaMutators(mgr manager.Manager) []mutation.MetaMutator {
2829
credentials.NewPatch(mgr.GetClient()),
2930
mirrors.NewPatch(mgr.GetClient()),
3031
calico.NewPatch(),
32+
users.NewPatch(),
3133
}
3234
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package users

0 commit comments

Comments
 (0)