diff --git a/001-Lab-Setup/README.md b/001-Lab-Setup/README.md index abb3e25..b0a8098 100644 --- a/001-Lab-Setup/README.md +++ b/001-Lab-Setup/README.md @@ -22,24 +22,24 @@ Note: You may need to refresh the page a few times before seeing your Kubernetes In the navigation on the left side of the console, click `Kubernetes Engine`. Here you will find the details about the cluster and a GUI for accessing and administering workloads and services. ## Task 3: Launch Cloud Shell -There is a button titled `Activate Google Cloud Shell` located in the top-bar navigation of the console. When clicked, a terminal will appear in the lower half of the console. This gives you direct command-line access to your Kubernetes cluster. +There is a button titled `Activate Google Cloud Shell` located in the top-bar navigation of the console. When clicked, a terminal will appear in the lower half of the console. This gives you direct command-line access to your Kubernetes cluster. Cloud shell comes packaged with a beta feature called `code editor` which gives you a minimal IDE for viewing and editing files. This will be used throughout the remainder of the labs. The link is found in the upper-right hand corner of the terminal. ## Task 4: Clone the Git Repository In your home directory, we are going to pull in the documentation and source code used for the course labs. We can do this by running the following command: ``` -git clone https://github.com/ManicodeSecurity/Defending-DevOps/ +git clone https://github.com/ManicodeSecurity/Defending-DevOps/ ``` ## Task 5: Connect to your Kubernetes Cluster -Most of the tools necessary to complete the labs come pre-installed in Google Cloud Shell including `kubectl` which is used extensively to interact with your cluster. Ensure your cluster is operational by running the following commands. +Most of the tools necessary to complete the labs come pre-installed in Google Cloud Shell including `kubectl` which is used extensively to interact with your cluster. Ensure your cluster is operational by running the following commands. First, we need to use connect to the cluster using Cloud Shell. In the navigation on the left, click `Kubernetes Engine -> Cluster` then click the `Connect` button next to your cluster: ![Cluster Connect](../images/gke-connect.png) -You will then be presented with options to connect to the cluster. Click `Run in Cloud Shell`. This will open Google Cloud Shell in the same browser tab. It will also paste a command into the terminal. All you need to do now is hit enter to run the command. +You will then be presented with options to connect to the cluster. Click `Run in Cloud Shell`. This will open Google Cloud Shell in the same browser tab. It will also paste a command into the terminal. All you need to do now is hit enter to run the command. The command you are running will look like this: ``` @@ -49,4 +49,4 @@ gcloud container clusters get-credentials --zone us-west1-a You can ensure you are connected to your cluster by running the following command. This will display all of the default pods running in the cluster. ``` kubectl get pods --all-namespaces -``` \ No newline at end of file +``` diff --git a/002-Containerizing-An-Application/README.md b/002-Containerizing-An-Application/README.md index fb9c20d..fc921a8 100644 --- a/002-Containerizing-An-Application/README.md +++ b/002-Containerizing-An-Application/README.md @@ -8,7 +8,7 @@ The source code for the application located in the `src/link-unshorten` director ### Task 1: Browse the Application Open up the files in `src/link-unshorten` in your favorite IDE or the Cloud Shell editor and familiarize yourself with the application. -### Task 2: Build the Docker Image +### Task 2: Build the Docker Image In the `src/link-unshorten` directory run the following command (substituting with your own identifier) to build the image on the Cloud Shell VM: ``` docker build -t /link-unshorten:0.1 . @@ -92,8 +92,8 @@ Hint 3: Yes, the answer is commented in the source code Hint 4: You will need to run `docker stop` on the first running container before running another one with the same port ### Bonus 3: Inspect the Docker image -[dive](https://github.com/wagoodman/dive) is an OSS project that helps with visualization and optimization of images. +[dive](https://github.com/wagoodman/dive) is an OSS project that helps with visualization and optimization of images. Install `dive` in Cloud Shell and inspect the unshorten image that was created. -Hint 1: Install using the instructions for Ubuntu/Debian. \ No newline at end of file +Hint 1: Install using the instructions for Ubuntu/Debian. diff --git a/003-Cluster-Setup/README.md b/003-Cluster-Setup/README.md index 643a304..3dd2d4d 100644 --- a/003-Cluster-Setup/README.md +++ b/003-Cluster-Setup/README.md @@ -16,7 +16,7 @@ echo "Default Namespace Switched:" $(kubectl get sa default -o jsonpath='{.metad 1. `kubectl` is the command line utility that we will use to interact with our Kubernetes cluster. The first task is to view the Pods that are running on our cluster with an out-of-the-box installation. Run the following command in you terminal: ``` kubectl get pods -``` +``` 2. As you can see no pods are running. This is because we are dropped into the `default` namespace and the `default` namespace has nothing deployed to it. Try running the same command with the following argument. This will list the pods used by the Kubernetes system itself: ``` @@ -86,7 +86,7 @@ exit ### Task 3: Exposing your Pod to the World There are a variety of ways to make our Pod accessible to the outside world. A Service with the type `LoadBalancer` will be used to give our Pod a stable existence and an IP we can reach from our web browser. -The `LoadBalancer` type spins up a load balancer in GCP automatically. +The `LoadBalancer` type spins up a load balancer in GCP automatically. 1. To expose the application we create a Service with the type of LoadBalancer: ``` @@ -106,7 +106,7 @@ http://:8080/api/check?url=bit.ly/test 4. This is no way to manage a real Kubernetes cluster. Tear down your app using the following commands: ``` kubectl delete pod link-unshorten && kubectl delete svc link-unshorten -``` +``` ### Task 4: "Codifying" Your Deployment Running ad hoc commands in a terminal are no way to maintain a proper DevOps infrastructure. Kubernetes is built with "Infrastructure as Code" in mind by using manifests. Manifests can be written in JSON and YAML. We will be using YAML for all labs. @@ -124,7 +124,7 @@ kubectl create -f link-unshorten-service.yaml kubectl get pods ``` -4. Under the hood we can see the new ReplicaSet that was created. Remember, a Deployment actually creates a ReplicaSet. Deployments provide the same replication functions via ReplicaSets and also the ability to rollout changes and roll them back if necessary. +4. Under the hood we can see the new ReplicaSet that was created. Remember, a Deployment actually creates a ReplicaSet. Deployments provide the same replication functions via ReplicaSets and also the ability to rollout changes and roll them back if necessary. ``` kubectl get replicaset ``` @@ -137,14 +137,14 @@ kubectl describe svc link-unshorten-service 6. Similar to how we interacted with our application earlier, we use the IP from the above output and paste it into our browser. ``` http:///api/check?url=bit.ly/test -``` +``` ### Task 5: Scale 1. We will first increase the number of pods in our Deployment using `kubectl scale`. Note - This will not reflect what is defined in the manifest. These values will be out of sync. ``` -kubectl scale deployment/link-unshorten --replicas=4 +kubectl scale deployment/link-unshorten --replicas=4 kubectl get pods # 4 pods should be running ``` @@ -169,7 +169,7 @@ kubectl delete hpa 5. Relaunch our Deployment from the manifest file: ``` kubectl create -f link-unshorten-deployment.yaml -kubectl get pods +kubectl get pods # two pods should be running ``` @@ -181,10 +181,10 @@ kubectl replace -f link-unshorten-deployment.yaml 7. Inspect the Pods scaling. Note that others will be terminating at the same time: ``` - kubectl get pods + kubectl get pods ``` -### Multi-Conatiner Pods +### Multi-Container Pods First, Un-comment the redis container lines in the `link-unshorten-deployment.yaml` manifest to deploy a second container within our Pod. Use `kubectl replace -f link-unshorten-deployment.yaml` to commit the changes after the lines have been un-commented. @@ -207,7 +207,7 @@ exit ``` ### Bonus - A critical RCE vulnerability was just reported through a bug bounty and was fixed late into the night. Roll out a new version of the app (0.2) in your cluster to patch the vulnerability on each of your three running pods. No downtime allowed! Show the deployment history using `kubectl rollout history` + A critical RCE vulnerability was just reported through a bug bounty and was fixed late into the night. Roll out a new version of the app (0.2) in your cluster to patch the vulnerability on each of your three running pods. No downtime allowed! Show the deployment history using `kubectl rollout history` ### Bonus 2 The new version you just rolled out contains a critical bug! Quickly rollback the deployment to 0.1 (Yes, 0.1 is the vulnerable version, but this is just for practice!) @@ -221,6 +221,6 @@ echo "Default Namespace Switched:" $(kubectl get sa default -o jsonpath='{.metad ``` ### Discussion Questions -1. What would be a good piece of your application or infrastructure to start breaking up into Pods within Kubernetes? +1. What would be a good piece of your application or infrastructure to start breaking up into Pods within Kubernetes? -2. What security challenges does administering a Kubernetes cluster using a tool like kubectl present? +2. What security challenges does administering a Kubernetes cluster using a tool like kubectl present? diff --git a/003-Cluster-Setup/manifests/link-unshorten-deployment.yaml b/003-Cluster-Setup/manifests/link-unshorten-deployment.yaml index bd2a0ba..df2ad9a 100644 --- a/003-Cluster-Setup/manifests/link-unshorten-deployment.yaml +++ b/003-Cluster-Setup/manifests/link-unshorten-deployment.yaml @@ -3,7 +3,7 @@ kind: Deployment metadata: # A Deployment named link-unshorten is created using the metadata: name field name: link-unshorten - # We give the deployment a label + # We give the deployment a label labels: app: unshorten-api spec: @@ -13,7 +13,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: # We label all Pods in this deployment as app: unshorten-api @@ -34,4 +34,4 @@ spec: # ports: # - containerPort: 6379 # name: redis - # protocol: TCP \ No newline at end of file + # protocol: TCP diff --git a/003-Cluster-Setup/manifests/link-unshorten-service.yaml b/003-Cluster-Setup/manifests/link-unshorten-service.yaml index e83d63c..01c4ccf 100644 --- a/003-Cluster-Setup/manifests/link-unshorten-service.yaml +++ b/003-Cluster-Setup/manifests/link-unshorten-service.yaml @@ -9,4 +9,4 @@ spec: targetPort: 8080 protocol: TCP selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/004-Cluster-Authentication/README.md b/004-Cluster-Authentication/README.md index 1a83ec3..8c3f07a 100644 --- a/004-Cluster-Authentication/README.md +++ b/004-Cluster-Authentication/README.md @@ -13,7 +13,7 @@ Kubernetes Engine Admin Editor User 2: @manicode.us -Roles: +Roles: Minimal GKE Role Browser ``` @@ -26,11 +26,11 @@ container.clusters.getCredentials ``` ### Task 1: Launch Your Infrastructure -First, we will spin up our application in both a `development` and `production` namespace. +First, we will spin up our application in both a `development` and `production` namespace. Note: You should be logged in to Cloud Shell using the admin account provided at the beginning of class to run the following commands, NOT `@manicode.us`. -We need to retrieve the credentials of our running cluster using the following `gcloud` command. This command updates our kubeconfig in Cloud Shell file with appropriate credentials and endpoint information to point kubectl at a specific cluster in Google Kubernetes Engine. +We need to retrieve the credentials of our running cluster using the following `gcloud` command. This command updates our kubeconfig in Cloud Shell file with appropriate credentials and endpoint information to point kubectl at a specific cluster in Google Kubernetes Engine. ``` # Use gcloud get-credentials to retrieve the cert @@ -65,11 +65,11 @@ kubectl get pods --all-namespaces Take note of this process. Our user has full administrative access to our cluster due to being provisioned with the `Kubernetes Engine Admin` role. We will now see how RBAC helps give us granular access control at the object-level within our cluster. ### Task 2: Authenticate as a Restricted User -We will now log in using a separate user who has very locked down access to the entire project. In an incognito window browse to `cloud.google.com` and authenticate with the user `@manicode.us` and the same password that was provided to you for the admin user. +We will now log in using a separate user who has very locked down access to the entire project. In an incognito window browse to `cloud.google.com` and authenticate with the user `@manicode.us` and the same password that was provided to you for the admin user. -Note: *Using the same password for multiple accounts is bad. Don't do this at home.* +Note: *Using the same password for multiple accounts is bad. Don't do this at home.* -Now open up Cloud Shell and use the following `gcloud get-credentials` command to retrieve the credentials for your user so we can start interacting with the cluster. This is the same cluster you just launched the `production` and `development` infrastructure in. +Now open up Cloud Shell and use the following `gcloud get-credentials` command to retrieve the credentials for your user so we can start interacting with the cluster. This is the same cluster you just launched the `production` and `development` infrastructure in. ``` # Authenticate to the cluster @@ -80,7 +80,7 @@ Now, attempt to run some `kubectl` queries on the cluster. ``` kubectl get pods --namespace=production kubectl get pods --namespace=development -kubectl get secrets +kubectl get secrets kubectl run link-unshorten --image=jmbmxer/link-unshorten:0.1 --port=8080 ``` These should all fail with a `Forbidden` error. While @manicode.us does technically have an account on the cluster, RBAC is stopping it from accessing any of the objects. @@ -93,7 +93,7 @@ kubectl auth can-i list secrets --namespace default ``` ### Task 3: Add Yourself as `cluster-admin` -By default, User 1 will not be able to create the `roles` or `rolebindings` needed to begin building our RBAC policies. We need to ensure User 1 (our Administrator) has the appropriate access to the cluster by granting the user `cluster-admin` rights. +By default, User 1 will not be able to create the `roles` or `rolebindings` needed to begin building our RBAC policies. We need to ensure User 1 (our Administrator) has the appropriate access to the cluster by granting the user `cluster-admin` rights. `cluster-admin` is one of several Default User-facing roles included with every Kubernetes installation. They should be used with caution as many of these roles grant excessive privileges and are often abused for a quick fix. @@ -124,10 +124,10 @@ kubectl auth can-i create roles --as=root --as-group=system:authenticated --as-g yes ``` -### Task 4: Create RBAC Rules +### Task 4: Create RBAC Rules Our user `@manicode.us` is a restricted user so we only want to grant access to read pods in the `development` namespace and nothing more. We will use RBAC to enforce a policy -Now, open the file `user-role-binding.yaml` in the `manifests/role` directory and replace with the one provided to you. It will be the same as your admin account but with the word `intern` at the end (eg. `manicode0003intern@manicode.us`). +Now, open the file `user-role-binding.yaml` in the `manifests/role` directory and replace with the one provided to you. It will be the same as your admin account but with the word `intern` at the end (eg. `manicode0003intern@manicode.us`). ``` # In the manifests/role directory kubectl create -f . @@ -167,4 +167,4 @@ Our intern just got promoted to Jr. DevSecOpsSysAdminNinja! Change the permissio Don't forget to delete the `development` and `production` namespace when you are done with the Bonuses. ``` kubectl delete ns development production -``` \ No newline at end of file +``` diff --git a/004-Cluster-Authentication/manifests/development/link-unshorten-deployment.yaml b/004-Cluster-Authentication/manifests/development/link-unshorten-deployment.yaml index 49cdad8..3a73c52 100644 --- a/004-Cluster-Authentication/manifests/development/link-unshorten-deployment.yaml +++ b/004-Cluster-Authentication/manifests/development/link-unshorten-deployment.yaml @@ -4,7 +4,7 @@ metadata: # A Deployment named link-unshorten is created using the metadata: name field name: link-unshorten namespace: development - # We give the deployment a label + # We give the deployment a label labels: app: unshorten-api spec: @@ -14,7 +14,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: # We label all Pods in this deployment as app: unshorten-api @@ -35,4 +35,4 @@ spec: ports: - containerPort: 6379 name: redis - protocol: TCP \ No newline at end of file + protocol: TCP diff --git a/004-Cluster-Authentication/manifests/development/link-unshorten-ns.yaml b/004-Cluster-Authentication/manifests/development/link-unshorten-ns.yaml index b91657b..233c330 100644 --- a/004-Cluster-Authentication/manifests/development/link-unshorten-ns.yaml +++ b/004-Cluster-Authentication/manifests/development/link-unshorten-ns.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: development \ No newline at end of file + name: development diff --git a/004-Cluster-Authentication/manifests/development/link-unshorten-service.yaml b/004-Cluster-Authentication/manifests/development/link-unshorten-service.yaml index 8b768bb..80c5875 100644 --- a/004-Cluster-Authentication/manifests/development/link-unshorten-service.yaml +++ b/004-Cluster-Authentication/manifests/development/link-unshorten-service.yaml @@ -10,4 +10,4 @@ spec: targetPort: 8080 protocol: TCP selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/004-Cluster-Authentication/manifests/production/link-unshorten-deployment.yaml b/004-Cluster-Authentication/manifests/production/link-unshorten-deployment.yaml index b843d9a..21e91fe 100644 --- a/004-Cluster-Authentication/manifests/production/link-unshorten-deployment.yaml +++ b/004-Cluster-Authentication/manifests/production/link-unshorten-deployment.yaml @@ -4,7 +4,7 @@ metadata: # A Deployment named link-unshorten is created using the metadata: name field name: link-unshorten namespace: production - # We give the deployment a label + # We give the deployment a label labels: app: unshorten-api spec: @@ -14,7 +14,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: # We label all Pods in this deployment as app: unshorten-api @@ -35,4 +35,4 @@ spec: ports: - containerPort: 6379 name: redis - protocol: TCP \ No newline at end of file + protocol: TCP diff --git a/004-Cluster-Authentication/manifests/production/link-unshorten-ns.yaml b/004-Cluster-Authentication/manifests/production/link-unshorten-ns.yaml index 9b8854c..67d15e1 100644 --- a/004-Cluster-Authentication/manifests/production/link-unshorten-ns.yaml +++ b/004-Cluster-Authentication/manifests/production/link-unshorten-ns.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: production \ No newline at end of file + name: production diff --git a/004-Cluster-Authentication/manifests/production/link-unshorten-service.yaml b/004-Cluster-Authentication/manifests/production/link-unshorten-service.yaml index 23a21f2..2e6ec8b 100644 --- a/004-Cluster-Authentication/manifests/production/link-unshorten-service.yaml +++ b/004-Cluster-Authentication/manifests/production/link-unshorten-service.yaml @@ -10,4 +10,4 @@ spec: targetPort: 8080 protocol: TCP selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/004-Cluster-Authentication/manifests/role/user-role-binding.yaml b/004-Cluster-Authentication/manifests/role/user-role-binding.yaml index 00f8435..45e8f76 100644 --- a/004-Cluster-Authentication/manifests/role/user-role-binding.yaml +++ b/004-Cluster-Authentication/manifests/role/user-role-binding.yaml @@ -11,4 +11,4 @@ subjects: roleRef: kind: Role name: pod-reader - apiGroup: "" \ No newline at end of file + apiGroup: "" diff --git a/004-Cluster-Authentication/manifests/role/user-role.yaml b/004-Cluster-Authentication/manifests/role/user-role.yaml index e2f4475..3292d61 100644 --- a/004-Cluster-Authentication/manifests/role/user-role.yaml +++ b/004-Cluster-Authentication/manifests/role/user-role.yaml @@ -6,4 +6,4 @@ metadata: rules: - apiGroups: [""] # "" indicates the core API group resources: ["pods"] - verbs: ["get", "watch", "list"] \ No newline at end of file + verbs: ["get", "watch", "list"] diff --git a/005-Dashboard/README.md b/005-Dashboard/README.md index 4176d0e..b7b3b48 100644 --- a/005-Dashboard/README.md +++ b/005-Dashboard/README.md @@ -22,7 +22,7 @@ kubectl create clusterrolebinding cluster-admin-binding \ --clusterrole cluster-admin \ --user $(gcloud config get-value account) ``` - + 1. The Dashboard UI is not always deployed by default. To deploy it, run the following command in Cloud Shell: ``` @@ -32,9 +32,9 @@ kubectl create -f dashboard.yaml 2. Since Kubernetes 1.9, authentication to the dashboard is enabled by default. We need to retrieve a service account token that has the appropriate access. In Cloud Shell run the following command: -Note: The token below is auth token `kubectl` uses itself to authenticate as you. You can use any valid token to authenticate to the dashboard. +Note: The token below is auth token `kubectl` uses itself to authenticate as you. You can use any valid token to authenticate to the dashboard. -A newline may have been added to the token value - just paste it into a text editor first to ensure the token is on one line. +Since the token may span multiple lines in the Cloud Shell, a newline may have been added *within* the copied token value - just paste it into a text editor and remove whitespace to ensure the token is on one line, and then copy the result. ``` gcloud config config-helper --format=json | jq -r '.credential.access_token' @@ -53,7 +53,7 @@ https://8080-dot-4279646-dot-devshell.appspot.com/api/v1/namespaces/kube-system/ 5. Paste your token from the previous command into the dashboard to authenticate. -6. Take a look around the dashboard. What data can you extract from it? Check out the `Secrets` listed in the namespace `kube-system`. +6. Take a look around the dashboard. What data can you extract from it? Check out the `Secrets` listed in the namespace `kube-system`. ## Bonus 1 Launch and scale the unshorten-api deployment using only the dashboard. @@ -64,7 +64,7 @@ The Service Account that the dashboard uses to launch is located in the `manifes ## Bonus 3 Authenticate to the dashboard using a token that is has a more restricted RBAC policy attached (maybe an intern?). Does the dashboard look any different? -### Task 2: Cleanup +### Task 2: Cleanup Don't forget to delete the `lab005` namespace when you are done with the Bonuses. ``` kubectl delete ns lab005 && \ @@ -78,4 +78,4 @@ echo "Default Namespace Switched:" $(kubectl get sa default -o jsonpath='{.metad ## Further Reading [Heptio - Securing K8S Dashboard](https://blog.heptio.com/on-securing-the-kubernetes-dashboard-16b09b1b7aca) - [Kubernetes Dashboard Wiki - Access Control](https://github.com/kubernetes/dashboard/wiki/Access-control) \ No newline at end of file + [Kubernetes Dashboard Wiki - Access Control](https://github.com/kubernetes/dashboard/wiki/Access-control) diff --git a/005-Dashboard/manifests/dashboard.yaml b/005-Dashboard/manifests/dashboard.yaml index f481434..b525e6c 100644 --- a/005-Dashboard/manifests/dashboard.yaml +++ b/005-Dashboard/manifests/dashboard.yaml @@ -183,4 +183,4 @@ spec: - port: 443 targetPort: 8443 selector: - k8s-app: kubernetes-dashboard \ No newline at end of file + k8s-app: kubernetes-dashboard diff --git a/006-Pod-Security-Policy/README.md b/006-Pod-Security-Policy/README.md index 12513c8..330d261 100644 --- a/006-Pod-Security-Policy/README.md +++ b/006-Pod-Security-Policy/README.md @@ -1,7 +1,7 @@ ## PodSecurityPolicy Pod security policies provide a framework to ensure that pods and containers run only with the appropriate privileges and access only a finite set of resources. Security policies also provide a way for cluster administrators to control resource creation, by limiting the capabilities available to specific roles, groups or namespaces. -If you haven't already or have been checking email and Slacking for the past 5 labs, please ensure you are `cluster-admin`. +If you haven't already or have been checking email and Slacking for the past 5 labs, please ensure you are `cluster-admin`. ``` kubectl create clusterrolebinding cluster-admin-binding \ --clusterrole cluster-admin \ @@ -54,8 +54,7 @@ gcloud beta container clusters update $(gcloud container clusters list --format ``` ### Task 4: Launch a Pod That Runs as Root -1. Inspect the modified Unshorten API deployment located in the `manifests/root-pod` directory and notice the new `runAsUser` field. This field specifies that for any Containers in the Pod, the first process runs with user ID 0 (root). - +1. Inspect the modified Unshorten API deployment located in the `manifests/root-pod` directory and notice the new `runAsUser` field. This field specifies that for any Containers in the Pod, the first process runs with user ID 0 (root). 2. Launch the Deployment and service: ``` # In the manifests/root-pod directory @@ -80,8 +79,7 @@ kubectl delete -f . Great job! We just stopped a container running as r00t. ### Task 5: Launch a Pod That Runs as Non-Root -1. Inspect the modified Unshorten API deployment located in the `manifests/non-root-pod` directory and notice the new `runAsUser` field. This field specifies that for any Containers in the Pod, the first process runs with user ID 999 (non-root). - +1. Inspect the modified Unshorten API deployment located in the `manifests/non-root-pod` directory and notice the new `runAsUser` field. This field specifies that for any Containers in the Pod, the first process runs with user ID 999 (non-root). 2. Launch the Deployment and service: ``` # In the manifests/non-root-pod directory diff --git a/006-Pod-Security-Policy/manifests/non-root-pod/link-unshorten-deployment-non-root.yaml b/006-Pod-Security-Policy/manifests/non-root-pod/link-unshorten-deployment-non-root.yaml index 235d28d..6652ecb 100644 --- a/006-Pod-Security-Policy/manifests/non-root-pod/link-unshorten-deployment-non-root.yaml +++ b/006-Pod-Security-Policy/manifests/non-root-pod/link-unshorten-deployment-non-root.yaml @@ -9,7 +9,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: app: unshorten-api @@ -31,4 +31,4 @@ spec: ports: - containerPort: 8080 name: http - protocol: TCP \ No newline at end of file + protocol: TCP diff --git a/006-Pod-Security-Policy/manifests/non-root-pod/link-unshorten-service-non-root.yaml b/006-Pod-Security-Policy/manifests/non-root-pod/link-unshorten-service-non-root.yaml index 6b9213f..971c3ca 100644 --- a/006-Pod-Security-Policy/manifests/non-root-pod/link-unshorten-service-non-root.yaml +++ b/006-Pod-Security-Policy/manifests/non-root-pod/link-unshorten-service-non-root.yaml @@ -9,4 +9,4 @@ spec: targetPort: 8080 protocol: TCP selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/006-Pod-Security-Policy/manifests/psp/pod-security-policy.yaml b/006-Pod-Security-Policy/manifests/psp/pod-security-policy.yaml index f0175af..a9b69d5 100644 --- a/006-Pod-Security-Policy/manifests/psp/pod-security-policy.yaml +++ b/006-Pod-Security-Policy/manifests/psp/pod-security-policy.yaml @@ -14,4 +14,4 @@ spec: supplementalGroups: rule: RunAsAny volumes: - - '*' \ No newline at end of file + - '*' diff --git a/006-Pod-Security-Policy/manifests/role/link-unshorten-cluster-role.yaml b/006-Pod-Security-Policy/manifests/role/link-unshorten-cluster-role.yaml index eef62fa..465fd19 100644 --- a/006-Pod-Security-Policy/manifests/role/link-unshorten-cluster-role.yaml +++ b/006-Pod-Security-Policy/manifests/role/link-unshorten-cluster-role.yaml @@ -10,4 +10,4 @@ rules: resourceNames: - restrict-root verbs: - - use \ No newline at end of file + - use diff --git a/006-Pod-Security-Policy/manifests/role/link-unshorten-rolebinding.yaml b/006-Pod-Security-Policy/manifests/role/link-unshorten-rolebinding.yaml index 927f8ea..af9b3db 100644 --- a/006-Pod-Security-Policy/manifests/role/link-unshorten-rolebinding.yaml +++ b/006-Pod-Security-Policy/manifests/role/link-unshorten-rolebinding.yaml @@ -13,4 +13,4 @@ subjects: # All service accounts in the default namespace - apiGroup: rbac.authorization.k8s.io kind: Group - name: system:serviceaccounts \ No newline at end of file + name: system:serviceaccounts diff --git a/006-Pod-Security-Policy/manifests/root-pod/link-unshorten-deployment.yaml b/006-Pod-Security-Policy/manifests/root-pod/link-unshorten-deployment.yaml index 8428f66..81b25e6 100644 --- a/006-Pod-Security-Policy/manifests/root-pod/link-unshorten-deployment.yaml +++ b/006-Pod-Security-Policy/manifests/root-pod/link-unshorten-deployment.yaml @@ -9,7 +9,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: app: unshorten-api @@ -24,4 +24,4 @@ spec: ports: - containerPort: 8080 name: http - protocol: TCP \ No newline at end of file + protocol: TCP diff --git a/006-Pod-Security-Policy/manifests/root-pod/link-unshorten-service.yaml b/006-Pod-Security-Policy/manifests/root-pod/link-unshorten-service.yaml index e83d63c..01c4ccf 100644 --- a/006-Pod-Security-Policy/manifests/root-pod/link-unshorten-service.yaml +++ b/006-Pod-Security-Policy/manifests/root-pod/link-unshorten-service.yaml @@ -9,4 +9,4 @@ spec: targetPort: 8080 protocol: TCP selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/007-Network-Policies/README.md b/007-Network-Policies/README.md index 3a8eccb..1ec3a52 100644 --- a/007-Network-Policies/README.md +++ b/007-Network-Policies/README.md @@ -14,7 +14,7 @@ echo "Default Namespace Switched:" $(kubectl get sa default -o jsonpath='{.metad ``` ### Task 2: Create our Network Policy -Go to the `manifests/network-policies` directory and inspect the Network policy named `hello-unshorten.yaml`. This policy simply selects Pods with label `app=unshorten-api` and specifies an ingress policy to allow traffic only from Pods with the label `app=unshorten-fe`. We only want to allow traffic from pods that are acting as frontends to our API. +Go to the `manifests/network-policies` directory and inspect the Network policy named `hello-unshorten.yaml`. This policy simply selects Pods with label `app=unshorten-api` and specifies an ingress policy to allow traffic only from Pods with the label `app=unshorten-fe`. We only want to allow traffic from pods that are acting as frontends to our API. In the `manifests/network-policies` directory run: ``` diff --git a/007-Network-Policies/manifests/api/link-unshorten-deployment.yaml b/007-Network-Policies/manifests/api/link-unshorten-deployment.yaml index b3bf2dc..11577ba 100644 --- a/007-Network-Policies/manifests/api/link-unshorten-deployment.yaml +++ b/007-Network-Policies/manifests/api/link-unshorten-deployment.yaml @@ -9,7 +9,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: app: unshorten-api @@ -21,4 +21,4 @@ spec: ports: - containerPort: 8080 name: http - protocol: TCP \ No newline at end of file + protocol: TCP diff --git a/007-Network-Policies/manifests/api/link-unshorten-service.yaml b/007-Network-Policies/manifests/api/link-unshorten-service.yaml index 5821dd8..6b00279 100644 --- a/007-Network-Policies/manifests/api/link-unshorten-service.yaml +++ b/007-Network-Policies/manifests/api/link-unshorten-service.yaml @@ -9,4 +9,4 @@ spec: targetPort: 8080 protocol: TCP selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/007-Network-Policies/manifests/network-policies/hello-unshorten.yaml b/007-Network-Policies/manifests/network-policies/hello-unshorten.yaml index 21b954f..679d5a6 100644 --- a/007-Network-Policies/manifests/network-policies/hello-unshorten.yaml +++ b/007-Network-Policies/manifests/network-policies/hello-unshorten.yaml @@ -12,4 +12,4 @@ spec: - from: - podSelector: matchLabels: - app: unshorten-fe \ No newline at end of file + app: unshorten-fe diff --git a/008-Istio/README.md b/008-Istio/README.md index eebe885..9723bed 100644 --- a/008-Istio/README.md +++ b/008-Istio/README.md @@ -24,7 +24,7 @@ gcloud beta container clusters update $(gcloud container clusters list --format (!)Ensure all cluster operations are labeled `DONE` before continuing(!) ``` -gcloud beta container operations list +gcloud beta container operations list ``` ### Task 2: Verify our Istio Installation @@ -80,7 +80,7 @@ Lets build some rules to explicit allow outbound egress traffic to only bit.ly a kubectl create -f . ``` -Once the rules are created, try to visit the API again and you should be able to successfully unshorten links to `bit.ly` domains only. +Once the rules are created, try to visit the API again and you should be able to successfully unshorten links to `bit.ly` domains only. ``` http://35.197.37.188/api/check?url=https://bit.ly/hi @@ -108,4 +108,4 @@ kubectl delete -f api -f istio-rules Now, disable Istio: ``` gcloud beta container clusters update $(gcloud container clusters list --format json | jq -r '.[].name') --update-addons=Istio=DISABLED --region=us-west1-a -``` \ No newline at end of file +``` diff --git a/008-Istio/manifests/api/link-unshorten-deployment.yaml b/008-Istio/manifests/api/link-unshorten-deployment.yaml index f803def..4ca90c2 100644 --- a/008-Istio/manifests/api/link-unshorten-deployment.yaml +++ b/008-Istio/manifests/api/link-unshorten-deployment.yaml @@ -3,7 +3,7 @@ kind: Deployment metadata: # A Deployment named link-unshorten is created using the metadata: name field name: link-unshorten - # We give the deployment a label + # We give the deployment a label labels: app: unshorten-api spec: @@ -13,7 +13,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: # We label all Pods in this deployment as app: unshorten-api @@ -27,4 +27,4 @@ spec: ports: - containerPort: 8080 name: http - protocol: TCP \ No newline at end of file + protocol: TCP diff --git a/008-Istio/manifests/api/link-unshorten-service.yaml b/008-Istio/manifests/api/link-unshorten-service.yaml index a995e1d..a6524b4 100644 --- a/008-Istio/manifests/api/link-unshorten-service.yaml +++ b/008-Istio/manifests/api/link-unshorten-service.yaml @@ -7,4 +7,4 @@ spec: - port: 8080 name: http selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/008-Istio/manifests/istio-rules/link-unshorten-egress.yaml b/008-Istio/manifests/istio-rules/link-unshorten-egress.yaml index 8dac9e0..e0b950f 100644 --- a/008-Istio/manifests/istio-rules/link-unshorten-egress.yaml +++ b/008-Istio/manifests/istio-rules/link-unshorten-egress.yaml @@ -45,4 +45,4 @@ spec: host: www.bit.ly port: number: 443 - weight: 100 \ No newline at end of file + weight: 100 diff --git a/009-Secrets/README.md b/009-Secrets/README.md index 83cc400..281c67c 100644 --- a/009-Secrets/README.md +++ b/009-Secrets/README.md @@ -148,13 +148,13 @@ curl \ http://127.0.0.1:8200/v1/secret/mysql ``` -### Task 4: Using Vault to Store and inject our MySQL Password +### Task 4: Using Vault to Store and inject our MySQL Password We can now call the Vault API to inject our secret into our `kubectl create` command on the fly as follows. First, delete `mysql-secrets` from our cluster: ``` -kubectl delete secret mysql-secrets +kubectl delete secret mysql-secrets ``` navigate to `manifests/secrets` and run: @@ -171,4 +171,4 @@ echo "Default Namespace Switched:" $(kubectl get sa default -o jsonpath='{.metad ``` ### Discussion Question -What secrets management systems are you using in-house? How could they better plug into DevOps pipelines? \ No newline at end of file +What secrets management systems are you using in-house? How could they better plug into DevOps pipelines? diff --git a/009-Secrets/manifests/api/link-unshorten-deployment.yaml b/009-Secrets/manifests/api/link-unshorten-deployment.yaml index e8137b2..5d4fdc1 100644 --- a/009-Secrets/manifests/api/link-unshorten-deployment.yaml +++ b/009-Secrets/manifests/api/link-unshorten-deployment.yaml @@ -9,7 +9,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: app: unshorten-api @@ -29,4 +29,4 @@ spec: ports: - containerPort: 8080 name: http - protocol: TCP \ No newline at end of file + protocol: TCP diff --git a/009-Secrets/manifests/api/link-unshorten-service.yaml b/009-Secrets/manifests/api/link-unshorten-service.yaml index e83d63c..01c4ccf 100644 --- a/009-Secrets/manifests/api/link-unshorten-service.yaml +++ b/009-Secrets/manifests/api/link-unshorten-service.yaml @@ -9,4 +9,4 @@ spec: targetPort: 8080 protocol: TCP selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/009-Secrets/manifests/mysql/mysql-deployment.yaml b/009-Secrets/manifests/mysql/mysql-deployment.yaml index 578f0fa..ff80027 100644 --- a/009-Secrets/manifests/mysql/mysql-deployment.yaml +++ b/009-Secrets/manifests/mysql/mysql-deployment.yaml @@ -28,4 +28,4 @@ spec: key: password ports: - containerPort: 3306 - name: mysql \ No newline at end of file + name: mysql diff --git a/009-Secrets/manifests/mysql/mysql-service.yaml b/009-Secrets/manifests/mysql/mysql-service.yaml index d887663..abcfd5e 100644 --- a/009-Secrets/manifests/mysql/mysql-service.yaml +++ b/009-Secrets/manifests/mysql/mysql-service.yaml @@ -10,4 +10,4 @@ spec: selector: app: unshorten tier: mysql - clusterIP: None \ No newline at end of file + clusterIP: None diff --git a/009-Secrets/manifests/secrets/mysql-secrets.yaml b/009-Secrets/manifests/secrets/mysql-secrets.yaml index 80fba62..757cd86 100644 --- a/009-Secrets/manifests/secrets/mysql-secrets.yaml +++ b/009-Secrets/manifests/secrets/mysql-secrets.yaml @@ -4,4 +4,4 @@ metadata: name: mysql-secrets type: Opaque data: - password: $$MYSQL_PASSWORD \ No newline at end of file + password: $$MYSQL_PASSWORD diff --git a/009-Secrets/manifests/vault/vault-service.yaml b/009-Secrets/manifests/vault/vault-service.yaml index 3a80f67..a0d067a 100644 --- a/009-Secrets/manifests/vault/vault-service.yaml +++ b/009-Secrets/manifests/vault/vault-service.yaml @@ -11,4 +11,4 @@ spec: protocol: TCP targetPort: 8200 selector: - app: vault \ No newline at end of file + app: vault diff --git a/009-Secrets/manifests/vault/vault-statefulset.yaml b/009-Secrets/manifests/vault/vault-statefulset.yaml index d80b897..dee94f0 100644 --- a/009-Secrets/manifests/vault/vault-statefulset.yaml +++ b/009-Secrets/manifests/vault/vault-statefulset.yaml @@ -1,5 +1,5 @@ apiVersion: apps/v1beta1 -# StatefulSets are intended to be used with stateful applications and distributed systems. +# StatefulSets are intended to be used with stateful applications and distributed systems. kind: StatefulSet metadata: name: vault @@ -23,4 +23,4 @@ spec: securityContext: capabilities: add: - - IPC_LOCK \ No newline at end of file + - IPC_LOCK diff --git a/010-Security-Pipeline/README.md b/010-Security-Pipeline/README.md index b7e8bf0..8411b6a 100644 --- a/010-Security-Pipeline/README.md +++ b/010-Security-Pipeline/README.md @@ -1,5 +1,5 @@ # Security Pipeline and Automation -This lab will spin up Jenkins in our cluster along with a private Docker image repository. Jenkins will also handle zero-downtime deploys of the unshorten API upon a successful build. The humble beginnings of a self-contained DevSecOps pipeline. +This lab will spin up Jenkins in our cluster along with a private Docker image repository. Jenkins will also handle zero-downtime deploys of the unshorten API upon a successful build. The humble beginnings of a self-contained DevSecOps pipeline. ### Create the `lab010` Namespace and Use as Default @@ -39,7 +39,7 @@ We need a location to store our versioned Docker images within our Kubernetes cl kubectl create -f . ``` -2. Once all of the Pods and Services are up and healthy, grab the URL for our freshly created registry and visit it in your browser. +2. Once all of the Pods and Services are up and healthy, grab the URL for our freshly created registry and visit it in your browser. Note: The registry runs on port `8080`. ``` @@ -83,7 +83,7 @@ kubectl port-forward $POD_NAME 8080:8080 >> /dev/null & https://github.com/ManicodeSecurity/unshorten-jenkins-demo ``` -5. Inspect the `Jenkinsfile` in the repo. It has the humble beginnings of an AppSec and DevSecOps pipeline. Each stage is meant to apply automation to the process where issues result in failed builds. +5. Inspect the [`Jenkinsfile`](https://github.com/ManicodeSecurity/unshorten-jenkins-demo/blob/master/Jenkinsfile) in the repo. It has the humble beginnings of an AppSec and DevSecOps pipeline. Each stage is meant to apply automation to the process where issues result in failed builds. ### Task 4: Trigger a Build Most pipeline setups will trigger builds on a git commit or through some other automated manner. To simulate this, we will tell Jenkins to trigger a build manually: @@ -111,4 +111,4 @@ echo "Default Namespace Switched:" $(kubectl get sa default -o jsonpath='{.metad ``` ### Further Reading -Check out the popular build workflow tool called [Skaffold](https://github.com/GoogleContainerTools/skaffold). \ No newline at end of file +Check out the popular build workflow tool called [Skaffold](https://github.com/GoogleContainerTools/skaffold). diff --git a/010-Security-Pipeline/manifests/jenkins/jenkins-deployment.yaml b/010-Security-Pipeline/manifests/jenkins/jenkins-deployment.yaml index d2f3b2a..251132b 100644 --- a/010-Security-Pipeline/manifests/jenkins/jenkins-deployment.yaml +++ b/010-Security-Pipeline/manifests/jenkins/jenkins-deployment.yaml @@ -28,4 +28,4 @@ spec: hostPath: path: /var/run/docker.sock - name: jenkins-home - emptyDir: {} \ No newline at end of file + emptyDir: {} diff --git a/010-Security-Pipeline/manifests/jenkins/jenkins-service.yaml b/010-Security-Pipeline/manifests/jenkins/jenkins-service.yaml index a5c7ef5..29a9172 100644 --- a/010-Security-Pipeline/manifests/jenkins/jenkins-service.yaml +++ b/010-Security-Pipeline/manifests/jenkins/jenkins-service.yaml @@ -8,4 +8,4 @@ spec: - port: 8080 targetPort: 8080 selector: - app: jenkins \ No newline at end of file + app: jenkins diff --git a/010-Security-Pipeline/manifests/registry/registry.yaml b/010-Security-Pipeline/manifests/registry/registry.yaml index 2a3611c..d5ffb4a 100644 --- a/010-Security-Pipeline/manifests/registry/registry.yaml +++ b/010-Security-Pipeline/manifests/registry/registry.yaml @@ -101,4 +101,4 @@ spec: path: /var/run/docker.sock - name: registry-persistent-storage persistentVolumeClaim: - claimName: registry-claim \ No newline at end of file + claimName: registry-claim diff --git a/010-Security-Pipeline/manifests/service-account/cluster-role-binding.yaml b/010-Security-Pipeline/manifests/service-account/cluster-role-binding.yaml index 887dc93..e73c657 100644 --- a/010-Security-Pipeline/manifests/service-account/cluster-role-binding.yaml +++ b/010-Security-Pipeline/manifests/service-account/cluster-role-binding.yaml @@ -11,4 +11,4 @@ subjects: name: jenkins namespace: lab010 - + diff --git a/010-Security-Pipeline/manifests/service-account/cluster-role.yaml b/010-Security-Pipeline/manifests/service-account/cluster-role.yaml index e676b11..1279f78 100644 --- a/010-Security-Pipeline/manifests/service-account/cluster-role.yaml +++ b/010-Security-Pipeline/manifests/service-account/cluster-role.yaml @@ -5,7 +5,7 @@ metadata: name: jenkins-limited rules: - apiGroups: [""] - resources: + resources: - pods - pods/log - pods/attach diff --git a/Admission-Control/README.md b/Admission-Control/README.md index 05e7cc1..1cc0c10 100644 --- a/Admission-Control/README.md +++ b/Admission-Control/README.md @@ -12,11 +12,11 @@ The webhooks are hosted using [Cloud Run](https://cloud.google.com/run/) on GCP. An Ingress object in Kubernetes may expose unnecessary external IP addresses to the internet. This can lead to unexpected compromise. -First, inspect the `ValidatingWebhookConfiguration` located at `deny-ingress/webhook-configuration.yaml`. You will see that we are using an open API endpoint hosted on Cloud Run to inspect the request. +First, inspect the `ValidatingWebhookConfiguration` located at `deny-ingress/webhook-config.yaml`. You will see that we are using an open API endpoint hosted on Cloud Run to inspect the request. Create the `ValidatingWebhookConfiguration` object: ``` -#in the deny-ingress directory +# in the deny-ingress directory kubectl create -f webhook-config.yaml ``` @@ -30,10 +30,10 @@ Due to the rules built in the [DenyIngresses](https://github.com/elithrar/admiss ## Block Public Load Balancers -Similar to Ingress objects, spinning up a public Loadbalancer may turn into a security issue. Let's us our Admission Webhook to block any public Loadbalancers from being created in the cluster: +Similar to Ingress objects, spinning up a public Loadbalancer may turn into a security issue. Let's use our Admission Webhook to block any public Loadbalancers from being created in the cluster: ``` -#in the deny-public-lb directory +# in the deny-public-lb directory kubectl create -f webhook-config.yaml ``` Now, to test that the Admission Control is working properly, create a simple Ingress object as follows: @@ -55,6 +55,6 @@ kubectl delete -f deny-public-lb ``` ## Shout Out -Shout out to the `Admission Control` repo from `elithrar`. Check it out on Github if you are looking for a robust framework for spinning up Admission Control in your clusters. +Shout out to the `Admission Control` repo from `elithrar`. Check it out on GitHub if you are looking for a robust framework for spinning up Admission Control in your clusters. -[https://github.com/elithrar/admission-control](https://github.com/elithrar/admission-control) \ No newline at end of file +[https://github.com/elithrar/admission-control](https://github.com/elithrar/admission-control) diff --git a/Admission-Control/deny-ingress/ingress.yaml b/Admission-Control/deny-ingress/ingress.yaml index a37d8dc..6ea54cf 100644 --- a/Admission-Control/deny-ingress/ingress.yaml +++ b/Admission-Control/deny-ingress/ingress.yaml @@ -9,4 +9,4 @@ spec: - path: /* backend: serviceName: hello-service - servicePort: 8000 \ No newline at end of file + servicePort: 8000 diff --git a/Admission-Control/deny-ingress/webhook-config.yaml b/Admission-Control/deny-ingress/webhook-config.yaml index 1e79199..b252478 100644 --- a/Admission-Control/deny-ingress/webhook-config.yaml +++ b/Admission-Control/deny-ingress/webhook-config.yaml @@ -18,4 +18,4 @@ webhooks: - "ingresses" failurePolicy: Fail clientConfig: - url: "https://deny-it-sflxj773na-uc.a.run.app/admission-control/deny-ingresses" \ No newline at end of file + url: "https://deny-it-sflxj773na-uc.a.run.app/admission-control/deny-ingresses" diff --git a/Admission-Control/deny-public-lb/public-lb.yaml b/Admission-Control/deny-public-lb/public-lb.yaml index 5ac6bdf..ec2f7af 100644 --- a/Admission-Control/deny-public-lb/public-lb.yaml +++ b/Admission-Control/deny-public-lb/public-lb.yaml @@ -9,4 +9,4 @@ spec: ports: - port: 8000 # Service's port protocol: TCP - targetPort: 8080 \ No newline at end of file + targetPort: 8080 diff --git a/Admission-Control/webhook/CloudRun.Dockerfile b/Admission-Control/webhook/CloudRun.Dockerfile index 823ff34..c191b14 100644 --- a/Admission-Control/webhook/CloudRun.Dockerfile +++ b/Admission-Control/webhook/CloudRun.Dockerfile @@ -22,4 +22,4 @@ COPY --from=build /go/bin/admissiond / # https://cloud.google.com/run/docs/reference/container-contract EXPOSE 8080 -CMD ["/admissiond", "-port=8080", "-http-only"] \ No newline at end of file +CMD ["/admissiond", "-port=8080", "-http-only"] diff --git a/Attacking-Kubelet/lab.md b/Attacking-Kubelet/lab.md index 17f0e5d..723336c 100644 --- a/Attacking-Kubelet/lab.md +++ b/Attacking-Kubelet/lab.md @@ -13,12 +13,12 @@ minikube delete minikube start ``` -2. The Kubelet API runs on every node in a cluster. If an individual has network access to a node in the kubernetes cluster, they are able to do some interesting things by default, including "exec-ing" into running pods. +2. The Kubelet API runs on every node in a cluster. If an individual has network access to a node in the kubernetes cluster, they are able to do some interesting things by default, including "exec-ing" into running pods. ``` # port 10250 is the read/write port that the Kubelet API uses for communication to the master node minikube ip curl --insecure https://$(minikube ip):10250/pods | jq -# jq is a tool to prettify JSON output - it is optional +# jq is a tool to prettify JSON output - it is optional ``` 3. As you can see, using a default implementation of Minikube (and many other kubernetes production bootstrappers) we are able to list all of the pods running on a given node with a simple `curl` command. This seems bad, right? Let's try to Exec and do some real damage. First, we launch some victim pods to take over. In the `manifests` directory, run the following command: @@ -43,7 +43,7 @@ curl --insecure -v -H "X-Stream-Protocol-Version: v2.channel.k8s.io" -H "X-Strea ``` wscat -c "https://$(minikube ip):10250/cri/exec/" --no-check ``` - + You can use cURL too: ### !! Please note that this command only works reliably with the newer versions of cURL. If you are getting errors, try updating cURL !! ``` @@ -56,7 +56,7 @@ curl -k --include \ https://$(minikube ip):10250/cri/exec/ ``` -8. These are *all of the environment variables* for our unshorten-api pod, printed to the screen, unauthenticated. +8. These are *all of the environment variables* for our unshorten-api pod, printed to the screen, unauthenticated. See any issues here? @@ -78,7 +78,7 @@ minikube ssh sudo cat /etc/kubernetes/kubelet.conf ``` -3. You will see that our config defines a set of certificates which successfully enables authentication between the Master and Nodes. +3. You will see that our config defines a set of certificates which successfully enables authentication between the Master and Nodes. ### Note: `kubeadm` does this by default but this is not always the case for other bootstrap mechanisms or scratch-built clusters! Always manually check that this configuration is enforced! diff --git a/Attacking-Kubelet/manifests/link-unshorten-deployment.yaml b/Attacking-Kubelet/manifests/link-unshorten-deployment.yaml index 03f4988..221ccc7 100644 --- a/Attacking-Kubelet/manifests/link-unshorten-deployment.yaml +++ b/Attacking-Kubelet/manifests/link-unshorten-deployment.yaml @@ -9,7 +9,7 @@ spec: selector: matchLabels: app: unshorten-api - template: + template: metadata: labels: app: unshorten-api diff --git a/Attacking-Kubelet/manifests/link-unshorten-service.yaml b/Attacking-Kubelet/manifests/link-unshorten-service.yaml index 92e08da..9f950df 100644 --- a/Attacking-Kubelet/manifests/link-unshorten-service.yaml +++ b/Attacking-Kubelet/manifests/link-unshorten-service.yaml @@ -9,4 +9,4 @@ spec: targetPort: 8080 protocol: TCP selector: - app: unshorten-api \ No newline at end of file + app: unshorten-api diff --git a/Audit-Logs/README.md b/Audit-Logs/README.md index f2b754e..7698d6e 100644 --- a/Audit-Logs/README.md +++ b/Audit-Logs/README.md @@ -1,24 +1,23 @@ -# Auditing -A recent addition to Kubernetes, (https://kubernetes.io/docs/tasks/debug-application-cluster/audit/) -[auditing] gives administrators and security teams the ability to log and monitor security-related events occurring on a cluster. By using audit policies, we can create granular rulesets to focus on only on the meaningful events and cut down on the noise. +# Auditing +A recent addition to Kubernetes, [auditing](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/), gives administrators and security teams the ability to log and monitor security-related events occurring on a cluster. By using audit policies, we can create granular rulesets to focus on only on the meaningful events and cut down on the noise. - ### Task 1: Inspect the Default Audit Policy -1. Since GKE uses a shared master node, we can't easily apply our own audit policies to a cluster. GKE clusters are bootstrapped with a default sane policy that can be found in the `manifests` directory. The policy summary can be found in the (https://cloud.google.com/kubernetes-engine/docs/concepts/audit-policy)[Kubernetes docs]. +### Task 1: Inspect the Default Audit Policy +1. Since GKE uses a shared master node, we can't easily apply our own audit policies to a cluster. GKE clusters are bootstrapped with a default sane policy that can be found in the `manifests` directory. The policy summary can be found in the [Kubernetes docs](https://cloud.google.com/kubernetes-engine/docs/concepts/audit-policy). - ### Task 2: Open Audit Logs in Stackdriver -In the main navigation in GCP, navigate to `Stackdriver` -> `Logging` -> `Logs` -Under the `Resources` drop-down select `Kubernetes Cluster` then the name of your cluster +### Task 2: Open Audit Logs in Stackdriver +In the main navigation in GCP, click the "hamburger" icon (≡), and then navigate to `Stackdriver` -> `Logging` -> `Logs Viewer`. +Under the `Resources` drop-down select `Kubernetes Cluster` then the name of your cluster. - Here you will see a list of audit events associated with your cluster. +Here you will see a list of audit events associated with your cluster. - ### Task 3: Trigger an Event -In Cloud Shell run a command that will trigger an audit event and inspect the event in Stackdriver. -``` -# Creating a secret will add a log entry -kubectl create secret generic mysql-secrets --from-literal=password=supertopsecretpassword -``` -Back in Stackdriver logs page, filter for the word `secret` and click the `Jump to Now` button. You should be able to see a log entry associated with the secret creation kubectl command. You can expand the log entry to view more detail. +### Task 3: Trigger an Event +In Cloud Shell run a command that will trigger an audit event and inspect the event in Stackdriver. +``` +# Creating a secret will add a log entry +kubectl create secret generic mysql-secrets --from-literal=password=supertopsecretpassword +``` +Back in Stackdriver logs page, filter for the word `secret` and click the `Jump to Now` button. You should be able to see a log entry associated with the secret creation kubectl command. You can expand the log entry to view more detail. - ## Discussion Question: -How would you ingest these logs into your current log management systems? What would you alert on? \ No newline at end of file +## Discussion Question: +How would you ingest these logs into your current log management systems? What would you alert on? diff --git a/Audit-Logs/manifests/GKE-Default-Policy.yaml b/Audit-Logs/manifests/GKE-Default-Policy.yaml index a92d7b0..441c046 100644 --- a/Audit-Logs/manifests/GKE-Default-Policy.yaml +++ b/Audit-Logs/manifests/GKE-Default-Policy.yaml @@ -111,4 +111,4 @@ rules: # Default level for all other requests. - level: Metadata omitStages: - - "RequestReceived" \ No newline at end of file + - "RequestReceived" diff --git a/Finale/lab.md b/Finale/lab.md index fc613e7..b912bce 100644 --- a/Finale/lab.md +++ b/Finale/lab.md @@ -1,7 +1,7 @@ # Finale This lab will use the skills and techniques we learned throughout the course to launch a hardened Kubernetes cluster: -Use the manifests located in `8-K8S-Cluster-Secrets/manifests/api` and `8-K8S-Cluster-Secrets/manifests/mysql` as our starting point. We have the following requirements for our unshorten-api / mysql deployment. Make sure to copy the .yaml files to this directory for modification. +Use the manifests located in `8-K8S-Cluster-Secrets/manifests/api` and `8-K8S-Cluster-Secrets/manifests/mysql` as our starting point. We have the following requirements for our unshorten-api / mysql deployment. Make sure to copy the .yaml files to this directory for modification. ### Cluster Security Requirements @@ -13,8 +13,8 @@ Use the manifests located in `8-K8S-Cluster-Secrets/manifests/api` and `8-K8S-Cl - Restricts elevation to root privileges 3. The application must exist in three namespaces: - - `development` - - `test` + - `development` + - `test` - `qa` 4. The Kubernetes API should have the following groups applied via RBAC and use HTTP Basic Authentication @@ -22,6 +22,6 @@ Use the manifests located in `8-K8S-Cluster-Secrets/manifests/api` and `8-K8S-Cl - `development` role can has access to read/write pods and secrets in the development namespace - `qa` role has read-only access to all pods in all namespaces -5. The kubelet API should be properly authenticated +5. The kubelet API should be properly authenticated 6. Create an audit log policy that generates logs when secrets are read/written and pods a created -7. Enable [Prometheus](https://github.com/giantswarm/kubernetes-prometheus/) in the cluster to gain deeper insight into cluster health and metrics \ No newline at end of file +7. Enable [Prometheus](https://github.com/giantswarm/kubernetes-prometheus/) in the cluster to gain deeper insight into cluster health and metrics diff --git a/README.md b/README.md index 38f1104..57ae6c3 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ View API Resources kubectl api-resources -o wide ``` -### Interact with running pods +### Interact with running pods ``` # Display all pods in all namespaces in the cluster kubectl get pods --all-namespaces @@ -48,8 +48,8 @@ kubectl logs -c ``` kubectl get logs kubectl exec -it /bin/bash -kubectl describe pod|service|deployment -kubectl get secret +kubectl describe pod|service|deployment +kubectl get secret kubectl get events | grep kubectl create --v 10 -f . ``` diff --git a/kube-goat/README.md b/kube-goat/README.md index f527979..95c6203 100644 --- a/kube-goat/README.md +++ b/kube-goat/README.md @@ -3,7 +3,7 @@ Your turn to hack a cluster. You have come across a kubeconfig file on a public Github repo. What can you do? -## The instructor will provide a link to the kubeconfig file for you to download. +## The instructor will provide a link to the kubeconfig file for you to download. In Google Cloud Shell, we will use the kubeconfig for the kube-goat cluster. Copy the kubeconfig and paste it in Cloud Shell at `~/.kube/goat-config` (You will need to create the `.kube` directory).