diff --git a/docs/patterns/aws-vpc-cni-network-policy.md b/docs/patterns/aws-vpc-cni-network-policy.md new file mode 100644 index 0000000000..4ea881ace2 --- /dev/null +++ b/docs/patterns/aws-vpc-cni-network-policy.md @@ -0,0 +1,7 @@ +--- +title: AWS VPC CNI Network Policy +--- + +{% + include-markdown "../../patterns/aws-vpc-cni-network-policy/README.md" +%} diff --git a/patterns/aws-vpc-cni-network-policy/README.md b/patterns/aws-vpc-cni-network-policy/README.md new file mode 100644 index 0000000000..1a41550939 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/README.md @@ -0,0 +1,51 @@ +# Amazon EKS Cluster w/ Network Policies + +This pattern demonstrates an EKS cluster that uses the native Network Policy support provided by the Amazon VPC CNI (1.14.0 or higher). + +- [Documentation](https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy.html) +- [Launch Blog](https://aws.amazon.com/blogs/containers/amazon-vpc-cni-now-supports-kubernetes-network-policies/) + +## Scenario + +This pattern deploys an Amazon EKS Cluster with Network Policies support implemented by the Amazon VPC CNI. Further it deploys a simple demo application (distributed as a Helm Chart) and some sample Network Policies to restrict the traffic between different components of the application. + +For a detailed description of the demo application and the Network Policies, please refer to the Stars demo of network policy section in the official [Documentation](https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy.html). + +## Deploy + +See [here](https://aws-ia.github.io/terraform-aws-eks-blueprints/getting-started/#prerequisites) for the prerequisites and steps to deploy this pattern. + +## Validate + +1. List out the pods running currently: + + ```sh + kubectl get pods -A + ``` + + ```text + NAMESPACE NAME READY STATUS RESTARTS AGE + [...] + client client-xlffc 1/1 Running 0 5m19s + [...] + management-ui management-ui-qrb2g 1/1 Running 0 5m24s + stars backend-sz87q 1/1 Running 0 5m23s + stars frontend-cscnf 1/1 Running 0 5m21s + [...] + ``` + + In your output, you should see pods in the namespaces shown in the following output. The NAMES of your pods and the number of pods in the READY column are different than those in the following output. Don't continue until you see pods with similar names and they all have Running in the STATUS column. + +2. Connect to the management user interface using the EXTERNAL IP of the running service and observe the traffic flow and restrictions based on the Network Policies deployed: + + ```sh + kubectl get service/management-ui -n management-ui + ``` + + Open the browser based on the URL obtained from the previous step to see the connection map and restrictions put in place by the Network Policies deployed. + +## Destroy + +{% + include-markdown "../../docs/_partials/destroy.md" +%} diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/.helmignore b/patterns/aws-vpc-cni-network-policy/charts/demo-application/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/Chart.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/Chart.yaml new file mode 100644 index 0000000000..7a8d00dd4e --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: demo-application +description: A Helm chart to deploy the demo-application +type: application +version: 1.0.0 \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/backend-deploy.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/backend-deploy.yaml new file mode 100644 index 0000000000..e3c3c1d465 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/backend-deploy.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend + namespace: stars +spec: + replicas: 1 + selector: + matchLabels: + role: backend + template: + metadata: + labels: + role: backend + spec: + containers: + - name: backend + image: calico/star-probe:v0.1.0 + imagePullPolicy: Always + command: + - probe + - --http-port=6379 + - --urls=http://frontend.stars:80/status,http://backend.stars:6379/status,http://client.client:9000/status + ports: + - containerPort: 6379 \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/backend-svc.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/backend-svc.yaml new file mode 100644 index 0000000000..5a579569ad --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/backend-svc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: backend + namespace: stars +spec: + ports: + - port: 6379 + targetPort: 6379 + selector: + role: backend \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-deploy.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-deploy.yaml new file mode 100644 index 0000000000..8d763dcede --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-deploy.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: client + namespace: client +spec: + replicas: 1 + selector: + matchLabels: + role: client + template: + metadata: + labels: + role: client + spec: + containers: + - name: client + image: calico/star-probe:v0.1.0 + imagePullPolicy: Always + command: + - probe + - --urls=http://frontend.stars:80/status,http://backend.stars:6379/status + ports: + - containerPort: 9000 \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-ns.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-ns.yaml new file mode 100644 index 0000000000..91f714e9cb --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-ns.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: client + labels: + role: client \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-svc.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-svc.yaml new file mode 100644 index 0000000000..cfd20be303 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/client-svc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: client + namespace: client +spec: + ports: + - port: 9000 + targetPort: 9000 + selector: + role: client \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/frontend-deploy.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/frontend-deploy.yaml new file mode 100644 index 0000000000..c1b0762c32 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/frontend-deploy.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend + namespace: stars +spec: + replicas: 1 + selector: + matchLabels: + role: frontend + template: + metadata: + labels: + role: frontend + spec: + containers: + - name: frontend + image: calico/star-probe:v0.1.0 + imagePullPolicy: Always + command: + - probe + - --http-port=80 + - --urls=http://frontend.stars:80/status,http://backend.stars:6379/status,http://client.client:9000/status + ports: + - containerPort: 80 \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/frontend-svc.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/frontend-svc.yaml new file mode 100644 index 0000000000..08fafd487b --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/frontend-svc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: frontend + namespace: stars +spec: + ports: + - port: 80 + targetPort: 80 + selector: + role: frontend \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-deploy.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-deploy.yaml new file mode 100644 index 0000000000..555f338290 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-deploy.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: management-ui + namespace: management-ui +spec: + replicas: 1 + selector: + matchLabels: + role: management-ui + template: + metadata: + labels: + role: management-ui + spec: + containers: + - name: management-ui + image: calico/star-collect:v0.1.0 + imagePullPolicy: Always + ports: + - containerPort: 9001 \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-ns.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-ns.yaml new file mode 100644 index 0000000000..ef0a8ec158 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-ns.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: management-ui + labels: + role: management-ui \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-svc.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-svc.yaml new file mode 100644 index 0000000000..09b850d8d2 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/management-ui-svc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: management-ui + namespace: management-ui +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 9001 + selector: + role: management-ui \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/stars-ns.yaml b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/stars-ns.yaml new file mode 100644 index 0000000000..de71efa857 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/charts/demo-application/templates/stars-ns.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: stars \ No newline at end of file diff --git a/patterns/aws-vpc-cni-network-policy/main.tf b/patterns/aws-vpc-cni-network-policy/main.tf new file mode 100644 index 0000000000..204407c62f --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/main.tf @@ -0,0 +1,289 @@ +provider "aws" { + region = local.region +} + +provider "kubernetes" { + host = module.eks.cluster_endpoint + cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) + + exec { + api_version = "client.authentication.k8s.io/v1beta1" + command = "aws" + # This requires the awscli to be installed locally where Terraform is executed + args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] + } +} + +provider "helm" { + kubernetes { + host = module.eks.cluster_endpoint + cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) + + exec { + api_version = "client.authentication.k8s.io/v1beta1" + command = "aws" + # This requires the awscli to be installed locally where Terraform is executed + args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] + } + } +} + +data "aws_availability_zones" "available" {} + +locals { + name = basename(path.cwd) + region = "us-west-2" + + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + + tags = { + Blueprint = local.name + GithubRepo = "github.com/aws-ia/terraform-aws-eks-blueprints" + } +} + +################################################################################ +# Cluster +################################################################################ + +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 19.16" + + cluster_name = local.name + cluster_version = "1.27" # Must be 1.25 or higher + cluster_endpoint_public_access = true + + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.private_subnets + + eks_managed_node_groups = { + initial = { + instance_types = ["m5.large"] + + min_size = 3 + max_size = 10 + desired_size = 5 + } + } + + tags = local.tags +} + +################################################################################ +# Supporting Resources +################################################################################ + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.0" + + name = local.name + cidr = local.vpc_cidr + + azs = local.azs + private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] + public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] + + enable_nat_gateway = true + single_nat_gateway = true + + public_subnet_tags = { + "kubernetes.io/role/elb" = 1 + } + + private_subnet_tags = { + "kubernetes.io/role/internal-elb" = 1 + } + + tags = local.tags +} + +################################################################################ +# EKS Addons (demo application) +################################################################################ + +module "addons" { + source = "aws-ia/eks-blueprints-addons/aws" + version = "~> 1.0" + + cluster_name = module.eks.cluster_name + cluster_endpoint = module.eks.cluster_endpoint + cluster_version = module.eks.cluster_version + oidc_provider_arn = module.eks.oidc_provider_arn + + # EKS Addons + eks_addons = { + coredns = {} + kube-proxy = {} + vpc-cni = { + preserve = true + most_recent = true # Must be 1.14.0 or higher + + timeouts = { + create = "25m" + delete = "10m" + } + + # Must enable network policy support + configuration_values = jsonencode({ + enableNetworkPolicy : "true", + }) + } + } + + # Deploy demo-application + helm_releases = { + demo-application = { + description = "A Helm chart to deploy the network policy demo application" + namespace = "default" + chart = "./charts/demo-application" + } + } + + tags = local.tags +} + +################################################################################ +# Restrict traffic flow using Network Policies +################################################################################ + +# Block all ingress and egress traffic within the stars namespace +resource "kubernetes_network_policy_v1" "default_deny_stars" { + metadata { + name = "default-deny" + namespace = "stars" + } + spec { + policy_types = ["Ingress"] + pod_selector { + match_labels = {} + } + } + depends_on = [module.addons] +} + +# Block all ingress and egress traffic within the client namespace +resource "kubernetes_network_policy_v1" "default_deny_client" { + metadata { + name = "default-deny" + namespace = "client" + } + spec { + policy_types = ["Ingress"] + pod_selector { + match_labels = {} + } + } + depends_on = [module.addons] +} + +# Allow the management-ui to access the star application pods +resource "kubernetes_network_policy_v1" "allow_ui_to_stars" { + metadata { + name = "allow-ui" + namespace = "stars" + } + spec { + policy_types = ["Ingress"] + pod_selector { + match_labels = {} + } + ingress { + from { + namespace_selector { + match_labels = { + role = "management-ui" + } + } + } + } + } + depends_on = [module.addons] +} + +# Allow the management-ui to access the client application pods +resource "kubernetes_network_policy_v1" "allow_ui_to_client" { + metadata { + name = "allow-ui" + namespace = "client" + } + spec { + policy_types = ["Ingress"] + pod_selector { + match_labels = {} + } + ingress { + from { + namespace_selector { + match_labels = { + role = "management-ui" + } + } + } + } + } + depends_on = [module.addons] +} + +# Allow the frontend pod to access the backend pod within the stars namespace +resource "kubernetes_network_policy_v1" "allow_frontend_to_backend" { + metadata { + name = "backend-policy" + namespace = "stars" + } + spec { + policy_types = ["Ingress"] + pod_selector { + match_labels = { + role = "backend" + } + } + ingress { + from { + pod_selector { + match_labels = { + role = "frontend" + } + } + } + ports { + protocol = "TCP" + port = "6379" + } + } + } + depends_on = [module.addons] +} + +# Allow the client pod to access the frontend pod within the stars namespace +resource "kubernetes_network_policy_v1" "allow_client_to_backend" { + metadata { + name = "frontend-policy" + namespace = "stars" + } + + spec { + policy_types = ["Ingress"] + pod_selector { + match_labels = { + role = "frontend" + } + } + ingress { + from { + namespace_selector { + match_labels = { + role = "client" + } + } + } + ports { + protocol = "TCP" + port = "80" + } + } + } + depends_on = [module.addons] +} diff --git a/patterns/aws-vpc-cni-network-policy/outputs.tf b/patterns/aws-vpc-cni-network-policy/outputs.tf new file mode 100644 index 0000000000..c952ef95d0 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/outputs.tf @@ -0,0 +1,4 @@ +output "configure_kubectl" { + description = "Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig" + value = "aws eks update-kubeconfig --name ${module.eks.cluster_name} --alias ${module.eks.cluster_name} --region ${local.region}" +} diff --git a/patterns/aws-vpc-cni-network-policy/variables.tf b/patterns/aws-vpc-cni-network-policy/variables.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/patterns/aws-vpc-cni-network-policy/versions.tf b/patterns/aws-vpc-cni-network-policy/versions.tf new file mode 100644 index 0000000000..4b98ab82b1 --- /dev/null +++ b/patterns/aws-vpc-cni-network-policy/versions.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.47" + } + helm = { + source = "hashicorp/helm" + version = ">= 2.9" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.20" + } + } +}