{"id":13626000,"url":"https://github.com/aws-samples/hardeneks","last_synced_at":"2026-04-02T00:27:54.644Z","repository":{"id":64734191,"uuid":"576434460","full_name":"aws-samples/hardeneks","owner":"aws-samples","description":"Runs checks to see if an EKS cluster follows EKS Best Practices.","archived":false,"fork":false,"pushed_at":"2026-03-23T04:16:54.000Z","size":5242,"stargazers_count":948,"open_issues_count":8,"forks_count":92,"subscribers_count":8,"default_branch":"main","last_synced_at":"2026-03-23T21:25:02.715Z","etag":null,"topics":["aws","best-practices","eks","k8s"],"latest_commit_sha":null,"homepage":"https://aws-samples.github.io/hardeneks/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit-0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aws-samples.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-12-09T21:17:30.000Z","updated_at":"2026-03-23T01:36:40.000Z","dependencies_parsed_at":"2024-11-18T02:45:39.595Z","dependency_job_id":"21689b05-109c-4d43-80a8-f45737d4d302","html_url":"https://github.com/aws-samples/hardeneks","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":"amazon-archives/__template_MIT-0","purl":"pkg:github/aws-samples/hardeneks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aws-samples%2Fhardeneks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aws-samples%2Fhardeneks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aws-samples%2Fhardeneks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aws-samples%2Fhardeneks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aws-samples","download_url":"https://codeload.github.com/aws-samples/hardeneks/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aws-samples%2Fhardeneks/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31293376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","best-practices","eks","k8s"],"created_at":"2024-08-01T21:02:07.470Z","updated_at":"2026-04-02T00:27:54.636Z","avatar_url":"https://github.com/aws-samples.png","language":"Python","readme":"# Hardeneks\r\n\r\n[![PyPI version](https://badge.fury.io/py/hardeneks.svg)](https://badge.fury.io/py/hardeneks)\r\n[![PyPI Supported Python Versions](https://img.shields.io/pypi/pyversions/hardeneks.svg)](https://pypi.python.org/pypi/hardeneks/)\r\n[![Python package](https://github.com/aws-samples/hardeneks/actions/workflows/ci.yaml/badge.svg)](https://github.com/aws-samples/hardeneks/actions/workflows/ci.yaml)\r\n[![Downloads](https://pepy.tech/badge/hardeneks)](https://pepy.tech/project/hardeneks)\r\n\r\n\r\nRuns checks to see if an EKS cluster follows [EKS Best Practices](https://aws.github.io/aws-eks-best-practices/).\r\n\r\n**Quick Start**:\r\n\r\n```\r\npython3.10 -m venv /tmp/.venv   # Or any other supported Python version listed above.\r\nsource /tmp/.venv/bin/activate\r\npip install hardeneks\r\nhardeneks\r\n```\r\n\r\n![alt text](https://raw.githubusercontent.com/aws-samples/hardeneks/main/docs/hardeneks.gif)\r\n\r\n**Usage**:\r\n\r\n```console\r\nhardeneks [OPTIONS]\r\n```\r\n\r\n**Options**:\r\n\r\n* `--region TEXT`: AWS region of the cluster. Ex: us-east-1\r\n* `--context TEXT`: K8s context\r\n* `--cluster TEXT`: EKS Cluster name\r\n* `--namespace TEXT`: Namespace to be checked (default is all namespaces)\r\n* `--config TEXT`: Path to a hardeneks config file\r\n* `--export-txt TEXT`: Export the report in txt format\r\n* `--export-csv TEXT`: Export the report in csv format\r\n* `--export-html TEXT`: Export the report in html format\r\n* `--export-json TEXT`: Export the report in json format\r\n* `--export-security-hub`: Export failed checks to AWS Security Hub\r\n* `--insecure-skip-tls-verify`: Skip TLS verification\r\n* `--width`: Width of the output (defaults to terminal size)\r\n* `--height`: Height of the output (defaults to terminal size)\r\n* `--help`: Show this message and exit.\r\n\r\n\r\n- \u003cb\u003eK8S_CONTEXT\u003cb\u003e \r\n  \r\n    You can get the contexts by running:\r\n    ```\r\n    kubectl config get-contexts\r\n    ```\r\n    or get the current context by running:\r\n    ```\r\n    kubectl config current-context\r\n    ```\r\n\r\n- \u003cb\u003eCLUSTER_NAME\u003cb\u003e\r\n  \r\n    You can get the cluster names by running:\r\n    ```\r\n    aws eks list-clusters --region us-east-1\r\n    ```\r\n  \r\n**Configuration File**:\r\n\r\nDefault behavior is to run all the checks. If you want to provide your own config file to specify list of rules to run, you can use the --config flag.You can also add namespaces to be skipped. \r\n\r\nFollowing is a sample config file:\r\n\r\n```yaml\r\nignore-namespaces:\r\n  - kube-node-lease\r\n  - kube-public\r\n  - kube-system\r\n  - kube-apiserver\r\n  - karpenter\r\n  - kubecost\r\n  - external-dns\r\n  - argocd\r\n  - aws-for-fluent-bit\r\n  - amazon-cloudwatch\r\n  - vpa\r\nrules: \r\n  cluster_wide:\r\n    security:\r\n      iam:\r\n        - disable_anonymous_access_for_cluster_roles\r\n        - check_endpoint_public_access\r\n        - check_aws_node_daemonset_service_account\r\n        - check_access_to_instance_profile\r\n        - restrict_wildcard_for_cluster_roles\r\n      multi_tenancy:\r\n        - ensure_namespace_quotas_exist\r\n      detective_controls:\r\n        - check_logs_are_enabled\r\n      network_security:\r\n        - check_vpc_flow_logs\r\n        - check_awspca_exists\r\n        - check_default_deny_policy_exists\r\n      encryption_secrets:\r\n        - use_encryption_with_ebs\r\n        - use_encryption_with_efs\r\n        - use_efs_access_points\r\n      infrastructure_security:\r\n        - deploy_workers_onto_private_subnets\r\n        - make_sure_inspector_is_enabled\r\n      pod_security:\r\n        - ensure_namespace_psa_exist\r\n      image_security:\r\n        - use_immutable_tags_with_ecr\r\n    reliability:\r\n      applications:\r\n        - check_metrics_server_is_running\r\n        - check_vertical_pod_autoscaler_exists\r\n    cluster_autoscaling:\r\n      cluster_autoscaler:\r\n        - check_any_cluster_autoscaler_exists\r\n        - ensure_cluster_autoscaler_and_cluster_versions_match\r\n        - ensure_cluster_autoscaler_has_autodiscovery_mode\r\n        - use_separate_iam_role_for_cluster_autoscaler\r\n        - employ_least_privileged_access_cluster_autoscaler_role\r\n        - use_managed_nodegroups\r\n    scalability:\r\n      control_plane:\r\n        - check_eks_version\r\n        - check_kubectl_compression\r\n  namespace_based:\r\n    security: \r\n      iam:\r\n        - disable_anonymous_access_for_roles\r\n        - restrict_wildcard_for_roles\r\n        - disable_service_account_token_mounts\r\n        - disable_run_as_root_user\r\n        - use_dedicated_service_accounts_for_each_deployment\r\n        - use_dedicated_service_accounts_for_each_stateful_set\r\n        - use_dedicated_service_accounts_for_each_daemon_set\r\n      pod_security:\r\n        - disallow_container_socket_mount\r\n        - disallow_host_path_or_make_it_read_only\r\n        - set_requests_limits_for_containers\r\n        - disallow_privilege_escalation\r\n        - check_read_only_root_file_system\r\n      network_security:\r\n        - use_encryption_with_aws_load_balancers\r\n      encryption_secrets:\r\n        - disallow_secrets_from_env_vars    \r\n      runtime_security:\r\n        - disallow_linux_capabilities\r\n    reliability:\r\n      applications:\r\n        - check_horizontal_pod_autoscaling_exists\r\n        - schedule_replicas_across_nodes\r\n        - run_multiple_replicas\r\n        - avoid_running_singleton_pods\r\n        - check_readiness_probes\r\n        - check_liveness_probes\r\n```\r\n\r\n## Permissions\r\n \r\nIn order to run hardeneks we need to have some permissions both on AWS side and k8s side.\r\n\r\n### Minimal IAM role policy for all checks\r\n\r\n```json\r\n{\r\n    \"Version\": \"2012-10-17\",\r\n    \"Statement\": [\r\n        {\r\n            \"Effect\": \"Allow\",\r\n            \"Action\": [\r\n                \"eks:ListClusters\",\r\n                \"eks:DescribeCluster\",\r\n                \"eks:ListPodIdentityAssociations\",\r\n                \"eks:DescribePodIdentityAssociation\",\r\n                \"eks:DescribeClusterVersions\",\r\n                \"ecr:DescribeRepositories\",\r\n                \"inspector2:BatchGetAccountStatus\",\r\n                \"ec2:DescribeFlowLogs\",\r\n                \"ec2:DescribeInstances\",\r\n                \"iam:ListAttachedRolePolicies\",\r\n                \"iam:ListRolePolicies\",\r\n                \"iam:GetPolicy\",\r\n                \"iam:GetPolicyVersion\",\r\n                \"iam:GetRolePolicy\"\r\n            ],\r\n            \"Resource\": \"*\"\r\n        }\r\n    ]\r\n}\r\n```\r\n\r\n### Minimal ClusterRole for all checks\r\n\r\n```yaml\r\nkind: ClusterRole\r\napiVersion: rbac.authorization.k8s.io/v1\r\nmetadata:\r\n  name: hardeneks-runner\r\nrules:\r\n- apiGroups: [\"\"]\r\n  resources: [\"namespaces\", \"resourcequotas\", \"persistentvolumes\", \"pods\", \"services\", \"nodes\"]\r\n  verbs: [\"list\"]\r\n- apiGroups: [\"\"]\r\n  resources: [\"serviceaccounts\"]\r\n  verbs: [\"get\"]\r\n- apiGroups: [\"rbac.authorization.k8s.io\"]\r\n  resources: [\"clusterroles\", \"clusterrolebindings\", \"roles\", \"rolebindings\"]\r\n  verbs: [\"list\"]\r\n- apiGroups: [\"networking.k8s.io\"]\r\n  resources: [\"networkpolicies\"]\r\n  verbs: [\"list\"]\r\n- apiGroups: [\"storage.k8s.io\"]\r\n  resources: [\"storageclasses\"]\r\n  verbs: [\"list\"]\r\n- apiGroups: [\"apps\"]\r\n  resources: [\"deployments\", \"daemonsets\", \"statefulsets\"]\r\n  verbs: [\"list\", \"get\"]\r\n- apiGroups: [\"autoscaling\"]\r\n  resources: [\"horizontalpodautoscalers\"]\r\n  verbs: [\"list\"]\r\n```\r\n\r\n## For Developers\r\n\r\n**Prerequisites**:\r\n\r\n* This cli uses poetry. Follow instructions that are outlined [here](https://python-poetry.org/docs/) to install poetry.\r\n\r\n\r\n**Installation**:\r\n\r\n```console\r\ngit clone git@github.com:aws-samples/hardeneks.git\r\ncd hardeneks\r\npoetry install\r\n```\r\n\r\n**Running Tests**:\r\n\r\n```console\r\npoetry shell\r\npytest --cov=hardeneks tests/ --cov-report term-missing\r\n```\r\n\r\n\r\n\r\n\r\n","funding_links":[],"categories":["Kubernetes","Python"],"sub_categories":["Kubernetes security posture management"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faws-samples%2Fhardeneks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faws-samples%2Fhardeneks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faws-samples%2Fhardeneks/lists"}