diff --git a/policies/calico/calico-policy-tutorial/00-install-calicoctl.sh b/policies/calico/calico-policy-tutorial/00-install-calicoctl.sh new file mode 100755 index 0000000..5e69717 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/00-install-calicoctl.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/operations/calicoctl/install +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# calicoctl is the command line interface used to manage Calico resources such as +# GlobalNetworkPolicy and the projectcalico.org/v3 NetworkPolicy used in this tutorial. + +# Variables +CALICOCTL_VERSION=$(curl -s https://api.github.com/repos/projectcalico/calico/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")') +CLI_ARCH=amd64 + +# Detect the CPU architecture so the correct calicoctl binary is downloaded +if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi + +# Download the calicoctl binary for the latest release and the detected architecture +curl -L --fail --remote-name https://github.com/projectcalico/calico/releases/download/${CALICOCTL_VERSION}/calicoctl-linux-${CLI_ARCH} + +# Make the binary executable and move it onto the PATH +chmod +x calicoctl-linux-${CLI_ARCH} +sudo mv calicoctl-linux-${CLI_ARCH} /usr/local/bin/calicoctl + +# Verify the installation +calicoctl version diff --git a/policies/calico/calico-policy-tutorial/01-deploy-demo.sh b/policies/calico/calico-policy-tutorial/01-deploy-demo.sh new file mode 100755 index 0000000..699ee16 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/01-deploy-demo.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial +# https://docs.tigera.io/calico/latest/network-policy/get-started/kubernetes-policy/kubernetes-policy-advanced + +# Variables +namespace="advanced-policy-demo" +template="demo.yaml" + +# Deploy the demo workloads (namespace, NGINX deployment + service, and the +# busybox "access" pod). The namespace is declared in the manifest itself. +kubectl apply -f $template + +# Wait for the workloads to be ready before running the connectivity checks +kubectl wait --namespace $namespace --for=condition=Available deployment/nginx --timeout=120s +kubectl wait --namespace $namespace --for=condition=Ready pod/access --timeout=120s + +# Check the status of the pods and services +kubectl get pods,svc -n $namespace diff --git a/policies/calico/calico-policy-tutorial/02-verify-access-allowed.sh b/policies/calico/calico-policy-tutorial/02-verify-access-allowed.sh new file mode 100755 index 0000000..834f727 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/02-verify-access-allowed.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# Before any policy is applied, all ingress and egress traffic is allowed. +# From the "access" pod both the in-cluster NGINX service and the public internet +# (google.com) should be reachable. + +# Variables +namespace="advanced-policy-demo" + +# Access the NGINX service by name (in-cluster). Expected: NGINX welcome page HTML. +echo "Testing access -> nginx (expected: ALLOWED)" +kubectl exec -n $namespace access -- wget -q --timeout=5 nginx -O - | head -5 + +# Access the public internet. Expected: google.com home page HTML. +echo "Testing access -> google.com (expected: ALLOWED)" +kubectl exec -n $namespace access -- wget -q --timeout=5 google.com -O - | head -5 diff --git a/policies/calico/calico-policy-tutorial/03-create-default-deny-policy.sh b/policies/calico/calico-policy-tutorial/03-create-default-deny-policy.sh new file mode 100755 index 0000000..9144fab --- /dev/null +++ b/policies/calico/calico-policy-tutorial/03-create-default-deny-policy.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# Variables +template="default-deny.yaml" + +# Apply the default-deny GlobalNetworkPolicy to lock down all ingress and egress +# traffic for every namespace except kube-system, calico-system and calico-apiserver. +# GlobalNetworkPolicy is a Calico v3 resource, so it is applied with calicoctl. +calicoctl apply --allow-version-mismatch -f $template 2>/dev/null diff --git a/policies/calico/calico-policy-tutorial/04-verify-access-denied.sh b/policies/calico/calico-policy-tutorial/04-verify-access-denied.sh new file mode 100755 index 0000000..fa72755 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/04-verify-access-denied.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# With the default-deny GlobalNetworkPolicy in place, all traffic that is not +# explicitly allowed is now blocked. From the "access" pod neither the NGINX +# service nor the public internet should be reachable. +# +# DNS resolution itself is blocked too, so name lookups fail with "bad address" +# rather than a timeout. + +# Variables +namespace="advanced-policy-demo" + +# Access the NGINX service by name. Expected: wget: bad address 'nginx' +echo "Testing access -> nginx (expected: bad address 'nginx')" +kubectl exec -n $namespace access -- wget -q --timeout=5 nginx -O - | head -5 + +# Access the public internet. Expected: wget: bad address 'google.com' +echo "Testing access -> google.com (expected: bad address 'google.com')" +kubectl exec -n $namespace access -- wget -q --timeout=5 google.com -O - | head -5 diff --git a/policies/calico/calico-policy-tutorial/05-create-allow-busybox-egress-policy.sh b/policies/calico/calico-policy-tutorial/05-create-allow-busybox-egress-policy.sh new file mode 100755 index 0000000..8198998 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/05-create-allow-busybox-egress-policy.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# Variables +template="allow-busybox-egress.yaml" + +# Apply a namespaced Calico NetworkPolicy that allows all egress traffic from the +# busybox "access" pod (run=access), overriding the default-deny for egress on +# that pod. Applied with calicoctl because it is a projectcalico.org/v3 resource. +calicoctl apply --allow-version-mismatch -f $template 2>/dev/null diff --git a/policies/calico/calico-policy-tutorial/06-verify-egress-allowed.sh b/policies/calico/calico-policy-tutorial/06-verify-egress-allowed.sh new file mode 100755 index 0000000..317a36b --- /dev/null +++ b/policies/calico/calico-policy-tutorial/06-verify-egress-allowed.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# Egress from the "access" pod is now allowed, so the public internet is reachable. +# However the NGINX service is still NOT reachable: although egress from "access" +# is permitted, the default-deny still blocks ingress to the NGINX pod (which we +# open up in the next step). + +# Variables +namespace="advanced-policy-demo" + +# Access the public internet. Expected: google.com home page HTML (ALLOWED). +echo "Testing access -> google.com (expected: ALLOWED)" +kubectl exec -n $namespace access -- wget -q --timeout=5 google.com -O - | head -5 + +# Access the NGINX service. Expected: wget: download timed out (ingress still denied). +echo "Testing access -> nginx (expected: DENIED - ingress to nginx not yet allowed)" +kubectl exec -n $namespace access -- wget -q --timeout=5 nginx -O - | head -5 diff --git a/policies/calico/calico-policy-tutorial/07-create-allow-nginx-ingress-policy.sh b/policies/calico/calico-policy-tutorial/07-create-allow-nginx-ingress-policy.sh new file mode 100755 index 0000000..bd1a564 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/07-create-allow-nginx-ingress-policy.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# Variables +template="allow-nginx-ingress.yaml" + +# Apply a namespaced Calico NetworkPolicy that allows ingress to the NGINX pods +# (app=nginx) from the "access" pod (run=access). Applied with calicoctl because +# it is a projectcalico.org/v3 resource. +calicoctl apply --allow-version-mismatch -f $template 2>/dev/null diff --git a/policies/calico/calico-policy-tutorial/08-verify-ingress-allowed.sh b/policies/calico/calico-policy-tutorial/08-verify-ingress-allowed.sh new file mode 100755 index 0000000..0831814 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/08-verify-ingress-allowed.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# Both policies are now in place: egress from "access" is allowed and ingress to +# NGINX from "access" is allowed. The "access" pod can therefore reach both the +# NGINX service and the public internet, while all other traffic stays denied by +# the default-deny GlobalNetworkPolicy. + +# Variables +namespace="advanced-policy-demo" + +# Access the NGINX service. Expected: NGINX welcome page HTML (ALLOWED). +echo "Testing access -> nginx (expected: ALLOWED)" +kubectl exec -n $namespace access -- wget -q --timeout=5 nginx -O - | head -5 + +# Access the public internet. Expected: google.com home page HTML (ALLOWED). +echo "Testing access -> google.com (expected: ALLOWED)" +kubectl exec -n $namespace access -- wget -q --timeout=5 google.com -O - | head -5 diff --git a/policies/calico/calico-policy-tutorial/09-get-policies.sh b/policies/calico/calico-policy-tutorial/09-get-policies.sh new file mode 100755 index 0000000..7add513 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/09-get-policies.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# Variables +namespace="advanced-policy-demo" + +# List the global (non-namespaced) default-deny policy +echo "GlobalNetworkPolicy:" +calicoctl get globalnetworkpolicy -o wide + +# List the namespaced Calico NetworkPolicies in the demo namespace +echo "NetworkPolicy in namespace [$namespace]:" +calicoctl get networkpolicy -n $namespace -o wide + +# Show the full YAML definition of the default-deny global policy +echo "default-deny definition:" +calicoctl get globalnetworkpolicy default-deny -o yaml diff --git a/policies/calico/calico-policy-tutorial/10-cleanup.sh b/policies/calico/calico-policy-tutorial/10-cleanup.sh new file mode 100755 index 0000000..0191202 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/10-cleanup.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial + +# Variables +namespace="advanced-policy-demo" + +# Delete the namespaced Calico NetworkPolicies +calicoctl delete networkpolicy allow-busybox-egress -n $namespace +calicoctl delete networkpolicy allow-nginx-ingress -n $namespace + +# Delete the global default-deny policy (gnp is the short name for GlobalNetworkPolicy) +calicoctl delete gnp default-deny + +# Delete the demo namespace and all the workloads it contains +kubectl delete ns $namespace diff --git a/policies/calico/calico-policy-tutorial/allow-busybox-egress.yaml b/policies/calico/calico-policy-tutorial/allow-busybox-egress.yaml new file mode 100644 index 0000000..fcbd35a --- /dev/null +++ b/policies/calico/calico-policy-tutorial/allow-busybox-egress.yaml @@ -0,0 +1,27 @@ +# Allow all egress traffic from the busybox "access" pod. +# +# This is a namespaced Calico NetworkPolicy (projectcalico.org/v3). It selects the +# pod labelled run=access and permits all of its outbound (Egress) connections, +# overriding the default-deny for egress on that pod only. +# +# For a production workload you would normally make this rule far more restrictive +# and only allow egress to the specific destinations the workload needs. Because +# this is just a probe pod, we allow all egress so we can explore what is reachable. +# +# Note: allowing egress from "access" lets it reach the internet (e.g. google.com), +# but it still cannot reach the NGINX pod until ingress to NGINX is allowed too +# (see allow-nginx-ingress.yaml). +# +# For more information, see: +# https://docs.tigera.io/calico/latest/reference/resources/networkpolicy +apiVersion: projectcalico.org/v3 +kind: NetworkPolicy +metadata: + name: allow-busybox-egress + namespace: advanced-policy-demo +spec: + selector: run == 'access' + types: + - Egress + egress: + - action: Allow diff --git a/policies/calico/calico-policy-tutorial/allow-nginx-ingress.yaml b/policies/calico/calico-policy-tutorial/allow-nginx-ingress.yaml new file mode 100644 index 0000000..1cc7f81 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/allow-nginx-ingress.yaml @@ -0,0 +1,26 @@ +# Allow ingress traffic to NGINX from the busybox "access" pod. +# +# This namespaced Calico NetworkPolicy (projectcalico.org/v3) selects the NGINX +# pods (app=nginx) and permits inbound (Ingress) connections, but only from pods +# labelled run=access. This punches a hole in the default-deny for ingress to +# NGINX, scoped to the "access" pod as the source. +# +# Combined with allow-busybox-egress.yaml (egress from access) this completes the +# path: the access pod can now reach the NGINX service, while everything else +# remains denied by the default-deny GlobalNetworkPolicy. +# +# For more information, see: +# https://docs.tigera.io/calico/latest/reference/resources/networkpolicy +apiVersion: projectcalico.org/v3 +kind: NetworkPolicy +metadata: + name: allow-nginx-ingress + namespace: advanced-policy-demo +spec: + selector: app == 'nginx' + types: + - Ingress + ingress: + - action: Allow + source: + selector: run == 'access' diff --git a/policies/calico/calico-policy-tutorial/default-deny.yaml b/policies/calico/calico-policy-tutorial/default-deny.yaml new file mode 100644 index 0000000..4a7eeb5 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/default-deny.yaml @@ -0,0 +1,25 @@ +# Default-deny GlobalNetworkPolicy. +# +# This is a Calico-only resource (it has no Kubernetes NetworkPolicy equivalent): +# a GlobalNetworkPolicy is NOT namespaced and applies to every pod that matches +# the selector across the whole cluster. A single Kubernetes NetworkPolicy would +# have to be recreated in every namespace to achieve the same effect. +# +# It implements a zero-trust posture: once applied, any Ingress or Egress traffic +# that is not explicitly allowed by another policy is denied. +# +# The kube-system, calico-system and calico-apiserver namespaces are excluded so +# the default deny does not break Kubernetes itself or Calico's own components. +# +# For more information, see: +# https://docs.tigera.io/calico/latest/reference/resources/globalnetworkpolicy +# https://docs.tigera.io/calico/latest/network-policy/adopt-zero-trust +apiVersion: projectcalico.org/v3 +kind: GlobalNetworkPolicy +metadata: + name: default-deny +spec: + selector: projectcalico.org/namespace not in {'kube-system', 'calico-system', 'calico-apiserver'} + types: + - Ingress + - Egress diff --git a/policies/calico/calico-policy-tutorial/demo.yaml b/policies/calico/calico-policy-tutorial/demo.yaml new file mode 100644 index 0000000..b206ba8 --- /dev/null +++ b/policies/calico/calico-policy-tutorial/demo.yaml @@ -0,0 +1,74 @@ +# This manifest deploys the demo workloads used by the Calico policy tutorial: +# - the advanced-policy-demo namespace +# - a plain NGINX deployment and ClusterIP service listening on port 80 +# - a long-running busybox "access" pod used to probe connectivity +# +# The tutorial normally creates the NGINX deployment with imperative kubectl +# commands and runs an interactive busybox pod. Here we declare everything as +# manifests, and keep the busybox pod alive with a sleep loop so the connectivity +# checks can be run non-interactively with "kubectl exec". +# +# For more information, see: +# https://docs.tigera.io/calico/latest/network-policy/get-started/calico-policy/calico-policy-tutorial +--- +apiVersion: v1 +kind: Namespace +metadata: + name: advanced-policy-demo +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx + namespace: advanced-policy-demo + labels: + app: nginx +spec: + replicas: 1 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + # The "app == 'nginx'" Calico selector in allow-nginx-ingress.yaml + # matches this label. + app: nginx + spec: + containers: + - name: nginx + image: nginx + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx + namespace: advanced-policy-demo + labels: + app: nginx +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Pod +metadata: + name: access + namespace: advanced-policy-demo + labels: + # The "run == 'access'" Calico selector used by the egress and ingress + # policies matches this label. + run: access +spec: + containers: + - name: access + image: busybox + # Keep the pod running so it can be used to test policy access with + # "kubectl exec" throughout the tutorial. + command: ["sh", "-c", "sleep infinity"] diff --git a/policies/cilium/egress-tutorial/00-install-cilium-hubble-cli.sh b/policies/cilium/egress-tutorial/00-install-cilium-hubble-cli.sh new file mode 100755 index 0000000..55e5ee1 --- /dev/null +++ b/policies/cilium/egress-tutorial/00-install-cilium-hubble-cli.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# For more information, see https://docs.cilium.io/en/latest/gettingstarted/k8s-install-default/#install-the-cilium-cli + +CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/master/stable.txt) +CLI_ARCH=amd64 +if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi +curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum} +sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum +sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin +rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum} + +HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt) +HUBBLE_ARCH=amd64 +if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi +curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum} +sha256sum --check hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum +sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin +rm hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum} diff --git a/policies/cilium/egress-tutorial/01-deploy-demo.sh b/policies/cilium/egress-tutorial/01-deploy-demo.sh new file mode 100755 index 0000000..06e1f3a --- /dev/null +++ b/policies/cilium/egress-tutorial/01-deploy-demo.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# For more information, see: +# https://docs.cilium.io/en/latest/security/tutorial-toc/ +# https://docs.cilium.io/en/latest/security/dns/ +# https://docs.cilium.io/en/latest/security/policy/#id1 +# https://cilium.io/blog/2017/5/4/demo-may-the-force-be-with-you/ + +# Variables +namespace="starwars" +template="dns-sw-app.yaml" + +# Check if the namespace already exists in the cluster +result=$(kubectl get namespace -o 'jsonpath={.items[?(@.metadata.name=="'$namespace'")].metadata.name'}) + +if [[ -n $result ]]; then + echo "[$namespace] namespace already exists in the cluster" +else + # Create the namespace for your ingress resources + echo "[$namespace] namespace does not exist in the cluster" + echo "Creating [$namespace] namespace in the cluster..." + kubectl create namespace $namespace +fi + +# Deploy the demo +kubectl apply -n $namespace -f $template + +# Check the status of the pods and services +kubectl get pods,svc -n $namespace diff --git a/policies/cilium/egress-tutorial/02-create-dns-matchname-policy.sh b/policies/cilium/egress-tutorial/02-create-dns-matchname-policy.sh new file mode 100755 index 0000000..f4691cb --- /dev/null +++ b/policies/cilium/egress-tutorial/02-create-dns-matchname-policy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" +template="dns-matchname.yaml" + +# Create L3\L4 rule +kubectl apply -n $namespace -f $template diff --git a/policies/cilium/egress-tutorial/03-create-dns-pattern-policy.sh b/policies/cilium/egress-tutorial/03-create-dns-pattern-policy.sh new file mode 100755 index 0000000..7947b52 --- /dev/null +++ b/policies/cilium/egress-tutorial/03-create-dns-pattern-policy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" +template="dns-pattern.yaml" + +# Create L3\L4 rule +kubectl apply -n $namespace -f $template diff --git a/policies/cilium/egress-tutorial/04-create-dns-port-policy.sh b/policies/cilium/egress-tutorial/04-create-dns-port-policy.sh new file mode 100755 index 0000000..a751d28 --- /dev/null +++ b/policies/cilium/egress-tutorial/04-create-dns-port-policy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" +template="dns-port.yaml" + +# Create L3\L4 rule +kubectl apply -n $namespace -f $template diff --git a/policies/cilium/egress-tutorial/05-get-policy.sh b/policies/cilium/egress-tutorial/05-get-policy.sh new file mode 100755 index 0000000..35212da --- /dev/null +++ b/policies/cilium/egress-tutorial/05-get-policy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" +policy="fqdn" + +# Describe Cilium network policy +kubectl get cnp -n $namespace $policy -o yaml diff --git a/policies/cilium/egress-tutorial/06-cilium-endpoint-list.sh b/policies/cilium/egress-tutorial/06-cilium-endpoint-list.sh new file mode 100755 index 0000000..2061240 --- /dev/null +++ b/policies/cilium/egress-tutorial/06-cilium-endpoint-list.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Variables +namespace="starwars" +labels="class=mediabot" + +# Get an array containing the nodes running a pod replica of the deathstar deployment +string=$(kubectl get pod -l $labels -n $namespace -o custom-columns=NODE:.spec.nodeName | grep -i -v node) +nodes=($string) + +for node in ${nodes[@]}; do + pod=$(kubectl get pods -n kube-system -l k8s-app=cilium -o name --field-selector spec.nodeName=$node | awk -F "/" '{print $2}') + echo "Pod: $pod Node: $node" + kubectl -n kube-system exec $pod -c cilium-agent -- cilium endpoint list +done diff --git a/policies/cilium/egress-tutorial/07-call-services.sh b/policies/cilium/egress-tutorial/07-call-services.sh new file mode 100755 index 0000000..dd18ee4 --- /dev/null +++ b/policies/cilium/egress-tutorial/07-call-services.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Variables +namespace="starwars" + +# Connect to pods and try to land +kubectl exec mediabot -n $namespace -- curl -I -s --connect-timeout 3 --max-time 5 https://api.github.com | head -1 # The api.github.com url does not exist +kubectl exec mediabot -n $namespace -- curl -I -s --connect-timeout 3 --max-time 5 http://api.github.com | head -1 # This call fails as the network policy allows only HTTPS traffic on port 443 +kubectl exec mediabot -n $namespace -- curl -I -s --connect-timeout 3 --max-time 5 https://github.com | head -1 # This call is not allowed by the policy because the DNS whitelist does not contain github.com, only *.github.com diff --git a/policies/cilium/egress-tutorial/dns-matchname.yaml b/policies/cilium/egress-tutorial/dns-matchname.yaml new file mode 100644 index 0000000..f0ea56e --- /dev/null +++ b/policies/cilium/egress-tutorial/dns-matchname.yaml @@ -0,0 +1,23 @@ +apiVersion: "cilium.io/v2" +kind: CiliumNetworkPolicy +metadata: + name: "fqdn" +spec: + endpointSelector: + matchLabels: + org: empire + class: mediabot + egress: + - toFQDNs: + - matchName: "api.github.com" + - toEndpoints: + - matchLabels: + "k8s:io.kubernetes.pod.namespace": kube-system + "k8s:k8s-app": kube-dns + toPorts: + - ports: + - port: "53" + protocol: ANY + rules: + dns: + - matchPattern: "*" \ No newline at end of file diff --git a/policies/cilium/egress-tutorial/dns-pattern.yaml b/policies/cilium/egress-tutorial/dns-pattern.yaml new file mode 100644 index 0000000..3ae9e7f --- /dev/null +++ b/policies/cilium/egress-tutorial/dns-pattern.yaml @@ -0,0 +1,23 @@ +apiVersion: "cilium.io/v2" +kind: CiliumNetworkPolicy +metadata: + name: "fqdn" +spec: + endpointSelector: + matchLabels: + org: empire + class: mediabot + egress: + - toFQDNs: + - matchPattern: "*.github.com" + - toEndpoints: + - matchLabels: + "k8s:io.kubernetes.pod.namespace": kube-system + "k8s:k8s-app": kube-dns + toPorts: + - ports: + - port: "53" + protocol: ANY + rules: + dns: + - matchPattern: "*" diff --git a/policies/cilium/egress-tutorial/dns-port.yaml b/policies/cilium/egress-tutorial/dns-port.yaml new file mode 100644 index 0000000..7b72a0b --- /dev/null +++ b/policies/cilium/egress-tutorial/dns-port.yaml @@ -0,0 +1,27 @@ +apiVersion: "cilium.io/v2" +kind: CiliumNetworkPolicy +metadata: + name: "fqdn" +spec: + endpointSelector: + matchLabels: + org: empire + class: mediabot + egress: + - toFQDNs: + - matchPattern: "*.github.com" + toPorts: + - ports: + - port: "443" + protocol: TCP + - toEndpoints: + - matchLabels: + "k8s:io.kubernetes.pod.namespace": kube-system + "k8s:k8s-app": kube-dns + toPorts: + - ports: + - port: "53" + protocol: ANY + rules: + dns: + - matchPattern: "*" diff --git a/policies/cilium/egress-tutorial/dns-sw-app.yaml b/policies/cilium/egress-tutorial/dns-sw-app.yaml new file mode 100644 index 0000000..6f2b226 --- /dev/null +++ b/policies/cilium/egress-tutorial/dns-sw-app.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: Pod +metadata: + name: mediabot + labels: + org: empire + class: mediabot +spec: + containers: + - name: mediabot + image: quay.io/cilium/json-mock:v1.3.8@sha256:5aad04835eda9025fe4561ad31be77fd55309af8158ca8663a72f6abb78c2603 \ No newline at end of file diff --git a/policies/cilium/ingress-tutorial/00-install-cilium-hubble-cli.sh b/policies/cilium/ingress-tutorial/00-install-cilium-hubble-cli.sh new file mode 100755 index 0000000..55e5ee1 --- /dev/null +++ b/policies/cilium/ingress-tutorial/00-install-cilium-hubble-cli.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# For more information, see https://docs.cilium.io/en/latest/gettingstarted/k8s-install-default/#install-the-cilium-cli + +CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/master/stable.txt) +CLI_ARCH=amd64 +if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi +curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum} +sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum +sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin +rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum} + +HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt) +HUBBLE_ARCH=amd64 +if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi +curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum} +sha256sum --check hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum +sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin +rm hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum} diff --git a/policies/cilium/ingress-tutorial/01-deploy-demo.sh b/policies/cilium/ingress-tutorial/01-deploy-demo.sh new file mode 100755 index 0000000..e1eced7 --- /dev/null +++ b/policies/cilium/ingress-tutorial/01-deploy-demo.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# For more information, see: +# https://docs.cilium.io/en/latest/security/tutorial-toc/ +# https://docs.cilium.io/en/latest/security/http/#deploy-the-demo-application +# https://docs.cilium.io/en/latest/security/policy/#id1 +# https://cilium.io/blog/2017/5/4/demo-may-the-force-be-with-you/ + +# Variables +namespace="starwars" +template="http-sw-app.yaml" + +# Check if the namespace already exists in the cluster +result=$(kubectl get namespace -o 'jsonpath={.items[?(@.metadata.name=="'$namespace'")].metadata.name'}) + +if [[ -n $result ]]; then + echo "[$namespace] namespace already exists in the cluster" +else + # Create the namespace for your ingress resources + echo "[$namespace] namespace does not exist in the cluster" + echo "Creating [$namespace] namespace in the cluster..." + kubectl create namespace $namespace +fi + +# Deploy the demo +kubectl apply -n $namespace -f $template + +# Check the status of the pods and services +kubectl get pods,svc -n $namespace diff --git a/policies/cilium/ingress-tutorial/02-cilium-endpoint-list.sh b/policies/cilium/ingress-tutorial/02-cilium-endpoint-list.sh new file mode 100755 index 0000000..b56a250 --- /dev/null +++ b/policies/cilium/ingress-tutorial/02-cilium-endpoint-list.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Variables +namespace="starwars" +labels="class=deathstar" + +# Get an array containing the nodes running a pod replica of the deathstar deployment +string=$(kubectl get pod -l $labels -n $namespace -o custom-columns=NODE:.spec.nodeName | grep -i -v node) +nodes=($string) + +for node in ${nodes[@]}; do + pod=$(kubectl get pods -n kube-system -l k8s-app=cilium -o name --field-selector spec.nodeName=$node | awk -F "/" '{print $2}') + echo "Pod: $pod Node: $node" + kubectl -n kube-system exec $pod -c cilium-agent -- cilium endpoint list +done diff --git a/policies/cilium/ingress-tutorial/03-cilium-policy-get.sh b/policies/cilium/ingress-tutorial/03-cilium-policy-get.sh new file mode 100755 index 0000000..c3a07a0 --- /dev/null +++ b/policies/cilium/ingress-tutorial/03-cilium-policy-get.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Each pod is represented in Cilium as an Endpoint. We can invoke the cilium tool inside the Cilium agent pod to list them. +# Cilium uses an daemonset to run an agent pod on every cluster node. +string=$(kubectl get pods -n kube-system -l k8s-app=cilium -o name | awk -F "/" '{print $2}') +pods=($string) +kubectl -n kube-system exec ${pods[0]} -c cilium-agent -- cilium policy get diff --git a/policies/cilium/ingress-tutorial/04-cilium-monitor.sh b/policies/cilium/ingress-tutorial/04-cilium-monitor.sh new file mode 100755 index 0000000..6d85d64 --- /dev/null +++ b/policies/cilium/ingress-tutorial/04-cilium-monitor.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Variables +namespace="starwars" +labels="class=deathstar" + +# Get an array containing the nodes running a pod replica of the deathstar deployment +string=$(kubectl get pod -l $labels -n $namespace -o custom-columns=NODE:.spec.nodeName | grep -i -v node) +nodes=($string) + +# Prompt the user to select one of the nodes +echo "Please select a node to monitor between those running a pod replica of the deathstar deployment:" +select node in "${nodes[@]}"; do + [[ -n $node ]] || { + echo "Invalid node. Please try again." >&2 + continue + } + break # valid node was made; exit prompt. +done + +echo "Monitoring node $node" +kubectl -n kube-system exec -it -c cilium-agent $(kubectl get pod -n kube-system -l k8s-app=cilium -o name --field-selector spec.nodeName=$node | awk -F "/" '{print $2}') -- cilium monitor -v --type l7 diff --git a/policies/cilium/ingress-tutorial/05-call-request-landing-web-method.sh b/policies/cilium/ingress-tutorial/05-call-request-landing-web-method.sh new file mode 100755 index 0000000..8f62022 --- /dev/null +++ b/policies/cilium/ingress-tutorial/05-call-request-landing-web-method.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" + +# Connect to pods and try to land +kubectl exec tiefighter -n $namespace -- curl -s -XPOST --connect-timeout 5 deathstar.$namespace.svc.cluster.local/v1/request-landing +kubectl exec xwing -n $namespace -- curl -s -XPOST --connect-timeout 5 deathstar.$namespace.svc.cluster.local/v1/request-landing diff --git a/policies/cilium/ingress-tutorial/06-call-other-web-method.sh b/policies/cilium/ingress-tutorial/06-call-other-web-method.sh new file mode 100755 index 0000000..ba95f77 --- /dev/null +++ b/policies/cilium/ingress-tutorial/06-call-other-web-method.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" + +# Connect to pods and try to land +kubectl exec tiefighter -n $namespace -- curl -s -XPUT --connect-timeout 3 deathstar.$namespace.svc.cluster.local/v1/exhaust-port +kubectl exec xwing -n $namespace -- curl -s -XPUT --connect-timeout 3 deathstar.$namespace.svc.cluster.local/v1/exhaust-port diff --git a/policies/cilium/ingress-tutorial/07-create-l3-l4-policy.sh b/policies/cilium/ingress-tutorial/07-create-l3-l4-policy.sh new file mode 100755 index 0000000..ba5774c --- /dev/null +++ b/policies/cilium/ingress-tutorial/07-create-l3-l4-policy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" +template="sw-l3-l4-policy.yaml" + +# Create L3\L4 rule +kubectl apply -n $namespace -f $template diff --git a/policies/cilium/ingress-tutorial/08-create-l3-l4-l7-policy.sh b/policies/cilium/ingress-tutorial/08-create-l3-l4-l7-policy.sh new file mode 100755 index 0000000..c8a46a2 --- /dev/null +++ b/policies/cilium/ingress-tutorial/08-create-l3-l4-l7-policy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" +template="sw-l3-l4-l7-policy.yaml" + +# Create L3\L4 rule +kubectl apply -n $namespace -f $template diff --git a/policies/cilium/ingress-tutorial/09-call-deathstar-methods-from-tiefighter.sh b/policies/cilium/ingress-tutorial/09-call-deathstar-methods-from-tiefighter.sh new file mode 100755 index 0000000..8524992 --- /dev/null +++ b/policies/cilium/ingress-tutorial/09-call-deathstar-methods-from-tiefighter.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Variables +namespace="starwars" + +# Connect to pods and try to land. The first call should succeed, while the second one should be blocked by Cilium's L7 policy. +kubectl exec tiefighter -n $namespace -- curl -s -XPOST deathstar.$namespace.svc.cluster.local/v1/request-landing +kubectl exec tiefighter -n $namespace -- curl -s -XPUT deathstar.$namespace.svc.cluster.local/v1/exhaust-port + +# The following calls should timeout, as the xwing pod is not allowed to access the deathstar service. +kubectl exec xwing -n $namespace -- curl -s -XPOST --connect-timeout 3 deathstar.$namespace.svc.cluster.local/v1/request-landing +kubectl exec xwing -n $namespace -- curl -s -XPUT --connect-timeout 3 deathstar.$namespace.svc.cluster.local/v1/exhaust-port \ No newline at end of file diff --git a/policies/cilium/ingress-tutorial/10-get-policy.sh b/policies/cilium/ingress-tutorial/10-get-policy.sh new file mode 100755 index 0000000..70e4529 --- /dev/null +++ b/policies/cilium/ingress-tutorial/10-get-policy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Variables +namespace="starwars" +policy="rule1" + +# Describe Cilium network policy +kubectl get cnp -n $namespace $policy -o yaml diff --git a/policies/cilium/ingress-tutorial/cilium_http_gsg.png b/policies/cilium/ingress-tutorial/cilium_http_gsg.png new file mode 100644 index 0000000..7b6a992 Binary files /dev/null and b/policies/cilium/ingress-tutorial/cilium_http_gsg.png differ diff --git a/policies/cilium/ingress-tutorial/cilium_http_l3_l4_gsg.png b/policies/cilium/ingress-tutorial/cilium_http_l3_l4_gsg.png new file mode 100644 index 0000000..47ba20f Binary files /dev/null and b/policies/cilium/ingress-tutorial/cilium_http_l3_l4_gsg.png differ diff --git a/policies/cilium/ingress-tutorial/cilium_http_l3_l4_l7_gsg.png b/policies/cilium/ingress-tutorial/cilium_http_l3_l4_l7_gsg.png new file mode 100644 index 0000000..bdc1c3d Binary files /dev/null and b/policies/cilium/ingress-tutorial/cilium_http_l3_l4_l7_gsg.png differ diff --git a/policies/cilium/ingress-tutorial/http-sw-app.yaml b/policies/cilium/ingress-tutorial/http-sw-app.yaml new file mode 100644 index 0000000..c268bbb --- /dev/null +++ b/policies/cilium/ingress-tutorial/http-sw-app.yaml @@ -0,0 +1,63 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: deathstar + labels: + app.kubernetes.io/name: deathstar +spec: + type: ClusterIP + ports: + - port: 80 + selector: + org: empire + class: deathstar +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deathstar + labels: + app.kubernetes.io/name: deathstar +spec: + replicas: 2 + selector: + matchLabels: + org: empire + class: deathstar + template: + metadata: + labels: + org: empire + class: deathstar + app.kubernetes.io/name: deathstar + spec: + containers: + - name: deathstar + image: docker.io/cilium/starwars +--- +apiVersion: v1 +kind: Pod +metadata: + name: tiefighter + labels: + org: empire + class: tiefighter + app.kubernetes.io/name: tiefighter +spec: + containers: + - name: spaceship + image: docker.io/tgraf/netperf +--- +apiVersion: v1 +kind: Pod +metadata: + name: xwing + labels: + org: alliance + class: xwing + app.kubernetes.io/name: xwing +spec: + containers: + - name: spaceship + image: docker.io/tgraf/netperf diff --git a/policies/cilium/ingress-tutorial/sw-l3-l4-l7-policy.yaml b/policies/cilium/ingress-tutorial/sw-l3-l4-l7-policy.yaml new file mode 100644 index 0000000..dc6bc24 --- /dev/null +++ b/policies/cilium/ingress-tutorial/sw-l3-l4-l7-policy.yaml @@ -0,0 +1,22 @@ +apiVersion: "cilium.io/v2" +kind: CiliumNetworkPolicy +metadata: + name: "rule1" +spec: + description: "L7 policy to restrict access to specific HTTP call" + endpointSelector: + matchLabels: + org: empire + class: deathstar + ingress: + - fromEndpoints: + - matchLabels: + org: empire + toPorts: + - ports: + - port: "80" + protocol: TCP + rules: + http: + - method: "POST" + path: "/v1/request-landing" diff --git a/policies/cilium/ingress-tutorial/sw-l3-l4-policy.yaml b/policies/cilium/ingress-tutorial/sw-l3-l4-policy.yaml new file mode 100644 index 0000000..d26123f --- /dev/null +++ b/policies/cilium/ingress-tutorial/sw-l3-l4-policy.yaml @@ -0,0 +1,18 @@ +apiVersion: "cilium.io/v2" +kind: CiliumNetworkPolicy +metadata: + name: "rule1" +spec: + description: "L3-L4 policy to restrict deathstar access to empire ships only" + endpointSelector: + matchLabels: + org: empire + class: deathstar + ingress: + - fromEndpoints: + - matchLabels: + org: empire + toPorts: + - ports: + - port: "80" + protocol: TCP