Skip to content

Commit

Permalink
feat: added support for whitelisting custom CIDRs from SSM Parameters (
Browse files Browse the repository at this point in the history
  • Loading branch information
srinivasreddych authored Jul 12, 2024
1 parent fd51c4c commit dca0f5c
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### **Added**

- added support for whitelisting custom CIDRs from SSM Parameters, from a static entry list, and auto loads AWS Codebuild IPs for being able to run seedfarmer commands
- added asg rolling update for self managed node groups

### **Changed**

### **Removed**
Expand Down
4 changes: 3 additions & 1 deletion manifests/local/compute-modules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ parameters:
# #operator: "Equal"
# install_nvidia_device_plugin: True
eks_node_spot: False
eks_api_endpoint_private: False # This makes the EKS Endpoint PUBLIC by default
eks_api_endpoint_private: False # This makes the EKS Endpoint PUBLIC and Private by default
# ips_to_whitelist_from_ssm: ${IPS_TO_WHITELIST_FROM_SSM} # It loads SSM Params from `.env` file
# ips_to_whitelist_adhoc: ${IPS_TO_WHITELIST_ADHOC} # It loads list of CIDRs from `.env` file
eks_secrets_envelope_encryption: True
- name: eks-addons
value:
Expand Down
26 changes: 25 additions & 1 deletion modules/compute/eks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,31 @@ Security:
- `eks_readonly_role_name`: The ReadOnly Role to be mapped to the `readonly-group` group of RBAC
- `eks_node_spot`: If `eks_node_spot` is set to True, we deploy SPOT instances of the above `nodegroup_config` for you else we deploy `ON_DEMAND` instances.
- `eks_secrets_envelope_encryption`: If set to True, we enable KMS secret for [envelope encryption](https://aws.amazon.com/about-aws/whats-new/2020/03/amazon-eks-adds-envelope-encryption-for-secrets-with-aws-kms/) for Kubernetes secrets.
- `eks_api_endpoint_private`: If set to True, we deploy EKS cluster with API endpoint set to [private mode](https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html).
- `eks_api_endpoint_private`: If set to `True`, we deploy the EKS cluster with API endpoint set to [private mode](https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html). By default, it is set to `PUBLIC AND PRIVATE` access mode (which whitelists `0.0.0.0/0` CIDR). If you want to whitelist custom CIDRs instead of the default whitelisted CIDR, you can either set `ips_to_whitelist_from_ssm` or `ips_to_whitelist_adhoc` attribute(s). You should not set either of them if `eks_api_endpoint_private` is set to `True`.
- `ips_to_whitelist_from_ssm`: You can load a list of SSM Parameters in which your enterprise stores CIDRs to be whietlisted. The SSM Parameters should be declared in a `.env` file (feature supported by SeedFarmer). If you set this parameter, the EKS module also loads the list of CODEBUILD Public IPS from `ip-ranges.json` [file](https://ip-ranges.amazonaws.com/ip-ranges.json) based on the region of operation, so your seedfarmer commands continue to work. Below is an example declaration:

```yaml
eks_api_endpoint_private: False
ips_to_whitelist_from_ssm: ${IPS_TO_WHITELIST_FROM_SSM}
```

```.env
IPS_TO_WHITELIST_FROM_SSM=["/company/org/ip-whitelists1", "/company/org/ip-whitelists2"]
```

- `ips_to_whitelist_adhoc`: You can declare a list of Public CIDRs which needs to be whietlisted. The entities leveraging Public CIDRs can be declared in a `.env` file (feature supported by SeedFarmer). If you set this parameter, the EKS module also loads the list of CODEBUILD Public IPS from `ip-ranges.json` [file](https://ip-ranges.amazonaws.com/ip-ranges.json) based on the region of operation, so your seedfarmer commands continue to work. Below is an example declaration:

```yaml
eks_api_endpoint_private: False
ips_to_whitelist_adhoc: ${IPS_TO_WHITELIST_ADHOC}
```

```.env
IPS_TO_WHITELIST_ADHOC=["1.2.3.4/8", "11.2.33.44/8"]
```

#### Optional Drivers/Addons/Plugins

- `deploy_aws_lb_controller`: Deploys the [ALB Ingress controller](https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html). Default behavior is set to False
- `deploy_external_dns`: Deploys the External DNS to interact with [AWS Route53](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md). Default behavior is set to False
- `deploy_aws_ebs_csi`: Deploys the [AWS EBS](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html) Driver. Default behavior is set to False
Expand Down
61 changes: 57 additions & 4 deletions modules/compute/eks/stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import Any, Dict, List, Optional, cast

import cdk_nag
import requests
import yaml
from aws_cdk import Aspects, CfnJson, Duration, RemovalPolicy, Stack, Tags
from aws_cdk import aws_aps as aps
Expand All @@ -17,6 +18,7 @@
from aws_cdk import aws_eks as eks
from aws_cdk import aws_iam as iam
from aws_cdk import aws_kms as kms
from aws_cdk import aws_ssm as ssm
from aws_cdk.lambda_layer_kubectl_v29 import KubectlV29Layer
from cdk_nag import NagSuppressions
from constructs import Construct, IConstruct
Expand Down Expand Up @@ -359,7 +361,10 @@ def _create_self_managed_node_group(self, dep_mod, eks_cluster, ng_config, vpc_c
desired_capacity=ng_config.get("eks_node_quantity"),
max_capacity=ng_config.get("eks_node_max_quantity"),
min_capacity=ng_config.get("eks_node_min_quantity"),
update_policy=None,
update_policy=asg.UpdatePolicy.rolling_update(
max_batch_size=1,
min_instances_in_service=ng_config.get("eks_node_quantity"),
),
instance_type=ec2.InstanceType(str(ng_config.get("eks_node_instance_type"))),
bootstrap_options=eks.BootstrapOptions(
kubelet_extra_args=KUBELET_EXTRA_ARGS if KUBELET_EXTRA_ARGS else None,
Expand Down Expand Up @@ -477,6 +482,26 @@ def _create_eks_cluster(
Creates an Amazon EKS cluster with the specified configuration.
"""

CIDRS = []
if eks_compute_config.get("ips_to_whitelist_from_ssm"):
cidrs_from_ssm = self._fetch_cidrs_from_ssm(json.loads(eks_compute_config.get("ips_to_whitelist_from_ssm")))
CIDRS.extend(cidrs_from_ssm)
if eks_compute_config.get("ips_to_whitelist_adhoc"):
cidrs_adhoc = json.loads(eks_compute_config.get("ips_to_whitelist_adhoc"))
CIDRS.extend(cidrs_adhoc)

if eks_compute_config.get("eks_api_endpoint_private"):
api_endpoint = eks.EndpointAccess.PRIVATE
else:
api_endpoint = eks.EndpointAccess.PUBLIC_AND_PRIVATE.only_from(
"0.0.0.0/0"
) # by default, opens up to 0.0.0.0/0

if eks_compute_config.get("ips_to_whitelist_from_ssm") or eks_compute_config.get("ips_to_whitelist_adhoc"):
aws_cidrs = self._fetch_aws_cidrs()
CIDRS.extend(aws_cidrs)
api_endpoint = eks.EndpointAccess.PUBLIC_AND_PRIVATE.only_from(*CIDRS)

# Create the EKS cluster
eks_cluster = eks.Cluster(
self,
Expand All @@ -485,9 +510,7 @@ def _create_eks_cluster(
vpc_subnets=[ec2.SubnetSelection(subnets=controlplane_subnets)],
cluster_name=f"{project_name}-{deployment_name}-{module_name}-cluster",
masters_role=cluster_admin_role,
endpoint_access=eks.EndpointAccess.PRIVATE
if eks_compute_config.get("eks_api_endpoint_private")
else eks.EndpointAccess.PUBLIC,
endpoint_access=api_endpoint,
version=eks.KubernetesVersion.of(str(eks_version)),
kubectl_layer=KubectlV29Layer(self, "Kubectlv29Layer"),
default_capacity=0,
Expand Down Expand Up @@ -2080,3 +2103,33 @@ def _add_suppressions(self) -> None:
{"id": "AwsSolutions-L1", "reason": "Suppress error caused by python_3_12 release in December"},
],
)

def _fetch_cidrs_from_ssm(self, ips_to_whitelist: List[str]):
"""
Fetches the CIDR ranges from the list of SSM Parameters.
"""
cidrs_list = []

for parameter_name in ips_to_whitelist:
string_value = ssm.StringParameter.from_string_parameter_attributes(
self, f"SSM-{parameter_name}", parameter_name=parameter_name
).string_value
cidrs_list.append(string_value)

return cidrs_list

def _fetch_aws_cidrs(self):
"""
Fetches the CIDR ranges from the AWS Services
https://docs.aws.amazon.com/vpc/latest/userguide/aws-ip-work-with.html#filter-json-file
"""

aws_cidrs_list = []
aws_url = "https://ip-ranges.amazonaws.com/ip-ranges.json"
aws_ips = requests.get(aws_url, allow_redirects=True).json()

for item in aws_ips["prefixes"]:
if item.get("service") in ("CODEBUILD") and item.get("region") == self.region:
aws_cidrs_list.append(item["ip_prefix"])

return aws_cidrs_list
12 changes: 12 additions & 0 deletions modules/compute/eks/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def stack_defaults():
os.environ["SEEDFARMER_PARAMETER_DATAPLANE_SUBNET_IDS"] = json.dumps(["subnet-12345", "subnet-54321"])
os.environ["SEEDFARMER_PARAMETER_CONTROLPLANE_SUBNET_IDS"] = json.dumps(["subnet-12345", "subnet-54321"])
os.environ["SEEDFARMER_PARAMETER_EKS_VERSION"] = "1.29"
os.environ["SEEDFARMER_PARAMETER_MOUNTPOINT_BUCKETS"] = json.dumps(["test-bucket1"])
os.environ["SEEDFARMER_PARAMETER_EKS_COMPUTE"] = json.dumps(
{
"eks_nodegroup_config": [
Expand All @@ -44,6 +45,7 @@ def stack_defaults():
"eks_node_spot": "False",
"eks_api_endpoint_private": "False",
"eks_secrets_envelope_encryption": "True",
"ips_to_whitelist": ["/test/sample1", "/test/sample2"],
}
)
os.environ["SEEDFARMER_PARAMETER_EKS_ADDONS"] = json.dumps(
Expand Down Expand Up @@ -157,6 +159,15 @@ def test_controlplane_subnet_ids(stack_defaults):
assert os.environ["SEEDFARMER_PARAMETER_CONTROLPLANE_SUBNET_IDS"] == ["subnet-12345", "subnet-54321"]


def test_mp_s3_buckets(stack_defaults):
del os.environ["SEEDFARMER_PARAMETER_MOUNTPOINT_BUCKETS"]

with pytest.raises(Exception):
import app # noqa: F401

assert os.environ["SEEDFARMER_PARAMETER_MOUNTPOINT_BUCKETS"] == ["test-bucket1"]


def test_eks_compute(stack_defaults):
del os.environ["SEEDFARMER_PARAMETER_EKS_COMPUTE"]

Expand All @@ -177,6 +188,7 @@ def test_eks_compute(stack_defaults):
"eks_node_spot": "False",
"eks_api_endpoint_private": "False",
"eks_secrets_envelope_encryption": "True",
"ips_to_whitelist": ["/test/sample1", "/test/sample2"],
}


Expand Down
31 changes: 30 additions & 1 deletion modules/compute/eks/tests/test_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def test_synthesize_stack(stack_defaults):
"eks_node_spot": "False",
"eks_api_endpoint_private": "False",
"eks_secrets_envelope_encryption": "True",
"ips_to_whitelist": ["/test/sample1", "/test/sample2"],
}

eks_addons_config = {
Expand Down Expand Up @@ -138,4 +139,32 @@ def test_synthesize_stack(stack_defaults):
},
)

# template.has_resource_properties("Custom::AWSCDK-EKS-Cluster")
# EKS Cluster Creator
template.resource_count_is("Custom::AWSCDK-EKS-Cluster", 1)

# Autoscaling group
# template.resource_count_is("AWS::AutoScaling::AutoScalingGroup", 1)

# Helm charts
template.resource_count_is("Custom::AWSCDK-EKS-HelmChart", 18)

# Security groups
template.resource_count_is("AWS::EC2::SecurityGroup", 1)

# Provider Lambda
template.resource_count_is("AWS::Lambda::Function", 2)

# Cluster Creator role
template.has_resource_properties(
"AWS::IAM::Role",
{
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRoleWithWebIdentity",
"Effect": "Allow",
},
],
},
},
)

0 comments on commit dca0f5c

Please sign in to comment.