diff --git a/hardeneks/__init__.py b/hardeneks/__init__.py index 0913747..275a63e 100644 --- a/hardeneks/__init__.py +++ b/hardeneks/__init__.py @@ -2,7 +2,6 @@ from pathlib import Path from pkg_resources import resource_filename import tempfile -import urllib3 import yaml from botocore.exceptions import EndpointConnectionError @@ -16,6 +15,7 @@ Resources, ) from .harden import harden +from hardeneks import helpers app = typer.Typer() @@ -66,14 +66,10 @@ def _get_cluster_name(context, region): def _get_region(): return boto3.session.Session().region_name - -def _load_kube_config(): - urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - kube_config_orig = f"{Path.home()}/.kube/config" +def _add_tls_verify(): + kubeconfig = helpers.get_kube_config() tmp_config = tempfile.NamedTemporaryFile().name - with open(kube_config_orig, "r") as fd: - kubeconfig = yaml.safe_load(fd) for cluster in kubeconfig["clusters"]: cluster["cluster"]["insecure-skip-tls-verify"] = True with open(tmp_config, "w") as fd: @@ -133,8 +129,9 @@ def run_hardeneks( """ if insecure_skip_tls_verify: - _load_kube_config() + _add_tls_verify() else: + # should pass in config file kubernetes.config.load_kube_config(context=context) context = _get_current_context(context) diff --git a/hardeneks/cluster_wide/scalability/control_plane.py b/hardeneks/cluster_wide/scalability/control_plane.py index 7cae80a..b972c65 100644 --- a/hardeneks/cluster_wide/scalability/control_plane.py +++ b/hardeneks/cluster_wide/scalability/control_plane.py @@ -1,9 +1,9 @@ import re -from rich.panel import Panel import kubernetes - +from rich.panel import Panel +from hardeneks import helpers from hardeneks import console -from ...resources import Resources +from hardeneks import Resources def check_EKS_version(resources: Resources): @@ -22,3 +22,28 @@ def check_EKS_version(resources: Resources): return False return True + +# +# check_kubectl_compression +# checks all clusters in config for disable-compression flag set to true +# if any cluster does not have setting, it returns False +def check_kubectl_compression(resources: Resources): + kubeconfig = helpers.get_kube_config() + isSetCorrectly = False + for cluster in kubeconfig.get("clusters", []): + clusterName = cluster.get("name", "") + if (resources.cluster in clusterName): + if cluster.get("cluster", {}).get("disable-compression", False) != True: + console.print( + Panel( + f"[red]`disable-compression` in Cluster {clusterName} should equal True", + subtitle="[link=https://aws.github.io/aws-eks-best-practices/scalability/docs/control-plane/#disable-kubectl-compression]Click to see the guide[/link]", + ) + ) + console.print() + else: + isSetCorrectly = True + break + + + return isSetCorrectly diff --git a/hardeneks/cluster_wide/scalability/skipped.json b/hardeneks/cluster_wide/scalability/skipped.json new file mode 100644 index 0000000..114447d --- /dev/null +++ b/hardeneks/cluster_wide/scalability/skipped.json @@ -0,0 +1,12 @@ +[{ + "name": "Limit workload and node bursting", + "link": "https://aws.github.io/aws-eks-best-practices/scalability/docs/control-plane/#limit-workload-and-node-bursting" +}, +{ + "name": "Scale nodes and pods down safely", + "link": "https://aws.github.io/aws-eks-best-practices/scalability/docs/control-plane/#scale-nodes-and-pods-down-safely" +}, +{ + "name": "Use Client-Side Cache when running Kubectl", + "link": "https://aws.github.io/aws-eks-best-practices/scalability/docs/control-plane/#use-client-side-cache-when-running-kubectl" +}] \ No newline at end of file diff --git a/hardeneks/config.yaml b/hardeneks/config.yaml index a1459cc..9eb5452 100644 --- a/hardeneks/config.yaml +++ b/hardeneks/config.yaml @@ -54,6 +54,7 @@ rules: scalability: control_plane: - check_EKS_version + - check_kubectl_compression namespace_based: security: iam: diff --git a/hardeneks/helpers.py b/hardeneks/helpers.py new file mode 100644 index 0000000..5f39fd8 --- /dev/null +++ b/hardeneks/helpers.py @@ -0,0 +1,17 @@ +from pathlib import Path +import urllib3 +import yaml + +# +# get_kube_config +# returns kube config in json +# +# we need to update this function to take in a config string, so users can pass in kubeconfig as a param +def get_kube_config(): + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + # need to fix this, so user can pass in .kube/config as a param (joshkurz) + kube_config_orig = f"{Path.home()}/.kube/config" + + with open(kube_config_orig, "r") as fd: + kubeconfig = yaml.safe_load(fd) + return kubeconfig \ No newline at end of file diff --git a/hardeneks/resources.py b/hardeneks/resources.py index 3097aab..08a64f4 100644 --- a/hardeneks/resources.py +++ b/hardeneks/resources.py @@ -1,6 +1,5 @@ from kubernetes import client - class Resources: def __init__(self, region, context, cluster, namespaces): self.region = region @@ -68,4 +67,4 @@ def set_resources(self): client.AutoscalingV1Api() .list_namespaced_horizontal_pod_autoscaler(self.namespace) .items - ) + ) \ No newline at end of file diff --git a/tests/test_scalability_control_plane.py b/tests/test_scalability_control_plane.py index 13ffaa0..9b11a2e 100644 --- a/tests/test_scalability_control_plane.py +++ b/tests/test_scalability_control_plane.py @@ -1,7 +1,11 @@ from hardeneks.resources import Resources from unittest.mock import patch +from hardeneks import helpers -from hardeneks.cluster_wide.scalability.control_plane import check_EKS_version +from hardeneks.cluster_wide.scalability.control_plane import ( + check_EKS_version, + check_kubectl_compression +) class Version: @@ -21,3 +25,21 @@ def test_check_EKS_version(mocked_client): assert check_EKS_version(namespaced_resources) mocked_client.return_value = Version("24") assert check_EKS_version(namespaced_resources) + +@patch(helpers.__name__ + ".get_kube_config") +def test_check_kubectl_compression(mocked_helpers): + namespaced_resources = Resources( + "some_region", "some_context", "foobarcluster", [] + ) + mocked_helpers.return_value = {'clusters': [{'cluster': {'server': 'testtest', 'disable-compression': True}, 'name': 'foobarcluster'}]} + assert check_kubectl_compression(namespaced_resources) + mocked_helpers.return_value = {'clusters': [{'cluster': {'server': 'testtest', 'disable-compression': True}, 'name': 'foobarcluster'}, {'cluster': {'server': 'testtest', 'disable-compression': False}, 'name': 'foobarcluster2'}]} + assert check_kubectl_compression(namespaced_resources) + mocked_helpers.return_value = {'clusters': [{'cluster': {'server': 'testtest', 'disable-compression': False}, 'name': 'foobarcluster'}, {'cluster': {'server': 'testtest', 'disable-compression': False}, 'name': 'foobarcluster4'}]} + assert not check_kubectl_compression(namespaced_resources) + mocked_helpers.return_value = {'clusters': [{'cluster': {'test': 'user'}, 'name': 'foobarcluster7'}]} + assert not check_kubectl_compression(namespaced_resources) + mocked_helpers.return_value = {'clusters': [{}]} + assert not check_kubectl_compression(namespaced_resources) + mocked_helpers.return_value = {} + assert not check_kubectl_compression(namespaced_resources) \ No newline at end of file