Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config option to enable the encryption of AWS EKS secrets #2723

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
4b4e2d1
Add config option (amazon_web_services.eks_kms_arn) to specify KMS-ke…
joneszc Sep 16, 2024
858d185
Add config option (amazon_web_services.eks_kms_arn) to specify KMS-ke…
joneszc Sep 17, 2024
06e430f
Merge branch 'nebari-dev:develop' into eks-cluster-encryption
joneszc Sep 17, 2024
7f29ffc
Add config option (amazon_web_services.eks_kms_arn) to specify KMS-ke…
joneszc Sep 17, 2024
4627d2f
Merge branch 'nebari-dev:develop' into eks-cluster-encryption
joneszc Sep 17, 2024
954e89b
Merge branch 'nebari-dev:develop' into eks-cluster-encryption
joneszc Sep 18, 2024
ed9f25f
Merge branch 'nebari-dev:develop' into eks-cluster-encryption
joneszc Sep 19, 2024
2a6a797
Merge branch 'nebari-dev:develop' into eks-cluster-encryption
joneszc Sep 24, 2024
9bb9e85
Merge branch 'nebari-dev:develop' into eks-cluster-encryption
joneszc Sep 26, 2024
e13fdb3
Add validation checks for config option amazon_web_services.eks_kms_a…
joneszc Sep 27, 2024
a89989c
Add validation checks for config option amazon_web_services.eks_kms_a…
joneszc Sep 27, 2024
e3decc8
Merge branch 'nebari-dev:develop' into eks-cluster-encryption
joneszc Sep 27, 2024
d3488c0
Merge branch 'nebari-dev:develop' into eks-cluster-encryption
joneszc Sep 27, 2024
c40930a
Merge branch 'nebari-dev:main' into eks-cluster-encryption
joneszc Oct 2, 2024
f31f209
Add validation checks for config option amazon_web_services.eks_kms_a…
joneszc Oct 2, 2024
cb39106
Add validation checks for config option amazon_web_services.eks_kms_a…
joneszc Oct 2, 2024
385f7aa
Add validation checks for config option amazon_web_services.eks_kms_a…
joneszc Oct 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/_nebari/provider/cloud/amazon_web_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,30 @@ def instances(region: str) -> Dict[str, str]:
return {t: t for t in instance_types}


@functools.lru_cache()
def kms_key_arns(region: str) -> Dict[str, dict]:
"""Return dict of available/enabled KMS key IDs and associated KeyMetadata for the AWS region."""
session = aws_session(region=region)
client = session.client("kms")
paginator = client.get_paginator("list_keys")
fields = [
"Arn",
"KeyUsage",
"KeySpec",
# "KeyState",
# "Origin",
# "KeyManager",
# "EncryptionAlgorithms",
# "MultiRegion",
]
kms_keys = [
client.describe_key(KeyId=j["KeyId"]).get("KeyMetadata")
for i in paginator.paginate()
for j in i["Keys"]
]
return {i["KeyId"]: {k: i[k] for k in fields} for i in kms_keys if i["Enabled"]}


def aws_get_vpc_id(name: str, namespace: str, region: str) -> Optional[str]:
"""Return VPC ID for the EKS cluster namedd `{name}-{namespace}`."""
cluster_name = f"{name}-{namespace}"
Expand Down
34 changes: 34 additions & 0 deletions src/_nebari/stages/infrastructure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class AWSInputVars(schema.Base):
eks_endpoint_access: Optional[
Literal["private", "public", "public_and_private"]
] = "public"
eks_kms_arn: Optional[str] = None
node_groups: List[AWSNodeGroupInputVars]
availability_zones: List[str]
vpc_cidr_block: str
Expand Down Expand Up @@ -504,6 +505,7 @@ class AmazonWebServicesProvider(schema.Base):
eks_endpoint_access: Optional[
Literal["private", "public", "public_and_private"]
] = "public"
eks_kms_arn: Optional[str] = None
existing_subnet_ids: Optional[List[str]] = None
existing_security_group_id: Optional[str] = None
vpc_cidr_block: str = "10.10.0.0/16"
Expand Down Expand Up @@ -560,6 +562,37 @@ def _check_input(cls, data: Any) -> Any:
f"Amazon Web Services instance {node_group.instance} not one of available instance types={available_instances}"
)

# check if kms key is valid
available_kms_keys = amazon_web_services.kms_key_arns(data["region"])
if "eks_kms_arn" in data and data["eks_kms_arn"] is not None:
key_id = [
id for id in available_kms_keys.keys() if id in data["eks_kms_arn"]
]
if (
len(key_id) == 1
and available_kms_keys[key_id[0]]["Arn"] == data["eks_kms_arn"]
):
key_id = key_id[0]
# Symmetric KMS keys with Encrypt and decrypt key-usage have the SYMMETRIC_DEFAULT key-spec
# EKS cluster encryption requires a Symmetric key that is set to encrypt and decrypt data
if available_kms_keys[key_id]["KeySpec"] != "SYMMETRIC_DEFAULT":
if available_kms_keys[key_id]["KeyUsage"] == "GENERATE_VERIFY_MAC":
raise ValueError(
f"Amazon Web Services KMS Key with ID {key_id} does not have KeyUsage set to 'Encrypt and decrypt' data"
)
elif available_kms_keys[key_id]["KeyUsage"] != "ENCRYPT_DECRYPT":
raise ValueError(
f"Amazon Web Services KMS Key with ID {key_id} is not of type Symmetric, and KeyUsage not set to 'Encrypt and decrypt' data"
)
else:
raise ValueError(
f"Amazon Web Services KMS Key with ID {key_id} is not of type Symmetric"
)
else:
raise ValueError(
f"Amazon Web Services KMS Key with ARN {data['eks_kms_arn']} not one of available/enabled keys={[v['Arn'] for v in available_kms_keys.values()]}"
)

return data


Expand Down Expand Up @@ -849,6 +882,7 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]):
name=self.config.escaped_project_name,
environment=self.config.namespace,
eks_endpoint_access=self.config.amazon_web_services.eks_endpoint_access,
eks_kms_arn=self.config.amazon_web_services.eks_kms_arn,
existing_subnet_ids=self.config.amazon_web_services.existing_subnet_ids,
existing_security_group_id=self.config.amazon_web_services.existing_security_group_id,
region=self.config.amazon_web_services.region,
Expand Down
1 change: 1 addition & 0 deletions src/_nebari/stages/infrastructure/template/aws/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ module "kubernetes" {

endpoint_public_access = var.eks_endpoint_access == "private" ? false : true
endpoint_private_access = var.eks_endpoint_access == "public" ? false : true
eks_kms_arn = var.eks_kms_arn
public_access_cidrs = var.eks_public_access_cidrs
permissions_boundary = var.permissions_boundary
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,20 @@ resource "aws_eks_cluster" "main" {
public_access_cidrs = var.public_access_cidrs
}

# Only set encryption_config if eks_kms_arn is not null
dynamic "encryption_config" {
for_each = var.eks_kms_arn != null ? [1] : []
content {
provider {
key_arn = var.eks_kms_arn
}
resources = ["secrets"]
}
}

depends_on = [
aws_iam_role_policy_attachment.cluster-policy,
aws_iam_role_policy_attachment.cluster_encryption,
]

tags = merge({ Name = var.name }, var.tags)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,34 @@ resource "aws_iam_role_policy_attachment" "cluster-policy" {
role = aws_iam_role.cluster.name
}

data "aws_iam_policy_document" "cluster_encryption" {
count = var.eks_kms_arn != null ? 1 : 0
statement {
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ListGrants",
"kms:DescribeKey"
]
resources = [var.eks_kms_arn]
}
}

resource "aws_iam_policy" "cluster_encryption" {
count = var.eks_kms_arn != null ? 1 : 0
name = "${var.name}-eks-encryption-policy"
description = "IAM policy for EKS cluster encryption"
policy = data.aws_iam_policy_document.cluster_encryption[count.index].json
}

# Grant the EKS Cluster role KMS permissions if a key-arn is specified
resource "aws_iam_role_policy_attachment" "cluster_encryption" {
count = var.eks_kms_arn != null ? 1 : 0

policy_arn = aws_iam_policy.cluster_encryption[count.index].arn
role = aws_iam_role.cluster.name
}

# =======================================================
# Kubernetes Node Group Policies
# =======================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ variable "endpoint_private_access" {
default = false
}

variable "eks_kms_arn" {
description = "kms key arn for EKS cluster encryption_config"
type = string
default = null
}

variable "public_access_cidrs" {
type = list(string)
default = ["0.0.0.0/0"]
Expand Down
6 changes: 6 additions & 0 deletions src/_nebari/stages/infrastructure/template/aws/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ variable "eks_endpoint_private_access" {
default = false
}

variable "eks_kms_arn" {
description = "kms key arn for EKS cluster encryption_config"
type = string
default = null
}

variable "eks_public_access_cidrs" {
type = list(string)
default = ["0.0.0.0/0"]
Expand Down
Loading