Skip to content

Commit 0213f17

Browse files
add subpath to configmap mount
1 parent e80399e commit 0213f17

File tree

5 files changed

+54
-23
lines changed

5 files changed

+54
-23
lines changed

cmd/cisco-vk/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ func runVirtualKubelet(cmd *cobra.Command, args []string) error {
211211
if keyFile == "" {
212212
keyFile = tlsutil.DefaultKeyFile
213213
}
214-
tlsCfg, err := tlsutil.EnsureTLSConfig(certFile, keyFile, appCfg.Device.Address)
214+
tlsCfg, err := tlsutil.EnsureTLSConfig(certFile, keyFile, tlsutil.DefaultGenCertFile, tlsutil.DefaultGenKeyFile, appCfg.Device.Address)
215215
if err != nil {
216216
return fmt.Errorf("failed to configure kubelet TLS: %w", err)
217217
}

internal/controller/ciscodevice_controller.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ const (
4646
configMountPath = "/etc/virtual-kubelet"
4747
// configFileName is the key used inside the ConfigMap.
4848
configFileName = "config.yaml"
49+
// tlsGenMountPath is the writable directory where the VK process writes
50+
// its self-signed TLS certificate when no Secret-provided cert is found.
51+
// An emptyDir is mounted here so the path is writable even on a RORFS.
52+
varLibMountPath = "/var/lib/virtual-kubelet"
4953
// DefaultImage is the default container image for the VK deployment.
5054
DefaultImage = "ghcr.io/cisco/virtual-kubelet-cisco:latest"
5155
// DefaultServiceAccount is the shared service account used by all VK deployments.
@@ -182,9 +186,14 @@ func (r *CiscoDeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request)
182186
VolumeMounts: []corev1.VolumeMount{
183187
{
184188
Name: "device-config",
185-
MountPath: configMountPath,
189+
MountPath: configMountPath + "/" + configFileName,
190+
SubPath: configFileName,
186191
ReadOnly: true,
187192
},
193+
{
194+
Name: "tls-gen",
195+
MountPath: varLibMountPath,
196+
},
188197
},
189198
},
190199
},
@@ -199,6 +208,15 @@ func (r *CiscoDeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request)
199208
},
200209
},
201210
},
211+
{
212+
// emptyDir provides a writable scratch space for the
213+
// self-signed TLS cert generated at startup. Using an
214+
// explicit emptyDir ensures this works on a RORFS.
215+
Name: "tls-gen",
216+
VolumeSource: corev1.VolumeSource{
217+
EmptyDir: &corev1.EmptyDirVolumeSource{},
218+
},
219+
},
202220
},
203221
// Use shared service account with VK RBAC permissions
204222
ServiceAccountName: serviceAccount,

internal/controller/ciscodevice_controller_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ func TestReconcile_CreatesDeployment(t *testing.T) {
149149
if !found {
150150
t.Errorf("expected --nodename router-b in container args, got %v", args)
151151
}
152-
if len(deploy.Spec.Template.Spec.Containers[0].VolumeMounts) != 1 {
153-
t.Errorf("expected 1 volume mount, got %d", len(deploy.Spec.Template.Spec.Containers[0].VolumeMounts))
152+
if len(deploy.Spec.Template.Spec.Containers[0].VolumeMounts) != 2 {
153+
t.Errorf("expected 2 volume mounts (device-config, tls-gen), got %d", len(deploy.Spec.Template.Spec.Containers[0].VolumeMounts))
154154
}
155155
}
156156

internal/tlsutil/tls.go

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,40 +32,49 @@ import (
3232
)
3333

3434
const (
35-
// DefaultCertFile is the default path for the kubelet TLS certificate.
36-
// A Kubernetes Secret of type kubernetes.io/tls mounted at
37-
// /etc/virtual-kubelet/tls/ will populate this path automatically,
38-
// allowing Secret-provided certs to take precedence over generated ones.
35+
// DefaultCertFile is the preferred path for the kubelet TLS certificate.
36+
// Mount a Kubernetes Secret of type kubernetes.io/tls here to have it
37+
// take precedence over any generated certificate.
3938
DefaultCertFile = "/etc/virtual-kubelet/tls/tls.crt"
4039

41-
// DefaultKeyFile is the default path for the kubelet TLS private key.
40+
// DefaultKeyFile is the preferred path for the kubelet TLS private key.
4241
DefaultKeyFile = "/etc/virtual-kubelet/tls/tls.key"
42+
43+
// DefaultGenCertFile is the writable fallback path where a self-signed
44+
// certificate is written when the preferred path does not exist.
45+
// /var/lib is writable in containers even when /etc is read-only.
46+
DefaultGenCertFile = "/var/lib/virtual-kubelet/tls/tls.crt"
47+
48+
// DefaultGenKeyFile is the writable fallback path for the generated key.
49+
DefaultGenKeyFile = "/var/lib/virtual-kubelet/tls/tls.key"
4350
)
4451

4552
// EnsureTLSConfig returns a *tls.Config for the kubelet HTTPS listener.
4653
//
4754
// Behaviour:
4855
// - If both certFile and keyFile exist on disk, they are loaded and returned.
49-
// This lets credentials provisioned via a Kubernetes Secret mount take
50-
// precedence automatically -- no restart or reconfiguration required.
51-
// - If neither file exists, a self-signed ECDSA certificate is generated,
52-
// written to certFile/keyFile (parent directories are created as needed),
53-
// and returned. Persisting the files keeps the TLS fingerprint stable
54-
// across restarts.
55-
// - If exactly one file is present, an error is returned; this typically
56-
// indicates a partial or misconfigured Secret mount.
56+
// This is the expected path when a Kubernetes Secret of type
57+
// kubernetes.io/tls is mounted at the certFile/keyFile location.
58+
// - If neither certFile nor keyFile exists, a self-signed ECDSA certificate
59+
// is generated and written to genCertFile/genKeyFile (parent directories
60+
// are created as needed). Using a separate writable path for generation
61+
// allows certFile/keyFile to live under a read-only ConfigMap mount.
62+
// Persisting the generated files keeps the fingerprint stable across
63+
// restarts.
64+
// - If exactly one of certFile/keyFile is present, an error is returned;
65+
// this typically indicates a partial or misconfigured Secret mount.
5766
//
5867
// deviceAddr is added as a Subject Alternative Name when generating a
5968
// self-signed certificate so that both local and remote health checks pass.
60-
func EnsureTLSConfig(certFile, keyFile, deviceAddr string) (*tls.Config, error) {
69+
func EnsureTLSConfig(certFile, keyFile, genCertFile, genKeyFile, deviceAddr string) (*tls.Config, error) {
6170
certExists := fileExists(certFile)
6271
keyExists := fileExists(keyFile)
6372

6473
switch {
6574
case certExists && keyExists:
6675
return loadTLSConfig(certFile, keyFile)
6776
case !certExists && !keyExists:
68-
return generateAndWrite(certFile, keyFile, deviceAddr)
77+
return generateAndWrite(genCertFile, genKeyFile, deviceAddr)
6978
default:
7079
return nil, fmt.Errorf(
7180
"tls misconfiguration: only one of %q / %q is present; provide both or neither",

internal/tlsutil/tls_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func TestEnsureTLSConfig_Generate(t *testing.T) {
4545
certFile := filepath.Join(dir, "tls.crt")
4646
keyFile := filepath.Join(dir, "tls.key")
4747

48-
cfg, err := EnsureTLSConfig(certFile, keyFile, "")
48+
cfg, err := EnsureTLSConfig(certFile, keyFile, certFile, keyFile, "")
4949
if err != nil {
5050
t.Fatalf("EnsureTLSConfig() error = %v", err)
5151
}
@@ -64,6 +64,8 @@ func TestEnsureTLSConfig_Generate(t *testing.T) {
6464
func TestEnsureTLSConfig_IPAddressSAN(t *testing.T) {
6565
dir := t.TempDir()
6666
cfg, err := EnsureTLSConfig(
67+
filepath.Join(dir, "tls.crt"),
68+
filepath.Join(dir, "tls.key"),
6769
filepath.Join(dir, "tls.crt"),
6870
filepath.Join(dir, "tls.key"),
6971
"192.168.1.1",
@@ -91,6 +93,8 @@ func TestEnsureTLSConfig_IPAddressSAN(t *testing.T) {
9193
func TestEnsureTLSConfig_DNSSANHostname(t *testing.T) {
9294
dir := t.TempDir()
9395
cfg, err := EnsureTLSConfig(
96+
filepath.Join(dir, "tls.crt"),
97+
filepath.Join(dir, "tls.key"),
9498
filepath.Join(dir, "tls.crt"),
9599
filepath.Join(dir, "tls.key"),
96100
"device.example.com",
@@ -121,13 +125,13 @@ func TestEnsureTLSConfig_LoadFromDisk(t *testing.T) {
121125
keyFile := filepath.Join(dir, "tls.key")
122126

123127
// First call generates and writes the files.
124-
first, err := EnsureTLSConfig(certFile, keyFile, "")
128+
first, err := EnsureTLSConfig(certFile, keyFile, certFile, keyFile, "")
125129
if err != nil {
126130
t.Fatalf("first EnsureTLSConfig() error = %v", err)
127131
}
128132

129133
// Second call must load from disk — same cert bytes.
130-
second, err := EnsureTLSConfig(certFile, keyFile, "")
134+
second, err := EnsureTLSConfig(certFile, keyFile, certFile, keyFile, "")
131135
if err != nil {
132136
t.Fatalf("second EnsureTLSConfig() error = %v", err)
133137
}
@@ -152,7 +156,7 @@ func TestEnsureTLSConfig_MisconfigOnlyOneFile(t *testing.T) {
152156
t.Fatal(err)
153157
}
154158

155-
_, err := EnsureTLSConfig(certFile, keyFile, "")
159+
_, err := EnsureTLSConfig(certFile, keyFile, certFile, keyFile, "")
156160
if err == nil {
157161
t.Fatal("expected an error, got nil")
158162
}

0 commit comments

Comments
 (0)