Skip to main content

Vier Schritte zur Härtung der Sicherheit von Amazon EKS

Artikel von
Headshot of Kamil Potrec

Kamil Potrec

blog feature amazon eks security

21. Juli 2021

0 Min. Lesezeit

Im ersten Teil dieser Blog-Serie haben wir uns mit der Bereitstellung von Amazon EKS mit Terraform beschäftigt und betrachtet, wie sich die anfängliche RBAC-Implementierung und der Instance Metadata Service absichern lassen. In diesem zweiten Beitrag stellen wir weitere Best Practices zur Härtung der Sicherheit von Amazon EKS vor. Dazu gehören die Bedeutung von dedizierten IAM-Rollen für Continuous Delivery, einer Multi-Account-Architektur zur Isolierung von Amazon-EKS-Clustern und die Frage, wie Sie Ihre Secrets in der Control Plane verschlüsseln. Zum Schluss zeigen wir, wie Sie statische Analysetools in Ihre CD-Pipelines integrieren, um diese Probleme zu erkennen, bevor sie Produktionsumgebungen erreichen.

Behalten Sie den Überblick darüber, wer den Cluster bereitgestellt hat

Der Amazon-EKS-Service ist in den Identity and Access Management-Service integriert, um Benutzer beim Cluster zu authentifizieren. Die Autorisierungsentscheidungen werden vom nativen Kubernetes-RBAC-Modell getroffen. Die RBAC-Konfiguration wird über die aws-auth ConfigMap den IAM-Identitäten zugeordnet. Es gibt jedoch eine spezielle Zuordnung, die in keiner Konfigurationsdatei und keiner Einstellung sichtbar ist. Die IAM-Entität, mit der der Cluster erstellt wird, wird automatisch der system:masters integrierten Kubernetes-Gruppe zugeordnet. Dies ist in der Benutzerdokumentation ausführlich beschrieben. Dennoch hat diese Zuordnung erhebliche Auswirkungen auf die Überprüfbarkeit der Cluster-Berechtigungen.

Um die Auswirkungen dieser Konfiguration zu veranschaulichen, löschen wir alle Roles und ClusterRoles aus unserem Demo-Cluster.

dev@pwnbox:~$ kubectl delete clusterroles --all
clusterrole.rbac.authorization.k8s.io "admin" deleted
clusterrole.rbac.authorization.k8s.io "aws-node" deleted
clusterrole.rbac.authorization.k8s.io "cluster-admin" deleted
clusterrole.rbac.authorization.k8s.io "edit" deleted
clusterrole.rbac.authorization.k8s.io "eks:addon-manager" deleted
clusterrole.rbac.authorization.k8s.io "eks:fargate-manager" deleted
clusterrole.rbac.authorization.k8s.io "eks:node-bootstrapper" deleted
clusterrole.rbac.authorization.k8s.io "eks:node-manager" deleted
clusterrole.rbac.authorization.k8s.io "eks:podsecuritypolicy:privileged" deleted
<OMITTED>
dev@pwnbox:~$ kubectl delete roles --all-namespaces --all
role.rbac.authorization.k8s.io "system:controller:bootstrap-signer" deleted
role.rbac.authorization.k8s.io "eks:addon-manager" deleted
role.rbac.authorization.k8s.io "eks:certificate-controller" deleted
role.rbac.authorization.k8s.io "eks:fargate-manager" deleted
role.rbac.authorization.k8s.io "eks:node-manager" deleted
<OMITTED>
dev@pwnbox:~$ kubectl auth can-i --list
Resources                                       Non-Resource URLs   Resource Names   Verbs
*.*                                               []                  []               [*]
                                                [*]                 []               [*]
selfsubjectaccessreviews.authorization.k8s.io   []                  []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                  []               [create]
                                                [/api/*]            []               [get]
                                                [/api]              []               [get]
                                                [/apis/*]           []               [get]
                                                [/apis]             []               [get]
                                                [/healthz]          []               [get]
                                                [/healthz]          []               [get]
                                                [/livez]            []               [get]
                                                [/livez]            []               [get]
                                                [/openapi/*]        []               [get]
                                                [/openapi]          []               [get]
                                                [/readyz]           []               [get]
                                                [/readyz]           []               [get]
                                                [/version/]         []               [get]
                                                [/version/]         []               [get]
                                                [/version]          []               [get]
                                                [/version]          []               [get]
dev@pwnbox:~$ kubectl get configmap --all-namespaces
NAMESPACE     NAME                                 DATA   AGE
kube-system   coredns                              1      7h59m
kube-system   cp-vpc-resource-controller           0      7h58m
kube-system   eks-certificates-controller          0      7h59m
kube-system   extension-apiserver-authentication   6      7h59m
kube-system   kube-proxy                           1      7h59m
kube-system   kube-proxy-config                    1      7h59m

Amazon EKS verwendet Webhook-Token-Authentifizierung, um die Integration mit IAM zu ermöglichen. Die Konfiguration finden Sie in den Argumenten des API-Servers.

FLAG: --authentication-token-webhook-cache-ttl="7m0s"
FLAG: --authentication-token-webhook-config-file="/etc/kubernetes/authenticator/apiserver-webhook-kubeconfig.yaml"
FLAG: --authentication-token-webhook-version="v1beta1"

AWS IAM kennt eindeutige Bezeichner, die in bestimmten Services verwendet werden können, um Fehlkonfigurationen durch die Wiederverwendung von Namen zu vermeiden. Aus der Amazon-EKS-Dokumentation geht nicht eindeutig hervor, ob die Berechtigung system:masters an den eindeutigen Bezeichner der Entität oder an ihren Anzeigenamen gebunden ist. Um zu überprüfen, ob durch das Entfernen der ursprünglichen Entität alle Berechtigungen für den Cluster entzogen werden, haben wir die temporäre Rolle gelöscht und neu erstellt.

dev@pwnbox:~$ aws iam get-role --role-name ci-eks-test
{
    "Role": {
        "Path": "/",
        "RoleName": "ci-eks-test",
        "RoleId": "AROAYREY3WYOOSHLPO3W6",
        "Arn": "arn:aws:iam::123456789012:role/ci-eks-test",
        "CreateDate": "2021-06-20T21:09:01+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::123456789012:user/ci"
                    },
                    "Action": "sts:AssumeRole",
                    "Condition": {}
                }
            ]
        },
        "MaxSessionDuration": 3600,
        "RoleLastUsed": {}
    }
}
dev@pwnbox:~$ kubectl auth can-i --list
Resources                                       Non-Resource URLs   Resource Names     Verbs
*.*                                             []                  []                 [*]
                                                [*]                 []                 [*]
selfsubjectaccessreviews.authorization.k8s.io   []                  []                 [create]
selfsubjectrulesreviews.authorization.k8s.io    []                  []                 [create]
                                                [/api/*]            []                 [get]
                                                [/api]              []                 [get]
                                                [/apis/*]           []                 [get]
                                                [/apis]             []                 [get]
                                                [/healthz]          []                 [get]
                                                [/healthz]          []                 [get]
                                                [/livez]            []                 [get]
                                                [/livez]            []                 [get]
                                                [/openapi/*]        []                 [get]
                                                [/openapi]          []                 [get]
                                                [/readyz]           []                 [get]
                                                [/readyz]           []                 [get]
                                                [/version/]         []                 [get]
                                                [/version/]         []                 [get]
                                                [/version]          []                 [get]
                                                [/version]          []                 [get]
podsecuritypolicies.policy                      []                  [eks.privileged]   [use]

dev@pwnbox:~$ aws iam get-role --role-name ci-eks-test
{
    "Role": {
        "Path": "/",
        "RoleName": "ci-eks-test",
        "RoleId": "AROAYREY3WYOAK6QMGSUT",
        "Arn": "arn:aws:iam::123456789012:role/ci-eks-test",
        "CreateDate": "2021-06-20T21:14:28+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::123456789012:user/ci"
                    },
                    "Action": "sts:AssumeRole",
                    "Condition": {}
                }
            ]
        },
        "MaxSessionDuration": 3600,
        "RoleLastUsed": {}
    }
}
dev@pwnbox:~$ kubectl auth can-i --list
Resources                                       Non-Resource URLs   Resource Names     Verbs
*.*                                             []                  []                 [*]
                                                [*]                 []                 [*]
selfsubjectaccessreviews.authorization.k8s.io   []                  []                 [create]
selfsubjectrulesreviews.authorization.k8s.io    []                  []                 [create]
                                                [/api/*]            []                 [get]
                                                [/api]              []                 [get]
                                                [/apis/*]           []                 [get]
                                                [/apis]             []                 [get]
                                                [/healthz]          []                 [get]
                                                [/healthz]          []                 [get]
                                                [/livez]            []                 [get]
                                                [/livez]            []                 [get]
                                                [/openapi/*]        []                 [get]
                                                [/openapi]          []                 [get]
                                                [/readyz]           []                 [get]
                                                [/readyz]           []                 [get]
                                                [/version/]         []                 [get]
                                                [/version/]         []                 [get]
                                                [/version]          []                 [get]
                                                [/version]          []                 [get]
podsecuritypolicies.policy                      []                  [eks.privileged]   [use]

Wie Sie sehen, haben wir nun eine völlig neue Rolle mit einer anderen RoleId. Der Amazon-EKS-Authentifizierungsservice verwendet jedoch den Anzeigenamen der Rolle, um uns dieselben system:masters-Berechtigungen zu gewähren.

Am wirksamsten lässt sich dieses Problem vermeiden, indem Sie ein zentrales Bereitstellungssystem und eine dedizierte Rolle für Bereitstellungen verwenden und unbedingt auf persönliche Anmeldedaten verzichten. Tools wie aws-vault können Ihnen helfen, den Wechsel zwischen verschiedenen Rollen sicher zu verwalten.

Sehen wir uns als Nächstes an, wie sich Nodes bei der Control Plane authentifizieren.

Trennen Sie Cluster mithilfe mehrerer Konten voneinander

Amazon EKS verlangt, dass an alle Nodes ein Instance-Profil mit den folgenden beiden Richtlinien angehängt ist: AmazonEKSWorkerNodePolicy und AmazonEC2ContainerRegistryReadOnly.

dev@pwnbox:$ aws iam get-policy-version --policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy --version-id v1
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": [
                        "ec2:DescribeInstances",
                        "ec2:DescribeRouteTables",
                        "ec2:DescribeSecurityGroups",
                        "ec2:DescribeSubnets",
                        "ec2:DescribeVolumes",
                        "ec2:DescribeVolumesModifications",
                        "ec2:DescribeVpcs",
                        "eks:DescribeCluster"
                    ],
                    "Resource": "*",
                    "Effect": "Allow"
                }
            ]
        },
        "VersionId": "v1",
        "IsDefaultVersion": true,
        "CreateDate": "2018-05-27T21:09:01+00:00"
    }
}

dev@pwnbox:$ aws iam get-policy-version --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly --version-id v3
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "ecr:GetAuthorizationToken",
                        "ecr:BatchCheckLayerAvailability",
                        "ecr:GetDownloadUrlForLayer",
                        "ecr:GetRepositoryPolicy",
                        "ecr:DescribeRepositories",
                        "ecr:ListImages",
                        "ecr:DescribeImages",
                        "ecr:BatchGetImage",
                        "ecr:GetLifecyclePolicy",
                        "ecr:GetLifecyclePolicyPreview",
                        "ecr:ListTagsForResource",
                        "ecr:DescribeImageScanFindings"
                    ],
                    "Resource": "*"
                }
            ]
        },
        "VersionId": "v3",
        "IsDefaultVersion": true,
        "CreateDate": "2019-12-10T20:56:32+00:00"
    }
}

Standardmäßig gewähren die Richtlinien Berechtigungen für alle Ressourcen innerhalb des AWS-Kontos. Das bedeutet, dass ein kompromittierter Pod in diesem Cluster Informationen zu allen Ressourcen, VPCs, Subnetzen und Images im Konto abrufen kann.

Der Amazon-EKS-Node benötigt tatsächlich Zugriff auf eine weitere verwaltete Richtlinie namens AmazonEKS_CNI_Policy. Diese Richtlinie ermöglicht es dem Node, IP-Adressen zuzuweisen und netzwerkbezogene Funktionen auszuführen.

dev@pwnbox:$ aws iam get-policy-version --policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy --version-id v4
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "ec2:AssignPrivateIpAddresses",
                        "ec2:AttachNetworkInterface",
                        "ec2:CreateNetworkInterface",
                        "ec2:DeleteNetworkInterface",
                        "ec2:DescribeInstances",
                        "ec2:DescribeTags",
                        "ec2:DescribeNetworkInterfaces",
                        "ec2:DescribeInstanceTypes",
                        "ec2:DetachNetworkInterface",
                        "ec2:ModifyNetworkInterfaceAttribute",
                        "ec2:UnassignPrivateIpAddresses"
                    ],
                    "Resource": "*"
                },
                {
                    "Effect": "Allow",
                    "Action": [
                        "ec2:CreateTags"
                    ],
                    "Resource": [
                        "arn:aws:ec2:*:*:network-interface/*"
                    ]
                }
            ]
        },
        "VersionId": "v4",
        "IsDefaultVersion": true,
        "CreateDate": "2020-04-20T20:52:01+00:00"
    }
}

Diese Richtlinie erlaubt es dem Aufrufer, die Netzwerkkonfiguration jeder Instanz im Konto zu ändern. Amazon empfiehlt sogar, diese Richtlinie direkt an das Kubernetes-Servicekonto aws-node anzuhängen. Standardmäßig hängt das Terraform-Modul diese Richtlinie an das IAM-Profil an. Sie können dieses Verhalten ändern, indem Sie das Attribut attach_worker_cni_policy auf false setzen.

Verschlüsseln Sie Ihre Secrets

Eine der von Amazon EKS verwalteten Komponenten der Control Plane ist der etcd-Schlüssel-Wert-Speicher. Der Kubernetes-API-Server verwendet ihn zum Speichern aller Objekte und Konfigurationen. Standardmäßig verschlüsselt Kubernetes keine Daten, die in diesem Schlüssel-Wert-Speicher abgelegt werden. AWS ist für die Sicherheit der Komponenten der Control Plane verantwortlich. Dennoch können wir zusätzlich eine Defense-in-Depth-Strategie anwenden, um den Schutz unserer Daten weiter zu verbessern.

Der AWS-KMS-Service ist einer der Verschlüsselungsanbieter, der Envelope-Verschlüsselung für in unserem Cluster gespeicherte Secrets-Objekte bereitstellen kann. Mit der Option `cluster_encryption_config` können wir einen KMS-Schlüssel angeben, der zum Verschlüsseln zwischengeschalteter Datenverschlüsselungsschlüssel verwendet wird. Diese wiederum dienen zur Verschlüsselung bestimmter Objekte. Dieser Amazon-EKS-Blogbeitrag von AWS bietet einen guten Überblick über den zugrunde liegenden Prozess. Ab diesem Zeitpunkt kann niemand mit Zugriff auf einen etcd-Cluster unsere Secrets lesen, ohne auch Zugriff auf unseren KMS-Schlüssel zu haben. Der gesamte Vorgang läuft für Endbenutzer vollständig transparent ab.

Erkennen Sie Probleme in der Deployment-Pipeline

Infrastructure as Code ermöglicht es uns, den gewünschten Zustand des Amazon-EKS-Clusters deklarativ zu beschreiben. Dadurch können wir einige dieser Probleme statisch erkennen, bevor etwas bereitgestellt wird.

In Terraform können wir einen Plan der bereitzustellenden Konfiguration erstellen.

dev@pwnbox:$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 <= read (data resources)

Terraform will perform the following actions:

  # data.aws_eks_cluster.cluster will be read during apply
  # (config refers to values not yet known)
 <= data "aws_eks_cluster" "cluster"  {
      + arn                       = (known after apply)
      + certificate_authority     = (known after apply)
      + created_at                = (known after apply)
      + enabled_cluster_log_types = (known after apply)
      + endpoint                  = (known after apply)
      + id                        = (known after apply)
      + identity                  = (known after apply)
      + kubernetes_network_config = (known after apply)
      + name                      = (known after apply)
      + platform_version          = (known after apply)
      + role_arn                  = (known after apply)
      + status                    = (known after apply)
      + tags                      = (known after apply)
      + version                   = (known after apply)
      + vpc_config                = (known after apply)
    }

<OMITTED>

# module.vpc.aws_vpc.this[0] will be created
  + resource "aws_vpc" "this" {
      + arn                              = (known after apply)
      + assign_generated_ipv6_cidr_block = false
      + cidr_block                       = "10.0.0.0/16"
      + default_network_acl_id           = (known after apply)
      + default_route_table_id           = (known after apply)
      + default_security_group_id        = (known after apply)
      + dhcp_options_id                  = (known after apply)
      + enable_classiclink               = (known after apply)
      + enable_classiclink_dns_support   = (known after apply)
      + enable_dns_hostnames             = false
      + enable_dns_support               = true
      + id                               = (known after apply)
      + instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      + main_route_table_id              = (known after apply)
      + owner_id                         = (known after apply)
      + tags                             = {
          + "Name" = "eks-threat-modelling-ddddaaaa"
        }
      + tags_all                         = {
          + "Name" = "eks-threat-modelling-ddddaaaa"
        }
    }

Plan: 44 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + cluster_endpoint          = (known after apply)
  + cluster_name              = (known after apply)
  + cluster_security_group_id = (known after apply)
  + config_map_aws_auth       = []
  + kubectl_config            = (known after apply)
  + region                    = "eu-west-1

Dieser Plan lässt sich in das JSON-Format konvertieren. So können wir eine statische Analyse aller Attribute durchführen.

dev@pwnbox:$ terraform show -json tfplan > tfplan.json
dev@pwnbox:$ jq '.resource_changes[] | select((.type == "aws_eks_cluster") and .mode == "managed").change.after' tfplan.json
{
  "enabled_cluster_log_types": [
    "audit",
    "authenticator"
  ],
  "encryption_config": [
    {
      "provider": [
        {}
      ],
      "resources": [
        "secrets"
      ]
    }
  ],
  "kubernetes_network_config": [
    {}
  ],
  "name": "eks-threat-modelling-ddddaaaa",
  "tags": null,
  "timeouts": {
    "create": "30m",
    "delete": "15m",
    "update": null
  },
  "version": "1.19",
  "vpc_config": [
    {
      "endpoint_private_access": true,
      "endpoint_public_access": false,
      "public_access_cidrs": [
        "0.0.0.0/0"
      ]
    }
  ]
}

Sobald wir die Struktur des Terraform-Plans verstanden haben, können wir mit Open Policy Agent und seiner deklarativen Sprache einfache Tests schreiben.

package play

deny[msg]{
    input.vpc_config[0].endpoint_public_access == true
    msg := sprintf("The public endpoint is enabled on the cluster", [])
}

Sie können dieses einfache Beispiel in einer Online-Umgebung ausprobieren: Rego Playground.

Das Schreiben dieser Regeln kann aufwendig sein. Es erfordert die Analyse der Terraform-Ausgabestrukturen, das Parsen der Dateien, die Pflege der Bibliotheken und eine aussagekräftige Darstellung der Ergebnisse. Hier können Produkte wie Snyk IaC helfen. Hier sehen Sie ein Beispiel für den Sicherheitsscan unserer Standardbereitstellung.

dev@pwnbox:$ snyk iac test tfplan.json

Testing tfplan.json...

Infrastructure as code issues:
  ✗ EKS cluster allows public access [High Severity] [SNYK-CC-TF-94] in EKS
    introduced by aws_eks_cluster[this] > vpc_config

  ✗ Non-Encrypted root block device [Medium Severity] [SNYK-CC-TF-53] in EC2
    introduced by aws_launch_configuration[workers] > root_block_device > encrypted

  ✗ Public IPs are automatically mapped to instances [Low Severity] [SNYK-CC-AWS-427] in VPC
    introduced by aws_subnet[public_0] > map_public_ip_on_launch

  ✗ EKS control plane logging insufficient [Low Severity] [SNYK-CC-TF-131] in EKS
    introduced by aws_eks_cluster[this] > enabled_cluster_log_types

  ✗ Public IPs are automatically mapped to instances [Low Severity] [SNYK-CC-AWS-427] in VPC
    introduced by aws_subnet[public_2] > map_public_ip_on_launch

  ✗ Public IPs are automatically mapped to instances [Low Severity] [SNYK-CC-AWS-427] in VPC
    introduced by aws_subnet[public_1] > map_public_ip_on_launch

Organization:      p0tr3c
Type:              Terraform
Target file:       tfplan.json
Project name:      hardened
Open source:       no
Project path:      tfplan.json

Tested tfplan.json for known issues, found 6 issues

dev@pwnbox:/$

Mit dem IaC-Scanning von Snyk können Sie Probleme in Ihrer Terraform-Konfiguration erkennen, bevor Sie sie in der Produktion bereitstellen, und so die Sicherheit Ihrer Produktionsumgebungen gewährleisten. Registrieren Sie sich unter https://app.snyk.io kostenlos! Außerdem können Sie das kostenlose Code-Prüftool von Snyk ausprobieren, um sich einen schnellen Eindruck von der Sicherheit Ihres Codes zu verschaffen.

Sichern Sie Ihre Infrastruktur an der Quelle

Snyk automatisiert die IaC-Sicherheit und Compliance in Workflows und erkennt abweichende sowie fehlende Ressourcen.

Gepostet in:

Weiterlesen

blog feature pypi spoof
Article

Die gesamte Snyk AI Security Platform – kostenlos für Open-Source-Maintainer

Open-Source-Maintainer werden von echten Schwachstellenmeldungen überflutet und brauchen Unterstützung dabei, diese zu priorisieren, zu beheben und schneller Korrekturen bereitzustellen. Das Secure Developer Program von Snyk bietet qualifizierten Projekten kostenlosen Zugang zur Snyk AI Security Platform.

Article

Bösartige node-ipc-Versionen nach mutmaßlicher Kompromittierung eines Maintainer-Kontos auf npm veröffentlicht

Am 14. Mai 2026 wurden mehrere bösartige Versionen des beliebten npm-Pakets node-ipc in der npm-Registry veröffentlicht. Aktuelle öffentliche Berichte nennen node...

blog feature toolkit
Blog

Bösartige Veröffentlichung des elementary-data-PyPI-Pakets stiehlt Cloud-Zugangsdaten von Data Engineers

Angreifer nutzten eine Sicherheitslücke durch Skriptinjektion in GitHub Actions aus, um eine bösartige Version der elementary-data-Python-CLI (v0.23.3) zu veröffentlichen. Diese enthielt eine Backdoor zum Diebstahl von Zugangsdaten, die auf dbt-Profile, Cloud-Anbieterschlüssel und SSH-Geheimnisse in Data-Engineering-Umgebungen abzielte.