├── terraform-aks ├── versions.tf ├── variables.tf ├── outputs.tf └── aks-cluster.tf ├── apps ├── webgoat.yaml ├── juiceshop.yaml ├── webgoat-prod.yaml ├── juiceshop-prod.yaml ├── terracotta-bank.yaml ├── nodegoat.yaml └── netflicks.yaml ├── .gitignore ├── config-template.yaml └── README.md /terraform-aks/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | source = "hashicorp/azurerm" 5 | version = "2.66.0" 6 | } 7 | } 8 | 9 | required_version = ">= 0.14" 10 | } 11 | 12 | -------------------------------------------------------------------------------- /terraform-aks/variables.tf: -------------------------------------------------------------------------------- 1 | variable "initials" { 2 | description = "Enter your initials to include in URLs. Lowercase only!!!" 3 | default = "" 4 | } 5 | 6 | variable "location" { 7 | description = "The Azure location where all resources in this example should be created, to find your nearest run `az account list-locations -o table`" 8 | default = "" 9 | } -------------------------------------------------------------------------------- /apps/webgoat.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: webgoat 5 | namespace: default 6 | labels: 7 | contrast: java-assess 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: webgoat 12 | template: 13 | metadata: 14 | labels: 15 | app: webgoat 16 | spec: 17 | containers: 18 | - image: webgoat/webgoat-7.1 19 | name: webgoat 20 | ports: 21 | - containerPort: 8080 22 | --- 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: webgoat-service 27 | namespace: default 28 | spec: 29 | type: LoadBalancer 30 | ports: 31 | - port: 80 32 | targetPort: 8080 33 | selector: 34 | app: webgoat 35 | -------------------------------------------------------------------------------- /apps/juiceshop.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: juice-shop 5 | namespace: default 6 | labels: 7 | contrast: nodejs-assess 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: juice-shop 12 | template: 13 | metadata: 14 | labels: 15 | app: juice-shop 16 | spec: 17 | containers: 18 | - image: bkimminich/juice-shop 19 | name: webgoat 20 | ports: 21 | - containerPort: 3000 22 | --- 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: juice-shop-service 27 | namespace: default 28 | spec: 29 | type: LoadBalancer 30 | ports: 31 | - port: 80 32 | targetPort: 3000 33 | selector: 34 | app: juice-shop 35 | -------------------------------------------------------------------------------- /apps/webgoat-prod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: webgoat-prod 5 | namespace: default 6 | labels: 7 | contrast: java-protect 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: webgoat-prod 12 | template: 13 | metadata: 14 | labels: 15 | app: webgoat-prod 16 | spec: 17 | containers: 18 | - image: webgoat/webgoat-7.1 19 | name: webgoat 20 | ports: 21 | - containerPort: 8080 22 | --- 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: webgoat-service-prod 27 | namespace: default 28 | spec: 29 | type: LoadBalancer 30 | ports: 31 | - port: 80 32 | targetPort: 8080 33 | selector: 34 | app: webgoat-prod 35 | -------------------------------------------------------------------------------- /apps/juiceshop-prod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: juice-shop-prod 5 | namespace: default 6 | labels: 7 | contrast: nodejs-protect 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: juice-shop-prod 12 | template: 13 | metadata: 14 | labels: 15 | app: juice-shop-prod 16 | spec: 17 | containers: 18 | - image: bkimminich/juice-shop 19 | name: juice-shop 20 | ports: 21 | - containerPort: 3000 22 | --- 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: juice-shop-service-prod 27 | namespace: default 28 | spec: 29 | type: LoadBalancer 30 | ports: 31 | - port: 80 32 | targetPort: 3000 33 | selector: 34 | app: juice-shop-prod 35 | -------------------------------------------------------------------------------- /apps/terracotta-bank.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: terracotta-bank 5 | namespace: default 6 | labels: 7 | contrast: java-assess 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: terracotta-bank 12 | template: 13 | metadata: 14 | labels: 15 | app: terracotta-bank 16 | spec: 17 | containers: 18 | - image: contrastsecuritydemo/terracotta-bank:3.0-no-agent 19 | name: terracotta-bank 20 | ports: 21 | - containerPort: 8080 22 | --- 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: terracotta-bank-service 27 | namespace: default 28 | spec: 29 | type: LoadBalancer 30 | ports: 31 | - port: 80 32 | targetPort: 8080 33 | selector: 34 | app: terracotta-bank 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | 11 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 12 | # .tfvars files are managed as part of configuration and so should be included in 13 | # version control. 14 | 15 | *.tfvars 16 | 17 | # Ignore override files as they are usually used to override resources locally and so 18 | # are not checked in 19 | override.tf 20 | override.tf.json 21 | *_override.tf 22 | *_override.tf.json 23 | 24 | # Include override files you do wish to add to version control using negated pattern 25 | # 26 | # !example_override.tf 27 | 28 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 29 | # example: *tfplan* 30 | config.yaml -------------------------------------------------------------------------------- /terraform-aks/outputs.tf: -------------------------------------------------------------------------------- 1 | output "resource_group_name" { 2 | value = azurerm_resource_group.default.name 3 | } 4 | 5 | output "kubernetes_cluster_name" { 6 | value = azurerm_kubernetes_cluster.default.name 7 | } 8 | 9 | # output "host" { 10 | # value = azurerm_kubernetes_cluster.default.kube_config.0.host 11 | # } 12 | 13 | # output "client_key" { 14 | # value = azurerm_kubernetes_cluster.default.kube_config.0.client_key 15 | # } 16 | 17 | # output "client_certificate" { 18 | # value = azurerm_kubernetes_cluster.default.kube_config.0.client_certificate 19 | # } 20 | 21 | # output "kube_config" { 22 | # value = azurerm_kubernetes_cluster.default.kube_config_raw 23 | # } 24 | 25 | # output "cluster_username" { 26 | # value = azurerm_kubernetes_cluster.default.kube_config.0.username 27 | # } 28 | 29 | # output "cluster_password" { 30 | # value = azurerm_kubernetes_cluster.default.kube_config.0.password 31 | # } 32 | -------------------------------------------------------------------------------- /terraform-aks/aks-cluster.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | } 4 | 5 | resource "azurerm_resource_group" "default" { 6 | name = "Sales-Engineer-AKS-${var.initials}" 7 | location = var.location 8 | 9 | tags = { 10 | environment = "Demo" 11 | } 12 | } 13 | 14 | resource "azurerm_kubernetes_cluster" "default" { 15 | name = "Sales-Engineer-AKS-${var.initials}" 16 | location = azurerm_resource_group.default.location 17 | resource_group_name = azurerm_resource_group.default.name 18 | dns_prefix = "Sales-Engineer-AKS-${var.initials}" 19 | 20 | default_node_pool { 21 | name = "default" 22 | node_count = 2 23 | vm_size = "Standard_D2_v2" 24 | os_disk_size_gb = 30 25 | } 26 | 27 | identity { 28 | type = "SystemAssigned" 29 | } 30 | 31 | role_based_access_control { 32 | enabled = true 33 | } 34 | 35 | tags = { 36 | environment = "Demo" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /apps/nodegoat.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nodegoat 5 | namespace: default 6 | labels: 7 | contrast: nodejs-assess 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: nodegoat 12 | template: 13 | metadata: 14 | labels: 15 | app: nodegoat 16 | spec: 17 | containers: 18 | - name: web 19 | image: contrastsecuritydemo/nodegoat:1.3.0 20 | args: ["sh", "-c", "echo 'checking mongo' && until nc -z -w 2 127.0.0.1 27017 && echo 'mongo is ready for connections' && npm run db:seed && npm start; do sleep 2; done"] 21 | ports: 22 | - containerPort: 4000 23 | env: 24 | - name: MONGODB_URI 25 | value: "mongodb://127.0.0.1:27017/nodegoat" 26 | - name: mongo 27 | image: mongo 28 | ports: 29 | - containerPort: 27017 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | name: nodegoat-service 35 | namespace: default 36 | spec: 37 | type: LoadBalancer 38 | ports: 39 | - port: 80 40 | targetPort: 4000 41 | selector: 42 | app: nodegoat 43 | -------------------------------------------------------------------------------- /apps/netflicks.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: netflicks 5 | namespace: default 6 | labels: 7 | contrast: dotnet-core-assess 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: netflicks 12 | template: 13 | metadata: 14 | labels: 15 | app: netflicks 16 | spec: 17 | containers: 18 | 19 | - image: pprofili/netflicks:contrast 20 | name: netflicks 21 | ports: 22 | - containerPort: 80 23 | env: 24 | - name: ConnectionStrings__DotNetFlicksConnection 25 | value: Server=tcp:127.0.0.1,1433;Initial Catalog=DotNetFlicksDb;Persist Security Info=False;User ID=sa;Password=reallyStrongPwd123;MultipleActiveResultSets=False; 26 | 27 | - name: mssqldb 28 | image: mcr.microsoft.com/mssql/server:2019-latest 29 | ports: 30 | - containerPort: 1433 31 | env: 32 | - name: ACCEPT_EULA 33 | value: "Y" 34 | - name: SA_PASSWORD 35 | value: "reallyStrongPwd123" 36 | --- 37 | apiVersion: v1 38 | kind: Service 39 | metadata: 40 | name: netflicks-service 41 | namespace: default 42 | spec: 43 | type: LoadBalancer 44 | ports: 45 | - port: 80 46 | targetPort: 80 47 | selector: 48 | app: netflicks 49 | -------------------------------------------------------------------------------- /config-template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Create a Secret with your API keys and username. Get your keys from the 3 | # Contrast Platform under User Settings > Your Keys. 4 | apiVersion: v1 5 | kind: Secret 6 | metadata: 7 | name: default-agent-connection-secret 8 | namespace: contrast-agent-operator 9 | type: Opaque 10 | stringData: 11 | apiKey: #Your Agent API Key 12 | serviceKey: #Your Agent service Key 13 | userName: #Your Agent username 14 | --- 15 | # Define a default ClusterAgentConnection for all agents to use. 16 | # This uses the secrets defined above. 17 | # Change the url value to match your Contrast Platform environment, eg: 18 | # - https://eval.contrastsecurity.com/Contrast 19 | # - https://eval003.contrastsecurity.com/Contrast 20 | # - https://app.contrastsecurity.com/Contrast 21 | apiVersion: agents.contrastsecurity.com/v1beta1 22 | kind: ClusterAgentConnection 23 | metadata: 24 | name: default-agent-connection 25 | namespace: contrast-agent-operator 26 | spec: 27 | template: 28 | spec: 29 | url: https://eval.contrastsecurity.com/Contrast 30 | apiKey: 31 | secretName: default-agent-connection-secret 32 | secretKey: apiKey 33 | serviceKey: 34 | secretName: default-agent-connection-secret 35 | secretKey: serviceKey 36 | userName: 37 | secretName: default-agent-connection-secret 38 | secretKey: userName 39 | --- 40 | # Configure agent injectors for each language. Optionally create one Assess and 41 | # and one Protect AgentInjector for each language. The selector.labels defined 42 | # here will be applied to deployments to automatically inject agents to workloads. 43 | apiVersion: agents.contrastsecurity.com/v1beta1 44 | kind: AgentInjector 45 | metadata: 46 | name: contrast-agent-injector-java-assess 47 | namespace: default 48 | spec: 49 | type: java 50 | selector: 51 | labels: 52 | - name: contrast 53 | value: java-assess 54 | --- 55 | apiVersion: agents.contrastsecurity.com/v1beta1 56 | kind: AgentInjector 57 | metadata: 58 | name: contrast-agent-injector-java-protect 59 | namespace: default 60 | spec: 61 | type: java 62 | selector: 63 | labels: 64 | - name: contrast 65 | value: java-protect 66 | configuration: 67 | name: protect-agent-configuration-java 68 | --- 69 | apiVersion: agents.contrastsecurity.com/v1beta1 70 | kind: AgentInjector 71 | metadata: 72 | name: contrast-agent-injector-nodejs-assess 73 | namespace: default 74 | spec: 75 | type: nodejs 76 | selector: 77 | labels: 78 | - name: contrast 79 | value: nodejs-assess 80 | --- 81 | apiVersion: agents.contrastsecurity.com/v1beta1 82 | kind: AgentInjector 83 | metadata: 84 | name: contrast-agent-injector-nodejs-protect 85 | namespace: default 86 | spec: 87 | type: nodejs-protect 88 | selector: 89 | labels: 90 | - name: contrast 91 | value: nodejs-protect 92 | configuration: 93 | name: protect-agent-configuration-nodejs 94 | --- 95 | apiVersion: agents.contrastsecurity.com/v1beta1 96 | kind: AgentInjector 97 | metadata: 98 | name: contrast-agent-injector-dotnet-code 99 | namespace: default 100 | spec: 101 | type: dotnet-core 102 | selector: 103 | labels: 104 | - name: contrast 105 | value: dotnet-core 106 | --- 107 | apiVersion: agents.contrastsecurity.com/v1beta1 108 | kind: AgentInjector 109 | metadata: 110 | name: contrast-agent-injector-php 111 | namespace: default 112 | spec: 113 | type: php 114 | selector: 115 | labels: 116 | - name: contrast 117 | value: php 118 | --- 119 | # Create a default agent configuration to be used by AgentInjectors 120 | # This example will run Contrast Assess and report to the QA environment in 121 | # the Contrast platform. 122 | apiVersion: agents.contrastsecurity.com/v1beta1 123 | kind: ClusterAgentConfiguration 124 | metadata: 125 | name: default-agent-configuration 126 | namespace: contrast-agent-operator 127 | spec: 128 | namespaces: 129 | - default 130 | template: 131 | spec: 132 | yaml: | 133 | server: 134 | name: kubernetes-qa 135 | environment: QA 136 | --- 137 | # Create an alternative agent configuration for Contrast Protect 138 | # Pass a server.name value to the agent that is unique from the one used by Assess. 139 | # Setting server.environment=PRODUCTION instructs the agent to run in Protect mode. 140 | apiVersion: agents.contrastsecurity.com/v1beta1 141 | kind: AgentConfiguration 142 | metadata: 143 | name: protect-agent-configuration-java 144 | namespace: default 145 | spec: 146 | yaml: | 147 | server: 148 | name: kubernetes-prod 149 | environment: PRODUCTION 150 | suppressDefaultServerName: true 151 | suppressDefaultApplicationName: true 152 | --- 153 | # Additional agent configuration can also be passed such as to configure 154 | # additional logging. 155 | apiVersion: agents.contrastsecurity.com/v1beta1 156 | kind: AgentConfiguration 157 | metadata: 158 | name: protect-agent-configuration-nodejs 159 | namespace: default 160 | spec: 161 | yaml: | 162 | server: 163 | name: kubernetes-prod 164 | environment: PRODUCTION 165 | agent: 166 | logger: 167 | stdout: true 168 | level: WARN 169 | service: 170 | logger: 171 | level: WARN 172 | stdout: true 173 | security_logger: 174 | path: /dev/null 175 | protect: 176 | enable: true 177 | suppressDefaultServerName: true 178 | suppressDefaultApplicationName: true 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Contrast Agent Operator Demo 2 | 3 | This repo includes examples of how to create a Kubernetes cluster, deploy the Contrast Agent Operator and some known vulnerable applications. Typical deployment times: 4 | 5 | * AKS - 5 mins 6 | * EKS - 20 mins 7 | 8 | --- 9 | 10 | ## Deploying on Azure/AKS 11 | 12 | This deployment will typically take ±5 minutes. 13 | 14 | ### Pre-requisites for Azure/AKS 15 | 16 | 1. Install Terraform from here: . 17 | 1. Install the Azure cli tools using `brew update && brew install azure-cli`. 18 | 1. Install kubectl using `brew update && brew install kubectl`. 19 | 20 | ### Steps for Azure/AKS 21 | 22 | 1. Change directory to the [AKS](/terraform-aks/) folder 23 | 1. If your Azure CLI is not authenticated then log into the Azure (`az login`) to cache your credentials. 24 | 1. Create a terraform.tfvars file to add your initials and preferred Azure location, for example: 25 | 26 | location="UK South" 27 | initials="da" 28 | 29 | 1. Run `terraform init` to download the required plugins. 30 | 1. Run `terraform apply` to deploy a new cluster. 31 | 1. Grab your AKS credentials for kubectl: 32 | 33 | az aks get-credentials --resource-group $(terraform output resource_group_name | tr -d '"') --name $(terraform output kubernetes_cluster_name | tr -d '"') 34 | 35 | 1. Deploy the operator and demo apps. 36 | 1. View the Kubernetes dashboard (optional): 37 | 38 | az aks browse --resource-group $(terraform output resource_group_name | tr -d '"') --name $(terraform output kubernetes_cluster_name | tr -d '"') 39 | 40 | 1. After your demo, run `terraform destroy --auto-approve` to remove all resources. 41 | 42 | --- 43 | 44 | ## Deploying on AWS/EKS 45 | 46 | ### Pre-requisites for AWS/EKS 47 | 48 | 1. Install kubectl using `brew update && brew install kubectl`. 49 | 1. Install the AWS CLI using `brew update && brew install awscli`. 50 | 1. If your AWS CLI is not authenticated then run (`aws configure`) to cache your credentials. 51 | 1. Install eksctl using `brew update && brew install eksctl`. 52 | 53 | ### Steps for AWS/EKS 54 | 55 | 1. Create a K8S cluster using `eksctl create cluster --name sales-engineering-da --region us-east-2` 56 | 1. Grab your EKS credentials for kubectl: `aws eks update-kubeconfig --region us-east-2 --name sales-engineering-da` 57 | 1. After your demo, run `eksctl delete cluster --name sales-engineering-da --region us-east-2` to remove all resources. 58 | 59 | --- 60 | 61 | ## Installing the Contrast Agent Operator 62 | 63 | 1. Install the operator: 64 | 65 | kubectl apply -f https://github.com/Contrast-Security-OSS/agent-operator/releases/latest/download/install-prod.yaml 66 | 67 | 1. Configure the operator using one of the two options below: 68 | 69 | ### Express Configuration 70 | 71 | 1. Rename the [config-template.yaml](config-template.yaml) to config.yaml and add your agent credentials on lines 9-11. This file includes agent injectors for all languages. 72 | 1. Run `kubectl apply -f config.yaml` 73 | 74 | ### Step by Step Configuration 75 | 76 | 1. Configure the operator credentials: 77 | 78 | kubectl -n contrast-agent-operator create secret generic default-agent-connection-secret --from-literal=apiKey=TODO --from-literal=serviceKey=TODO --from-literal=userName=TODO 79 | 80 | 1. Create a ClusterAgentConnection: 81 | 82 | ``` 83 | kubectl apply -f - <