From bef79ec65add810f852d66d42c5a58ea90587a36 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Thu, 23 Feb 2023 16:23:14 -0500 Subject: [PATCH] feat: Add check `K8S008` for detecting the use of the Docker socket (#26) --- eksup/src/analysis.rs | 6 ++-- eksup/src/finding.rs | 4 +++ eksup/src/k8s/checks.rs | 6 ++-- eksup/src/k8s/findings.rs | 10 +++++-- eksup/src/k8s/resources.rs | 44 +++++++++++++++++++++++++++- eksup/src/playbook.rs | 2 ++ eksup/src/version.rs | 6 ++++ eksup/templates/playbook.md | 3 ++ examples/test-mixed_v1.24_upgrade.md | 16 +++++++++- 9 files changed, 89 insertions(+), 8 deletions(-) diff --git a/eksup/src/analysis.rs b/eksup/src/analysis.rs index 87fcce3..f21224f 100644 --- a/eksup/src/analysis.rs +++ b/eksup/src/analysis.rs @@ -2,7 +2,7 @@ use anyhow::Result; use aws_sdk_eks::model::Cluster; use serde::{Deserialize, Serialize}; -use crate::{eks, finding::Findings, k8s}; +use crate::{eks, finding::Findings, k8s, version}; /// Container of all findings collected #[derive(Debug, Serialize, Deserialize)] @@ -37,6 +37,7 @@ impl Results { output.push_str(&self.kubernetes.pod_topology_distribution.to_stdout_table()?); output.push_str(&self.kubernetes.readiness_probe.to_stdout_table()?); output.push_str(&self.kubernetes.termination_grace_period.to_stdout_table()?); + output.push_str(&self.kubernetes.docker_socket.to_stdout_table()?); Ok(output) } @@ -52,13 +53,14 @@ pub(crate) async fn analyze(aws_shared_config: &aws_config::SdkConfig, cluster: let cluster_name = cluster.name().unwrap(); let cluster_version = cluster.version().unwrap(); + let target_version = version::get_target_version(cluster_version)?; let cluster_findings = eks::get_cluster_findings(cluster).await?; let subnet_findings = eks::get_subnet_findings(&ec2_client, &k8s_client, cluster).await?; let addon_findings = eks::get_addon_findings(&eks_client, cluster_name, cluster_version).await?; let dataplane_findings = eks::get_data_plane_findings(&asg_client, &ec2_client, &eks_client, &k8s_client, cluster).await?; - let kubernetes_findings = k8s::get_kubernetes_findings(&k8s_client).await?; + let kubernetes_findings = k8s::get_kubernetes_findings(&k8s_client, &target_version).await?; Ok(Results { cluster: cluster_findings, diff --git a/eksup/src/finding.rs b/eksup/src/finding.rs index 01c8ec4..d2811b5 100644 --- a/eksup/src/finding.rs +++ b/eksup/src/finding.rs @@ -139,6 +139,9 @@ pub enum Code { /// `pod.spec.TerminationGracePeriodSeconds` is set to zero K8S007, + + /// Mounts `docker.sock` or `dockershim.sock` + K8S008, } impl std::fmt::Display for Code { @@ -163,6 +166,7 @@ impl std::fmt::Display for Code { Code::K8S005 => write!(f, "K8S005"), Code::K8S006 => write!(f, "K8S006"), Code::K8S007 => write!(f, "K8S007"), + Code::K8S008 => write!(f, "K8S008"), } } } diff --git a/eksup/src/k8s/checks.rs b/eksup/src/k8s/checks.rs index ea69a8c..8a5af61 100644 --- a/eksup/src/k8s/checks.rs +++ b/eksup/src/k8s/checks.rs @@ -368,6 +368,8 @@ pub struct DockerSocket { #[tabled(inline)] pub resource: Resource, + + pub docker_socket: bool, } impl Findings for Vec { @@ -420,6 +422,6 @@ pub trait K8sFindings { /// K8S007 - check if StatefulSets have terminationGracePeriodSeconds == 0 fn termination_grace_period(&self) -> Option; - // /// K8S008 - check if resources use the Docker socket - // fn docker_socket(&self) -> Option; + /// K8S008 - check if resources use the Docker socket + fn docker_socket(&self, target_version: &str) -> Option; } diff --git a/eksup/src/k8s/findings.rs b/eksup/src/k8s/findings.rs index ce03bde..7e6b9d0 100644 --- a/eksup/src/k8s/findings.rs +++ b/eksup/src/k8s/findings.rs @@ -14,19 +14,24 @@ pub struct KubernetesFindings { pub readiness_probe: Vec, pub pod_topology_distribution: Vec, pub termination_grace_period: Vec, + pub docker_socket: Vec, } -pub async fn get_kubernetes_findings(k8s_client: &K8sClient) -> Result { +pub async fn get_kubernetes_findings(k8s_client: &K8sClient, target_version: &str) -> Result { let resources = resources::get_resources(k8s_client).await?; let min_replicas: Vec = resources.iter().filter_map(|s| s.min_replicas()).collect(); let min_ready_seconds: Vec = resources.iter().filter_map(|s| s.min_ready_seconds()).collect(); - let readiness_probe: Vec = resources.iter().filter_map(|s| s.readiness_probe()).collect(); let pod_topology_distribution: Vec = resources.iter().filter_map(|s| s.pod_topology_distribution()).collect(); + let readiness_probe: Vec = resources.iter().filter_map(|s| s.readiness_probe()).collect(); let termination_grace_period: Vec = resources.iter().filter_map(|s| s.termination_grace_period()).collect(); + let docker_socket: Vec = resources + .iter() + .filter_map(|s| s.docker_socket(target_version)) + .collect(); Ok(KubernetesFindings { min_replicas, @@ -34,5 +39,6 @@ pub async fn get_kubernetes_findings(k8s_client: &K8sClient) -> Result None, } } + + fn docker_socket(&self, target_version: &str) -> Option { + let pod_template = self.spec.template.to_owned(); + + let target_version = version::parse_minor(target_version).unwrap(); + if target_version > 24 { + // From 1.25+, there shouldn't be any further action required + return None; + } + let remediation = if target_version < 24 { + finding::Remediation::Recommended + } else { + finding::Remediation::Required + }; + + match pod_template { + Some(pod_template) => { + let containers = pod_template.spec.unwrap_or_default().containers; + + for container in containers { + let volume_mounts = container.volume_mounts.unwrap_or_default(); + for volume_mount in volume_mounts { + if volume_mount.mount_path.contains("docker.sock") || volume_mount.mount_path.contains("dockershim.sock") { + let finding = finding::Finding { + code: finding::Code::K8S008, + symbol: remediation.symbol(), + remediation, + }; + + return Some(checks::DockerSocket { + finding, + resource: self.get_resource(), + docker_socket: true, + }); + } + } + } + None + } + None => None, + } + } } pub async fn get_resources(client: &Client) -> Result> { diff --git a/eksup/src/playbook.rs b/eksup/src/playbook.rs index d80d2f1..f273f3d 100644 --- a/eksup/src/playbook.rs +++ b/eksup/src/playbook.rs @@ -60,6 +60,7 @@ pub struct TemplateData { pod_topology_distribution: String, readiness_probe: String, termination_grace_period: String, + docker_socket: String, } fn get_release_data() -> Result> { @@ -183,6 +184,7 @@ pub(crate) fn create(args: &Playbook, cluster: &Cluster, analysis: analysis::Res pod_topology_distribution: kubernetes_findings.pod_topology_distribution.to_markdown_table("\t")?, readiness_probe: kubernetes_findings.readiness_probe.to_markdown_table("\t")?, termination_grace_period: kubernetes_findings.termination_grace_period.to_markdown_table("\t")?, + docker_socket: kubernetes_findings.docker_socket.to_markdown_table("\t")?, }; let filename = match &args.filename { diff --git a/eksup/src/version.rs b/eksup/src/version.rs index 1645af7..dac8d58 100644 --- a/eksup/src/version.rs +++ b/eksup/src/version.rs @@ -8,6 +8,12 @@ use serde::{Deserialize, Serialize}; /// Latest support version pub const LATEST: &str = "1.25"; +#[derive(Debug, Serialize, Deserialize)] +pub struct Versions { + pub current: String, + pub target: String, +} + seq!(N in 20..=24 { /// Kubernetes version(s) supported #[derive(Clone, Copy, Debug, Serialize, Deserialize)] diff --git a/eksup/templates/playbook.md b/eksup/templates/playbook.md index 75aa90f..8dc0b01 100644 --- a/eksup/templates/playbook.md +++ b/eksup/templates/playbook.md @@ -221,6 +221,9 @@ When upgrading the control plane, Amazon EKS performs standard infrastructure an #### Check [[K8S007]](https://clowdhaus.github.io/eksup/process/checks/#k8s007) {{ termination_grace_period }} + #### Check [[K8S008]](https://clowdhaus.github.io/eksup/process/checks/#k8s008) +{{ docker_socket }} + 2. Inspect [AWS service quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) before upgrading. Accounts that are multi-tenant or already have a number of resources provisioned may be at risk of hitting service quota limits which will cause the cluster upgrade to fail, or impede the upgrade process. {{#if pod_ips}} diff --git a/examples/test-mixed_v1.24_upgrade.md b/examples/test-mixed_v1.24_upgrade.md index cd48404..97011a9 100644 --- a/examples/test-mixed_v1.24_upgrade.md +++ b/examples/test-mixed_v1.24_upgrade.md @@ -73,8 +73,8 @@ #### Check [[K8S001]](https://clowdhaus.github.io/eksup/process/checks/#k8s001) | CHECK | | NODE | CONTROL PLANE | SKEW | QUANTITY | |--------|----|-------|---------------|------|----------| - | K8S001 | ⚠️ | v1.22 | v1.23 | +1 | 2 | | K8S001 | ❌ | v1.21 | v1.23 | +2 | 2 | + | K8S001 | ⚠️ | v1.22 | v1.23 | +1 | 2 | | | NAME | NODE | CONTROL PLANE | SKEW | |----|-----------------------------|-------|---------------|------| @@ -248,6 +248,20 @@ When upgrading the control plane, Amazon EKS performs standard infrastructure an | ❌ | bad-ss | statefulset | StatefulSet | 0 | + #### Check [[K8S008]](https://clowdhaus.github.io/eksup/process/checks/#k8s008) + | | NAME | NAMESPACE | KIND | DOCKERSOCKET | + |----|-------------------|-------------|-------------|--------------| + | ❌ | bad-cron | cronjob | CronJob | true | + | ❌ | bad-ds | daemonset | DaemonSet | true | + | ❌ | aws-node | kube-system | DaemonSet | true | + | ❌ | bad-dpl | deployment | Deployment | true | + | ❌ | bad-cron-27953110 | cronjob | Job | true | + | ❌ | bad-cron-27953115 | cronjob | Job | true | + | ❌ | bad-cron-27953120 | cronjob | Job | true | + | ❌ | bad-job | job | Job | true | + | ❌ | bad-ss | statefulset | StatefulSet | true | + + 2. Inspect [AWS service quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) before upgrading. Accounts that are multi-tenant or already have a number of resources provisioned may be at risk of hitting service quota limits which will cause the cluster upgrade to fail, or impede the upgrade process. 3. Verify that there is sufficient IP space available to the pods running in the cluster when using custom networking. With the in-place, surge upgrade process, there will be higher IP consumption during the upgrade.