From 75a446bd3ede2db16ee35023f7b37a4bc4f5dde4 Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Tue, 6 Dec 2022 19:45:06 +0000 Subject: [PATCH 1/2] kubelet: Restrict access to tls private key This moves the `tlsPrivateKeyFile` to be under a `private` subdirectory. This allows us to keep the public cert files readable for non-system processes that expect to be able to read them while limiting access to the more sensitive private key. Signed-off-by: Sean McGinnis --- packages/kubernetes-1.21/etc-kubernetes-pki.mount | 6 +++--- packages/kubernetes-1.21/kubelet-config | 2 +- packages/kubernetes-1.22/etc-kubernetes-pki.mount | 6 +++--- packages/kubernetes-1.22/kubelet-config | 2 +- packages/kubernetes-1.23/etc-kubernetes-pki.mount | 6 +++--- packages/kubernetes-1.23/kubelet-config | 2 +- packages/kubernetes-1.24/etc-kubernetes-pki.mount | 4 ++-- packages/kubernetes-1.24/kubelet-config | 2 +- sources/models/shared-defaults/kubernetes-services.toml | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/kubernetes-1.21/etc-kubernetes-pki.mount b/packages/kubernetes-1.21/etc-kubernetes-pki.mount index 38c482deb44..584e61475d1 100644 --- a/packages/kubernetes-1.21/etc-kubernetes-pki.mount +++ b/packages/kubernetes-1.21/etc-kubernetes-pki.mount @@ -1,5 +1,5 @@ [Unit] -Description=Kubernetes PKI directory (/etc/kubernetes/pki) +Description=Kubernetes PKI private directory (/etc/kubernetes/pki/private) DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target @@ -8,9 +8,9 @@ Wants=selinux-policy-files.service [Mount] What=tmpfs -Where=/etc/kubernetes/pki +Where=/etc/kubernetes/pki/private Type=tmpfs Options=nosuid,nodev,noexec,noatime,context=system_u:object_r:secret_t:s0,mode=0700 [Install] -WantedBy=preconfigured.target \ No newline at end of file +WantedBy=preconfigured.target diff --git a/packages/kubernetes-1.21/kubelet-config b/packages/kubernetes-1.21/kubelet-config index b731e117998..5eb37d113f8 100644 --- a/packages/kubernetes-1.21/kubelet-config +++ b/packages/kubernetes-1.21/kubelet-config @@ -116,7 +116,7 @@ protectKernelDefaults: true serializeImagePulls: false {{#if (and (default "" settings.kubernetes.server-certificate) (default "" settings.kubernetes.server-key))}} tlsCertFile: "/etc/kubernetes/pki/kubelet-server.crt" -tlsPrivateKeyFile: "/etc/kubernetes/pki/kubelet-server.key" +tlsPrivateKeyFile: "/etc/kubernetes/pki/private/kubelet-server.key" {{else}} serverTLSBootstrap: {{settings.kubernetes.server-tls-bootstrap}} {{/if}} diff --git a/packages/kubernetes-1.22/etc-kubernetes-pki.mount b/packages/kubernetes-1.22/etc-kubernetes-pki.mount index 38c482deb44..584e61475d1 100644 --- a/packages/kubernetes-1.22/etc-kubernetes-pki.mount +++ b/packages/kubernetes-1.22/etc-kubernetes-pki.mount @@ -1,5 +1,5 @@ [Unit] -Description=Kubernetes PKI directory (/etc/kubernetes/pki) +Description=Kubernetes PKI private directory (/etc/kubernetes/pki/private) DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target @@ -8,9 +8,9 @@ Wants=selinux-policy-files.service [Mount] What=tmpfs -Where=/etc/kubernetes/pki +Where=/etc/kubernetes/pki/private Type=tmpfs Options=nosuid,nodev,noexec,noatime,context=system_u:object_r:secret_t:s0,mode=0700 [Install] -WantedBy=preconfigured.target \ No newline at end of file +WantedBy=preconfigured.target diff --git a/packages/kubernetes-1.22/kubelet-config b/packages/kubernetes-1.22/kubelet-config index d958ea21500..a24b0f98779 100644 --- a/packages/kubernetes-1.22/kubelet-config +++ b/packages/kubernetes-1.22/kubelet-config @@ -112,7 +112,7 @@ protectKernelDefaults: true serializeImagePulls: false {{#if (and (default "" settings.kubernetes.server-certificate) (default "" settings.kubernetes.server-key))}} tlsCertFile: "/etc/kubernetes/pki/kubelet-server.crt" -tlsPrivateKeyFile: "/etc/kubernetes/pki/kubelet-server.key" +tlsPrivateKeyFile: "/etc/kubernetes/pki/private/kubelet-server.key" {{else}} serverTLSBootstrap: {{settings.kubernetes.server-tls-bootstrap}} {{/if}} diff --git a/packages/kubernetes-1.23/etc-kubernetes-pki.mount b/packages/kubernetes-1.23/etc-kubernetes-pki.mount index 38c482deb44..584e61475d1 100644 --- a/packages/kubernetes-1.23/etc-kubernetes-pki.mount +++ b/packages/kubernetes-1.23/etc-kubernetes-pki.mount @@ -1,5 +1,5 @@ [Unit] -Description=Kubernetes PKI directory (/etc/kubernetes/pki) +Description=Kubernetes PKI private directory (/etc/kubernetes/pki/private) DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target @@ -8,9 +8,9 @@ Wants=selinux-policy-files.service [Mount] What=tmpfs -Where=/etc/kubernetes/pki +Where=/etc/kubernetes/pki/private Type=tmpfs Options=nosuid,nodev,noexec,noatime,context=system_u:object_r:secret_t:s0,mode=0700 [Install] -WantedBy=preconfigured.target \ No newline at end of file +WantedBy=preconfigured.target diff --git a/packages/kubernetes-1.23/kubelet-config b/packages/kubernetes-1.23/kubelet-config index 0446311d600..631b9d73849 100644 --- a/packages/kubernetes-1.23/kubelet-config +++ b/packages/kubernetes-1.23/kubelet-config @@ -114,7 +114,7 @@ protectKernelDefaults: true serializeImagePulls: false {{#if (and (default "" settings.kubernetes.server-certificate) (default "" settings.kubernetes.server-key))}} tlsCertFile: "/etc/kubernetes/pki/kubelet-server.crt" -tlsPrivateKeyFile: "/etc/kubernetes/pki/kubelet-server.key" +tlsPrivateKeyFile: "/etc/kubernetes/pki/private/kubelet-server.key" {{else}} serverTLSBootstrap: {{settings.kubernetes.server-tls-bootstrap}} {{/if}} diff --git a/packages/kubernetes-1.24/etc-kubernetes-pki.mount b/packages/kubernetes-1.24/etc-kubernetes-pki.mount index aad5fb37d05..584e61475d1 100644 --- a/packages/kubernetes-1.24/etc-kubernetes-pki.mount +++ b/packages/kubernetes-1.24/etc-kubernetes-pki.mount @@ -1,5 +1,5 @@ [Unit] -Description=Kubernetes PKI directory (/etc/kubernetes/pki) +Description=Kubernetes PKI private directory (/etc/kubernetes/pki/private) DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target @@ -8,7 +8,7 @@ Wants=selinux-policy-files.service [Mount] What=tmpfs -Where=/etc/kubernetes/pki +Where=/etc/kubernetes/pki/private Type=tmpfs Options=nosuid,nodev,noexec,noatime,context=system_u:object_r:secret_t:s0,mode=0700 diff --git a/packages/kubernetes-1.24/kubelet-config b/packages/kubernetes-1.24/kubelet-config index 4e670682f81..df0250882e2 100644 --- a/packages/kubernetes-1.24/kubelet-config +++ b/packages/kubernetes-1.24/kubelet-config @@ -113,7 +113,7 @@ protectKernelDefaults: true serializeImagePulls: false {{#if (and (default "" settings.kubernetes.server-certificate) (default "" settings.kubernetes.server-key))}} tlsCertFile: "/etc/kubernetes/pki/kubelet-server.crt" -tlsPrivateKeyFile: "/etc/kubernetes/pki/kubelet-server.key" +tlsPrivateKeyFile: "/etc/kubernetes/pki/private/kubelet-server.key" {{else}} serverTLSBootstrap: {{settings.kubernetes.server-tls-bootstrap}} {{/if}} diff --git a/sources/models/shared-defaults/kubernetes-services.toml b/sources/models/shared-defaults/kubernetes-services.toml index 562251bdac2..2a3b95e16ad 100644 --- a/sources/models/shared-defaults/kubernetes-services.toml +++ b/sources/models/shared-defaults/kubernetes-services.toml @@ -40,7 +40,7 @@ path = "/etc/kubernetes/pki/kubelet-server.crt" template-path = "/usr/share/templates/kubelet-server-crt" [configuration-files.kubelet-server-key] -path = "/etc/kubernetes/pki/kubelet-server.key" +path = "/etc/kubernetes/pki/private/kubelet-server.key" template-path = "/usr/share/templates/kubelet-server-key" [configuration-files.kubelet-exec-start-conf] From e1e35d4522c208541871f88a7aa22b493600a0c8 Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Thu, 8 Dec 2022 21:02:13 +0000 Subject: [PATCH 2/2] migrations: Add migration for kubelet private key path This adds a migration to update the `kubelet-server.key` file location used for Kubernetes PKI. This was moved from the common location with the public key to a separate private location so users would still be able to read the public key if needed. Signed-off-by: Sean McGinnis --- Release.toml | 4 +- sources/Cargo.lock | 8 ++ sources/Cargo.toml | 3 +- .../v1.12.0/k8s-private-pki-path/Cargo.toml | 13 +++ .../v1.12.0/k8s-private-pki-path/src/main.rs | 81 +++++++++++++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 sources/api/migration/migrations/v1.12.0/k8s-private-pki-path/Cargo.toml create mode 100644 sources/api/migration/migrations/v1.12.0/k8s-private-pki-path/src/main.rs diff --git a/Release.toml b/Release.toml index 53f5f45b4d2..463841b6f75 100644 --- a/Release.toml +++ b/Release.toml @@ -171,4 +171,6 @@ version = "1.12.0" "migrate_v1.11.0_public-control-container-v0-6-4.lz4", ] "(1.11.0, 1.11.1)" = [] -"(1.11.1, 1.12.0)" = [] +"(1.11.1, 1.12.0)" = [ + "migrate_v1.12.0_k8s-private-pki-path.lz4", +] diff --git a/sources/Cargo.lock b/sources/Cargo.lock index 5a3d559718f..db993f6d0cf 100644 --- a/sources/Cargo.lock +++ b/sources/Cargo.lock @@ -1972,6 +1972,14 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k8s-private-pki-path" +version = "0.1.0" +dependencies = [ + "migration-helpers", + "serde_json", +] + [[package]] name = "language-tags" version = "0.3.2" diff --git a/sources/Cargo.toml b/sources/Cargo.toml index b479dc273ac..c3d70773fc6 100644 --- a/sources/Cargo.toml +++ b/sources/Cargo.toml @@ -25,7 +25,8 @@ members = [ "api/prairiedog", # "api/migration/migrations/vX.Y.Z/..." - # (all migrations currently archived; replace this line with new ones) + # (all previous migrations archived; add new ones after this line) + "api/migration/migrations/v1.12.0/k8s-private-pki-path", "bottlerocket-release", diff --git a/sources/api/migration/migrations/v1.12.0/k8s-private-pki-path/Cargo.toml b/sources/api/migration/migrations/v1.12.0/k8s-private-pki-path/Cargo.toml new file mode 100644 index 00000000000..7148c291242 --- /dev/null +++ b/sources/api/migration/migrations/v1.12.0/k8s-private-pki-path/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "k8s-private-pki-path" +version = "0.1.0" +authors = ["Sean McGinnis "] +license = "Apache-2.0 OR MIT" +edition = "2018" +publish = false +# Don't rebuild crate just because of changes to README. +exclude = ["README.md"] + +[dependencies] +migration-helpers = { path = "../../../migration-helpers", version = "0.1.0"} +serde_json = "1.0" diff --git a/sources/api/migration/migrations/v1.12.0/k8s-private-pki-path/src/main.rs b/sources/api/migration/migrations/v1.12.0/k8s-private-pki-path/src/main.rs new file mode 100644 index 00000000000..6c9cc80c193 --- /dev/null +++ b/sources/api/migration/migrations/v1.12.0/k8s-private-pki-path/src/main.rs @@ -0,0 +1,81 @@ +#![deny(rust_2018_idioms)] + +use migration_helpers::{migrate, Migration, MigrationData, Result}; +use std::process; + +const SETTING: &'static str = "configuration-files.kubelet-server-key.path"; +const OLD_VALUE: &'static str = "/etc/kubernetes/pki/kubelet-server.key"; +const NEW_VALUE: &'static str = "/etc/kubernetes/pki/private/kubelet-server.key"; + +/// We moved the render output location for the kubelet PKI private key to be in a restricted +/// subdirectory. We need to update this output path in the stored configuration so updated nodes +/// pick up the change. +fn run() -> Result<()> { + migrate(KubeletServerKey {}) +} + +pub struct KubeletServerKey {} + +impl KubeletServerKey { + fn migrate(&mut self, mut input: MigrationData, action: &'static str) -> Result { + let old_value; + let new_value; + if action == "upgrade" { + old_value = OLD_VALUE; + new_value = NEW_VALUE; + } else { + // Downgrade: everything old is new again + old_value = NEW_VALUE; + new_value = OLD_VALUE; + } + + if let Some(data) = input.data.get_mut(SETTING) { + match data { + serde_json::Value::String(current_value) => { + if current_value == old_value { + *data = new_value.into(); + println!( + "Changed '{}' from {:?} to {:?} on {}", + SETTING, old_value, new_value, action + ); + } else { + println!( + "'{}' is already set to {:?}, leaving alone", + SETTING, new_value + ); + } + } + _ => { + println!( + "'{}' is set to non-string value '{}'; KubeletServerKey only handles strings", + SETTING, data + ); + } + } + } else { + println!("Found no setting '{}'", SETTING); + } + + Ok(input) + } +} + +impl Migration for KubeletServerKey { + fn forward(&mut self, input: MigrationData) -> Result { + self.migrate(input, "upgrade") + } + + fn backward(&mut self, input: MigrationData) -> Result { + self.migrate(input, "downgrade") + } +} + +// Returning a Result from main makes it print a Debug representation of the error, but with Snafu +// we have nice Display representations of the error, so we wrap "main" (run) and print any error. +// https://github.com/shepmaster/snafu/issues/110 +fn main() { + if let Err(e) = run() { + eprintln!("{}", e); + process::exit(1); + } +}