├── dashboard
├── cert
│ ├── self-signed-root.srl
│ ├── .gitignore
│ ├── gen-csr-nanw.sh
│ ├── v3_ext.txt
│ ├── req-self-signed-root.conf
│ ├── req-nanw.conf
│ ├── gen-self-signed.sh
│ ├── self-signed-root.crt
│ ├── nanw.csr
│ ├── nanw.crt
│ └── nanw_eng_vmware_com.crt
├── collie
│ ├── grafana-export-datasources.sh
│ ├── grafana-import-datasources.sh
│ └── grafana-export-dashboards.sh
├── .gitlab-ci.yml
└── .gitignore
├── api-server
├── .gitignore
├── config
│ ├── app-default.yaml
│ └── oauth-default.yaml
├── Dockerfile
├── service
│ ├── template
│ │ ├── namespace.yaml
│ │ ├── service-account.yaml
│ │ ├── secret.yaml
│ │ ├── configmap.yaml
│ │ ├── kube-hunter-job.yaml
│ │ ├── cluster-role-binding.yaml
│ │ ├── role-binding.yaml
│ │ ├── resource-quota.yaml
│ │ ├── role.yaml
│ │ ├── clustervpa-configmap.yaml
│ │ ├── cluster-role.yaml
│ │ └── kube-bench-job.yaml
│ ├── persist
│ │ └── persist.go
│ ├── org
│ │ └── org.go
│ ├── auth
│ │ ├── authInfo.go
│ │ └── auth.go
│ ├── agent-template.go
│ └── oauth
│ │ └── common
│ │ └── common.go
├── README.md
├── Makefile
├── model
│ ├── error.go
│ ├── admin.go
│ ├── bottle.go
│ └── account.go
├── controller
│ ├── controller.go
│ ├── report.go
│ └── portal.go
├── httputil
│ └── error.go
├── util
│ └── util.go
├── middleware
│ └── auth.go
├── commonms
│ ├── base.go
│ └── healthz.go
└── go.mod
├── collie
├── .eslintrc.json
├── next.config.js
├── postcss.config.js
├── src
│ ├── app
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ └── layout.tsx
│ └── middleware.ts
├── .gitignore
├── tailwind.config.ts
├── public
│ ├── vercel.svg
│ └── next.svg
├── tsconfig.json
├── package.json
└── README.md
├── deployment
├── helm-charts
│ ├── .helmignore
│ ├── grafana
│ │ ├── templates
│ │ │ ├── extra-manifests.yaml
│ │ │ ├── secret-env.yaml
│ │ │ ├── tests
│ │ │ │ ├── test-serviceaccount.yaml
│ │ │ │ ├── test-configmap.yaml
│ │ │ │ ├── test-role.yaml
│ │ │ │ ├── test-rolebinding.yaml
│ │ │ │ ├── test-podsecuritypolicy.yaml
│ │ │ │ └── test.yaml
│ │ │ ├── serviceaccount.yaml
│ │ │ ├── poddisruptionbudget.yaml
│ │ │ ├── headless-service.yaml
│ │ │ ├── rolebinding.yaml
│ │ │ ├── clusterrolebinding.yaml
│ │ │ ├── dashboards-json-configmap.yaml
│ │ │ ├── clusterrole.yaml
│ │ │ ├── image-renderer-service.yaml
│ │ │ ├── pvc.yaml
│ │ │ ├── secret.yaml
│ │ │ ├── role.yaml
│ │ │ ├── configmap-dashboard-provider.yaml
│ │ │ ├── podsecuritypolicy.yaml
│ │ │ ├── servicemonitor.yaml
│ │ │ ├── networkpolicy.yaml
│ │ │ ├── image-renderer-servicemonitor.yaml
│ │ │ ├── service.yaml
│ │ │ ├── hpa.yaml
│ │ │ ├── image-renderer-hpa.yaml
│ │ │ ├── deployment.yaml
│ │ │ ├── statefulset.yaml
│ │ │ ├── image-renderer-network-policy.yaml
│ │ │ ├── ingress.yaml
│ │ │ └── NOTES.txt
│ │ ├── Makefile
│ │ ├── Chart.yaml
│ │ └── values-custom.yaml
│ ├── es
│ │ ├── Makefile
│ │ ├── Chart.yaml
│ │ └── values-custom.yaml
│ ├── api-server
│ │ ├── templates
│ │ │ ├── configmap.yaml
│ │ │ ├── serviceaccount.yaml
│ │ │ ├── service.yaml
│ │ │ ├── tests
│ │ │ │ └── test-connection.yaml
│ │ │ ├── secret.yaml
│ │ │ ├── rbac.yaml
│ │ │ ├── hpa.yaml
│ │ │ ├── NOTES.txt
│ │ │ ├── _helpers.tpl
│ │ │ ├── ingress.yaml
│ │ │ └── deployment.yaml
│ │ ├── .helmignore
│ │ ├── Chart.yaml
│ │ └── values.yaml
│ ├── prep.sh
│ └── Makefile
├── scripts
│ ├── .gitignore
│ ├── delete-agent.sh
│ ├── azure
│ │ ├── delete-agent.sh
│ │ ├── sc-standard.yml
│ │ ├── deploy-all.sh
│ │ ├── check-req.sh
│ │ ├── pv-my.yml
│ │ ├── deploy-server.sh
│ │ ├── delete-all.sh
│ │ ├── deploy-grafana.sh
│ │ └── deploy-es.sh
│ ├── minikube
│ │ ├── delete-agent.sh
│ │ ├── deploy-all.sh
│ │ ├── check-req.sh
│ │ ├── kill-processes-by-keyword.sh
│ │ ├── deploy-server.sh
│ │ ├── delete-all.sh
│ │ ├── debug-image.sh
│ │ ├── deploy-grafana.sh
│ │ └── deploy-es.sh
│ ├── deploy-agent.sh
│ ├── gitlab-get-user.sh
│ ├── es-add-doc.sh
│ ├── .env.starter
│ ├── toolbox.yaml
│ ├── es-delete-old-index.sh
│ ├── q.sh
│ ├── es-query.sh
│ ├── ingress.yaml
│ ├── es-recreate-index.sh
│ ├── upgrade-minikube.sh
│ ├── deploy-api-server.sh
│ ├── auth-gitlab.sh
│ ├── bad-pod.yaml
│ ├── README.md
│ ├── portal.yaml
│ ├── redeploy-api-server.sh
│ └── job.yaml
├── README.md
└── k8s
│ └── azure
│ └── parameters.json
├── k8s-agent
├── .gitignore
├── Dockerfile
├── README.md
├── .env.starter
├── Makefile
├── internal
│ ├── config
│ │ └── version.go
│ ├── reporter
│ │ └── collie_client_interface.go
│ ├── services
│ │ └── version
│ │ │ └── version.go
│ ├── model
│ │ └── model.go
│ └── commonms
│ │ ├── base.go
│ │ └── healthz.go
├── .gitlab-ci.yml
└── go.mod
├── doc
└── images
│ ├── screenshot-login.png
│ ├── screenshot-paired.png
│ ├── screenshot-pairing.png
│ └── screenshot-dashboard.png
├── NOTICE
├── .gitignore
├── CONTRIBUTING_CLA.md
└── deployment.md
/dashboard/cert/self-signed-root.srl:
--------------------------------------------------------------------------------
1 | B998C67DE151E673
2 |
--------------------------------------------------------------------------------
/api-server/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | collie-api-server
3 | .env
4 |
5 |
--------------------------------------------------------------------------------
/collie/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/dashboard/cert/.gitignore:
--------------------------------------------------------------------------------
1 | #*.key
2 | #*.crt
3 | #*.csr
4 | #*.srl
5 |
--------------------------------------------------------------------------------
/deployment/helm-charts/.helmignore:
--------------------------------------------------------------------------------
1 | tests/
2 | .pytest_cache/
3 |
4 |
--------------------------------------------------------------------------------
/deployment/scripts/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | .env.local
3 | .env.collie
4 |
5 |
--------------------------------------------------------------------------------
/deployment/README.md:
--------------------------------------------------------------------------------
1 | # deployment
2 |
3 | Scripts for deployment and integration test.
--------------------------------------------------------------------------------
/k8s-agent/.gitignore:
--------------------------------------------------------------------------------
1 | .terraform
2 | .idea
3 | .run
4 | *.iml
5 | bin
6 | .env
7 | collie-agent
8 |
9 |
--------------------------------------------------------------------------------
/deployment/scripts/delete-agent.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | kubectl delete namespace collie-agent --wait=true
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/collie/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {}
3 |
4 | module.exports = nextConfig
5 |
--------------------------------------------------------------------------------
/collie/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/collie/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omnissa-archive/compliance-dashboard-for-kubernetes/main/collie/src/app/favicon.ico
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/extra-manifests.yaml:
--------------------------------------------------------------------------------
1 | {{ range .Values.extraObjects }}
2 | ---
3 | {{ tpl (toYaml .) $ }}
4 | {{ end }}
5 |
--------------------------------------------------------------------------------
/doc/images/screenshot-login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omnissa-archive/compliance-dashboard-for-kubernetes/main/doc/images/screenshot-login.png
--------------------------------------------------------------------------------
/doc/images/screenshot-paired.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omnissa-archive/compliance-dashboard-for-kubernetes/main/doc/images/screenshot-paired.png
--------------------------------------------------------------------------------
/doc/images/screenshot-pairing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omnissa-archive/compliance-dashboard-for-kubernetes/main/doc/images/screenshot-pairing.png
--------------------------------------------------------------------------------
/k8s-agent/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.17.2
2 | ARG TARGETARCH
3 | COPY bin/collie-agent-$TARGETARCH /usr/local/bin/collie-agent
4 | CMD ["collie-agent"]
5 |
--------------------------------------------------------------------------------
/doc/images/screenshot-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omnissa-archive/compliance-dashboard-for-kubernetes/main/doc/images/screenshot-dashboard.png
--------------------------------------------------------------------------------
/api-server/config/app-default.yaml:
--------------------------------------------------------------------------------
1 | agent_image: "collie.azurecr.io/collie-agent:1"
2 | grafana_url: "https://collie.eng.omnissa.com/d/qIbLYbT4z/k8s-compliance-report"
3 |
--------------------------------------------------------------------------------
/deployment/scripts/azure/delete-agent.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | kubectl get ns/collie-agent > /dev/null 2>&1
4 | if [ $? -eq 0 ]; then
5 | kubectl delete namespace collie-agent --wait
6 | fi
7 |
--------------------------------------------------------------------------------
/deployment/scripts/minikube/delete-agent.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | kubectl get ns/collie-agent > /dev/null 2>&1
4 | if [ $? -eq 0 ]; then
5 | kubectl delete namespace collie-agent --wait
6 | fi
7 |
--------------------------------------------------------------------------------
/api-server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.17.2
2 | ARG TARGETARCH
3 | COPY bin/collie-api-server-$TARGETARCH /usr/local/bin/collie-api-server
4 | COPY assets /assets
5 | COPY config/*.yaml /config/
6 | CMD ["collie-api-server"]
7 |
--------------------------------------------------------------------------------
/dashboard/collie/grafana-export-datasources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | mkdir -p ./grafana-data-sources
4 | curl -k "$COLLIE_GRAFANA_URL/api/datasources" -u $COLLIE_GRAFANA_CREDENTIAL |jq -c -M '.[]'|split -l 1 - ./grafana-data-sources/
5 |
--------------------------------------------------------------------------------
/deployment/scripts/deploy-agent.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | curl -skH "Authorization: Bearer elastic:16b9f5de2c23718edbf713731584fbb3" "https://collie.eng.omnissa.com/collie/api/v1/onboarding/agent.yaml?provider=AKS&aid=a63aefd2af7028be" | kubectl apply -f -
4 |
--------------------------------------------------------------------------------
/dashboard/cert/gen-csr-nanw.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | rm -f nanw.key
4 | rm -f nanw.crt
5 | rm -f nanw.csr
6 |
7 | openssl req -newkey rsa:2048 -nodes -sha256 -keyout nanw.key -new -out nanw.csr -config req-nanw.conf
8 |
9 | openssl req -text -noout -verify -in nanw.csr
10 |
--------------------------------------------------------------------------------
/deployment/scripts/gitlab-get-user.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source .env
4 |
5 | ACCESS_TOKEN=$(source auth-gitlab.sh | jq -r '.access_token')
6 |
7 | echo $ACCESS_TOKEN
8 |
9 | curl -s -H "Authorization: Bearer $ACCESS_TOKEN" https://gitlab.eng.omnissa.com/api/v4/user
10 |
11 |
--------------------------------------------------------------------------------
/api-server/service/template/namespace.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: collie-agent
5 | labels:
6 | app.kubernetes.io/name: collie
7 | app.kubernetes.io/instance: collie
8 | app.kubernetes.io/version: "v1"
9 | app.kubernetes.io/managed-by: collie
--------------------------------------------------------------------------------
/dashboard/collie/grafana-import-datasources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | for i in ./grafana-data-sources/*; do \
4 | curl -k -X "POST" "$COLLIE_GRAFANA_URL/api/datasources" \
5 | -H "Content-Type: application/json" \
6 | --user $COLLIE_GRAFANA_CREDENTIAL \
7 | --data-binary @$i
8 | done
9 |
--------------------------------------------------------------------------------
/deployment/scripts/es-add-doc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | curl -k -u $ES_KEY -X POST -H "Content-Type: application/json" -d '
4 | {
5 | "field1": "value1",
6 | "field2": "value2",
7 | "n1": {
8 | "n11": {
9 | "v1": 1
10 | },
11 | "n12": 3
12 | }
13 | }' $ES_URL/t1/_doc
14 |
15 |
--------------------------------------------------------------------------------
/api-server/README.md:
--------------------------------------------------------------------------------
1 | # Collie API server
2 |
3 | Gen doc
4 |
5 | ```console
6 | $ go get -u github.com/swaggo/swag/cmd/swag
7 | $ swag init
8 | ```
9 |
10 | Run app
11 |
12 | ```console
13 | $ go run main.go
14 | ```
15 |
16 | [open swagger](http://localhost:8080/swagger/index.html)
17 |
18 |
--------------------------------------------------------------------------------
/k8s-agent/README.md:
--------------------------------------------------------------------------------
1 | # Collie API server
2 |
3 | Gen doc
4 |
5 | ```console
6 | $ go get -u github.com/swaggo/swag/cmd/swag
7 | $ swag init
8 | ```
9 |
10 | Run app
11 |
12 | ```console
13 | $ go run main.go
14 | ```
15 |
16 | [open swagger](http://localhost:8080/swagger/index.html)
17 |
18 |
--------------------------------------------------------------------------------
/deployment/helm-charts/es/Makefile:
--------------------------------------------------------------------------------
1 | default: test
2 |
3 | RELEASE := helm-es-minikube
4 | TIMEOUT := 1200s
5 |
6 | install:
7 | helm upgrade --wait --timeout=$(TIMEOUT) --install --values values.yaml $(RELEASE) ../../
8 |
9 | test: install
10 | helm test $(RELEASE)
11 |
12 | purge:
13 | helm del $(RELEASE)
14 |
--------------------------------------------------------------------------------
/deployment/scripts/azure/sc-standard.yml:
--------------------------------------------------------------------------------
1 | allowVolumeExpansion: true
2 | apiVersion: storage.k8s.io/v1
3 | kind: StorageClass
4 | metadata:
5 | name: standard
6 | parameters:
7 | skuname: StandardSSD_LRS
8 | provisioner: disk.csi.azure.com
9 | reclaimPolicy: Retain
10 | volumeBindingMode: WaitForFirstConsumer
11 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/Makefile:
--------------------------------------------------------------------------------
1 | default: test
2 |
3 | RELEASE := helm-es-minikube
4 | TIMEOUT := 1200s
5 |
6 | install:
7 | helm upgrade --wait --timeout=$(TIMEOUT) --install --values values.yaml $(RELEASE) ../../
8 |
9 | test: install
10 | helm test $(RELEASE)
11 |
12 | purge:
13 | helm del $(RELEASE)
14 |
--------------------------------------------------------------------------------
/api-server/service/template/service-account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: agent
5 | namespace: collie-agent
6 | labels:
7 | app.kubernetes.io/name: agent
8 | app.kubernetes.io/instance: agent
9 | app.kubernetes.io/version: "v1"
10 | app.kubernetes.io/managed-by: collie
--------------------------------------------------------------------------------
/deployment/scripts/minikube/deploy-all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source ./delete-all.sh
4 | source ./check-req.sh
5 |
6 | kubectl create namespace collie-server
7 | kubectl apply -f persistent-volume.yml --namespace collie-server
8 |
9 | source deploy-es.sh
10 | source deploy-grafana.sh
11 | source deploy-server.sh
12 |
13 | echo
14 | echo Complete.
15 |
16 |
17 |
--------------------------------------------------------------------------------
/api-server/service/template/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: agent
5 | namespace: collie-agent
6 | labels:
7 | app.kubernetes.io/name: agent
8 | app.kubernetes.io/instance: agent
9 | app.kubernetes.io/version: "v1"
10 | app.kubernetes.io/managed-by: collie
11 |
12 | data:
13 | API_KEY: {{.ApiKey}}
14 | ES_KEY: {{.EsKey}}
--------------------------------------------------------------------------------
/deployment/scripts/.env.starter:
--------------------------------------------------------------------------------
1 | export API_URL=https://collie.eng.omnissa.com/collie
2 | export ES_URL=https://collie.eng.omnissa.com:9200
3 | export COLLIE_ES_USERNAME=
4 | export COLLIE_ES_PASSWORD=
5 | export ES_KEY=
6 | export COLLIE_ES_URL=$ES_URL
7 |
8 | export OAUTH_CSP_CLIENTID=
9 | export OAUTH_CSP_CLIENTSECRET=
10 | export OAUTH_GITLAB_CLIENTID=
11 | export OAUTH_GITLAB_CLIENTSECRET=
12 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: {{ include "..fullname" . }}
5 | labels:
6 | {{- include "..labels" . | nindent 4 }}
7 |
8 | data:
9 | COLLIE_URL: {{ .Values.collie.url }}
10 | #API_URL: {{ .Values.collie.url }}/collie
11 | ES_URL: {{ .Values.es.url }}
12 | GRAFANA_URL: {{ .Values.grafana.url }}
13 |
--------------------------------------------------------------------------------
/deployment/scripts/azure/deploy-all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source ./delete-all.sh
4 | source ./check-req.sh
5 |
6 | kubectl create namespace collie-server
7 | kubectl apply -f sc-standard.yml
8 | kubectl apply -f persistent-volume.yml --namespace collie-server
9 |
10 | source deploy-es.sh
11 | source deploy-grafana.sh
12 | source deploy-server.sh
13 |
14 | echo
15 | echo Complete.
16 |
17 |
18 |
--------------------------------------------------------------------------------
/api-server/service/template/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: agent
5 | namespace: collie-agent
6 | labels:
7 | app.kubernetes.io/name: agent
8 | app.kubernetes.io/instance: agent
9 | app.kubernetes.io/version: "v1"
10 | app.kubernetes.io/managed-by: collie
11 |
12 | data:
13 | API_URL: {{.ApiUrl}}
14 | ES_URL: {{.EsUrl}}
15 | PROVIDER: {{.Provider}}
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceAccount.create -}}
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: {{ include "..serviceAccountName" . }}
6 | labels:
7 | {{- include "..labels" . | nindent 4 }}
8 | {{- with .Values.serviceAccount.annotations }}
9 | annotations:
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | {{- end }}
13 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright 2023 Omnissa, LLC.
2 |
3 | This product is licensed to you under the Apache License, V2.0 (the "License"). You may not use this product except in compliance with the License.
4 |
5 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file.
--------------------------------------------------------------------------------
/deployment/helm-charts/es/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | description: Official Elastic helm chart for Elasticsearch
3 | home: https://github.com/elastic/helm-charts
4 | maintainers:
5 | - email: helm-charts@elastic.co
6 | name: Elastic
7 | name: elasticsearch
8 | version: 8.5.1
9 | appVersion: 8.5.1
10 | sources:
11 | - https://github.com/elastic/elasticsearch
12 | icon: https://helm.elastic.co/icons/elasticsearch.png
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.so
7 | *.dylib
8 |
9 | # Test binary, built with `go test -c`
10 | *.test
11 |
12 | # Output of the go coverage tool, specifically when used with LiteIDE
13 | *.out
14 |
15 | # Dependency directories (remove the comment below to include it)
16 | # vendor/
17 |
18 | # Go workspace file
19 | go.work
20 | go.work.sum
21 |
22 | .env
23 | .secret
24 |
--------------------------------------------------------------------------------
/k8s-agent/.env.starter:
--------------------------------------------------------------------------------
1 | export API_KEY=
2 | export API_URL=https://collie.eng.omnissa.com/collie
3 | export ES_URL=https://collie.eng.omnissa.com:9200
4 | export ES_KEY=
5 |
6 | export PROVIDER=AKS
7 | export AGENTID=demo
8 |
9 | # for local testing without having to run the agent inside k8s
10 | export KUBECONFIG=/Users/nanw/.kube/config
11 |
12 | # tuning to make testing cycle faster
13 | export CONTROLLER_INITIAL_SLEEP_DURATION=3s
14 |
--------------------------------------------------------------------------------
/dashboard/cert/v3_ext.txt:
--------------------------------------------------------------------------------
1 | basicConstraints = critical, CA:FALSE
2 | subjectKeyIdentifier = hash
3 | authorityKeyIdentifier = keyid,issuer
4 | keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement, dataEncipherment
5 | extendedKeyUsage = critical, serverAuth
6 | subjectAltName = @alt_names
7 |
8 | [alt_names]
9 | DNS.1 = nanw.eng.omnissa.com
10 | DNS.2 = collie.eng.omnissa.com
11 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ include "..fullname" . }}
5 | labels:
6 | {{- include "..labels" . | nindent 4 }}
7 | spec:
8 | type: {{ .Values.service.type }}
9 | ports:
10 | - port: {{ .Values.service.port }}
11 | targetPort: http
12 | protocol: TCP
13 | name: http
14 | selector:
15 | {{- include "..selectorLabels" . | nindent 4 }}
16 |
--------------------------------------------------------------------------------
/api-server/service/template/kube-hunter-job.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: kube-hunter
5 | namespace: collie-agent
6 | spec:
7 | template:
8 | metadata:
9 | labels:
10 | app: kube-hunter
11 | spec:
12 | containers:
13 | - name: kube-hunter
14 | image: collie.azurecr.io/kube-hunter:0.6.8
15 | command: ["kube-hunter"]
16 | args: ["--pod","--report=json"]
17 | restartPolicy: Never
--------------------------------------------------------------------------------
/deployment/scripts/toolbox.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: toolbox
5 | namespace: default
6 | labels:
7 | app: toolbox
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: toolbox
13 | template:
14 | metadata:
15 | labels:
16 | app: toolbox
17 | spec:
18 | containers:
19 | - name: toolbox
20 | image: ubuntu:latest
21 | imagePullPolicy: IfNotPresent
22 |
--------------------------------------------------------------------------------
/dashboard/cert/req-self-signed-root.conf:
--------------------------------------------------------------------------------
1 | [req]
2 | distinguished_name = req_distinguished_name
3 | req_extensions = v3_ca
4 | prompt = no
5 | [req_distinguished_name]
6 | C = US
7 | ST = CA
8 | L = Palo Alto
9 | O = Omnissa
10 | OU = EUC
11 | CN = nanw-dev
12 | [v3_ca]
13 | basicConstraints = critical, CA:TRUE
14 | subjectKeyIdentifier = hash
15 | authorityKeyIdentifier = keyid:always, issuer:always
16 | keyUsage = critical, cRLSign, digitalSignature, keyCertSign
17 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/tests/test-connection.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: "{{ include "..fullname" . }}-test-connection"
5 | labels:
6 | {{- include "..labels" . | nindent 4 }}
7 | annotations:
8 | "helm.sh/hook": test
9 | spec:
10 | containers:
11 | - name: wget
12 | image: busybox
13 | command: ['wget']
14 | args: ['{{ include "..fullname" . }}:{{ .Values.service.port }}']
15 | restartPolicy: Never
16 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/secret-env.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.envRenderSecret }}
2 | apiVersion: v1
3 | kind: Secret
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}-env
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | type: Opaque
10 | data:
11 | {{- range $key, $val := .Values.envRenderSecret }}
12 | {{ $key }}: {{ tpl ($val | toString) $ | b64enc | quote }}
13 | {{- end }}
14 | {{- end }}
15 |
--------------------------------------------------------------------------------
/deployment/scripts/es-delete-old-index.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | curl -k -u $ES_USERNAME:$ES_PASSWORD -X DELETE "$ES_URL/collie-k8s-cluster-info-elastic"
4 | curl -k -u $ES_USERNAME:$ES_PASSWORD -X DELETE "$ES_URL/collie-k8s-compliance-elastic"
5 | curl -k -u $ES_USERNAME:$ES_PASSWORD -X DELETE "$ES_URL/collie-k8s-resource-elastic"
6 | curl -k -u $ES_USERNAME:$ES_PASSWORD -X DELETE "$ES_URL/collie-k8s-activity-elastic"
7 | #curl -k -u $ES_USERNAME:$ES_PASSWORD -X DELETE "$ES_URL/collie-k8s-elastic"
8 |
--------------------------------------------------------------------------------
/collie/src/middleware.ts:
--------------------------------------------------------------------------------
1 | import { authMiddleware } from "@clerk/nextjs";
2 |
3 | // This example protects all routes including api/trpc routes
4 | // Please edit this to allow other routes to be public as needed.
5 | // See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your middleware
6 | export default authMiddleware({});
7 |
8 | export const config = {
9 | matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
10 | };
11 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *.orig
18 | *~
19 | # Various IDEs
20 | .project
21 | .idea/
22 | *.tmproj
23 | .vscode/
24 |
--------------------------------------------------------------------------------
/deployment/scripts/azure/check-req.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ping -c 1 collie-dev.org > /dev/null 2>&1
4 |
5 | if [ $? -ne 0 ]; then
6 | echo "Error: collie-dev.org is not set to local public IP."
7 | echo
8 | echo --------------------------
9 | echo Manual operation needed:
10 | echo 1. Use ifconfig to identify local machine public IP.
11 | echo 2. Update /etc/hosts, add 'collie-dev.org' to local host public IP.
12 | echo --------------------------
13 | echo
14 |
15 | exit 1
16 | fi
17 |
--------------------------------------------------------------------------------
/deployment/scripts/q.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | source .env
3 |
4 | for i in 1 2
5 | do
6 | RESP=$(curl -skS -u $COLLIE_ES_USERNAME:$COLLIE_ES_PASSWORD "$COLLIE_ES_URL/collie-k8s-activity-elastic/_search?q=a:9e888726a3908753" | jq ".hits.hits[0]._source")
7 |
8 | if [ "$RESP" = "null" ]; then
9 | echo "Not ready yet..."
10 | sleep 10
11 | else
12 | echo "$RESP"
13 | echo "OK"
14 | exit 0
15 | fi
16 | done
17 |
18 | echo "Failed waiting for agent report in ES"
19 | exit 1
20 |
--------------------------------------------------------------------------------
/deployment/scripts/minikube/check-req.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ping -c 1 collie-dev.org > /dev/null 2>&1
4 |
5 | if [ $? -ne 0 ]; then
6 | echo "Error: collie-dev.org is not set to local public IP."
7 | echo
8 | echo --------------------------
9 | echo Manual operation needed:
10 | echo 1. Use ifconfig to identify local machine public IP.
11 | echo 2. Update /etc/hosts, add 'collie-dev.org' to local host public IP.
12 | echo --------------------------
13 | echo
14 |
15 | exit 1
16 | fi
17 |
--------------------------------------------------------------------------------
/deployment/scripts/minikube/kill-processes-by-keyword.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | keyword="$1"
4 |
5 | # Get the process IDs of all processes matching the keyword
6 | pids=$(pgrep -f "$keyword")
7 |
8 | if [ -z "$pids" ]; then
9 | echo "No processes found matching the keyword '$keyword'"
10 | else
11 | # Kill each process
12 | for pid in $pids; do
13 | echo "Killing process: $pid"
14 | kill "$pid"
15 | done
16 | echo "All processes matching the keyword '$keyword' have been killed."
17 | fi
18 |
19 |
--------------------------------------------------------------------------------
/deployment/scripts/es-query.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ORG_ID=elastic
4 | INDEX_URL=$ES_URL/collie-k8s-$ORG_ID/_search
5 |
6 | curl -k -u $ES_KEY -XGET -H "Content-Type: application/json" -d '{
7 | "query": {
8 | "bool": {
9 | "must": [
10 | {
11 | "range": {
12 | "@timestamp": {
13 | "lt": "2023-06-08T22:52:13Z"
14 | }
15 | }
16 | },
17 | {
18 | "term": {
19 | "a": "demo"
20 | }
21 | }
22 | ]
23 | }
24 | }
25 | }' $INDEX_URL
26 |
27 |
--------------------------------------------------------------------------------
/deployment/scripts/azure/pv-my.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolume
3 | metadata:
4 | name: my-pv
5 | spec:
6 | capacity:
7 | storage: 5Gi # Specify the storage capacity
8 | volumeMode: Filesystem # or Block for raw block devices
9 | accessModes:
10 | - ReadWriteOnce # Read-write access for a single node
11 | persistentVolumeReclaimPolicy: Retain # or Delete
12 | storageClassName: standard # Use a StorageClass, if applicable
13 | hostPath:
14 | path: /mnt/data # Host path for the volume
15 |
--------------------------------------------------------------------------------
/api-server/service/template/cluster-role-binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: agent
5 | labels:
6 | app.kubernetes.io/name: agent
7 | app.kubernetes.io/instance: agent
8 | app.kubernetes.io/version: "v1"
9 | app.kubernetes.io/managed-by: collie
10 |
11 | roleRef:
12 | apiGroup: rbac.authorization.k8s.io
13 | kind: ClusterRole
14 | name: agent
15 | subjects:
16 | - kind: ServiceAccount
17 | name: agent
18 | namespace: collie-agent
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/tests/test-serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.testFramework.enabled .Values.serviceAccount.create }}
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | labels:
6 | {{- include "grafana.labels" . | nindent 4 }}
7 | name: {{ include "grafana.serviceAccountNameTest" . }}
8 | namespace: {{ include "grafana.namespace" . }}
9 | annotations:
10 | "helm.sh/hook": test-success
11 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
12 | {{- end }}
13 |
--------------------------------------------------------------------------------
/api-server/service/template/role-binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | name: agent
5 | labels:
6 | app.kubernetes.io/name: agent
7 | app.kubernetes.io/instance: agent
8 | app.kubernetes.io/version: "v1"
9 | app.kubernetes.io/managed-by: collie
10 | namespace: collie-agent
11 |
12 | roleRef:
13 | apiGroup: rbac.authorization.k8s.io
14 | kind: Role
15 | name: agent
16 | subjects:
17 | - kind: ServiceAccount
18 | name: agent
19 | namespace: collie-agent
--------------------------------------------------------------------------------
/collie/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/deployment/helm-charts/es/values-custom.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # Shrink default JVM heap.
3 | esJavaOpts: "-Xmx256m -Xms256m"
4 |
5 | # Allocate smaller chunks of memory per pod.
6 | resources:
7 | requests:
8 | cpu: "1000m"
9 | memory: "512M"
10 | limits:
11 | cpu: "1000m"
12 | memory: "1024M"
13 |
14 | # Request smaller persistent volumes.
15 | volumeClaimTemplate:
16 | accessModes: [ "ReadWriteOnce" ]
17 | # for minikube: standard
18 | # for AKS: managed-csi
19 | storageClassName: "standard"
20 | resources:
21 | requests:
22 | storage: 100M
23 |
--------------------------------------------------------------------------------
/deployment/helm-charts/prep.sh:
--------------------------------------------------------------------------------
1 | brew install kubectl
2 | brew upgrade kubectl
3 | brew link --overwrite kubernetes-cli
4 | brew install helm
5 | helm repo add grafana https://grafana.github.io/helm-charts
6 | helm repo add elastic https://helm.elastic.co
7 |
8 | brew unlink minikube
9 | brew install minikube
10 | brew link minikube
11 |
12 | minikube delete
13 | minikube config set cpus 4
14 | minikube config set memory 4096
15 | minikube start
16 | minikube addons enable default-storageclass
17 | minikube addons enable storage-provisioner
18 | minikube addons enable ingress
19 |
20 |
--------------------------------------------------------------------------------
/k8s-agent/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/collie-agent-amd64 .
3 | docker build -t collie.azurecr.io/collie-agent:1 .
4 |
5 | generate:
6 | go generate ./...
7 |
8 | push:
9 | docker push collie.azurecr.io/collie-agent:1
10 |
11 | deploy:
12 | cat deployment.yaml | envsubst | kubectl apply -f -
13 |
14 | SHELL := /bin/bash
15 | run:
16 | source ./.env && go run .
17 |
18 | test:
19 | go test ./... -race
20 |
21 | lint:
22 | go vet ./...
23 | gofmt -w -s .
24 | golangci-lint run
25 |
26 | release: build push
27 |
--------------------------------------------------------------------------------
/api-server/service/template/resource-quota.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ResourceQuota
3 | metadata:
4 | name: agent-critical-pods
5 | namespace: collie-agent
6 | labels:
7 | app.kubernetes.io/name: agent
8 | app.kubernetes.io/instance: agent
9 | app.kubernetes.io/version: "v1"
10 | app.kubernetes.io/managed-by: collie
11 | spec:
12 | scopeSelector:
13 | matchExpressions:
14 | - operator: In
15 | scopeName: PriorityClass
16 | values:
17 | # Required to ensure agent is always running to provide autoscaling capabilities.
18 | - system-cluster-critical
--------------------------------------------------------------------------------
/collie/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from 'tailwindcss'
2 |
3 | const config: Config = {
4 | content: [
5 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
6 | './src/components/**/*.{js,ts,jsx,tsx,mdx}',
7 | './src/app/**/*.{js,ts,jsx,tsx,mdx}',
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
13 | 'gradient-conic':
14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
15 | },
16 | },
17 | },
18 | plugins: [],
19 | }
20 | export default config
21 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceAccount.create }}
2 | {{- $root := . -}}
3 | apiVersion: v1
4 | kind: ServiceAccount
5 | metadata:
6 | labels:
7 | {{- include "grafana.labels" . | nindent 4 }}
8 | {{- with .Values.serviceAccount.labels }}
9 | {{- toYaml . | nindent 4 }}
10 | {{- end }}
11 | {{- with .Values.serviceAccount.annotations }}
12 | annotations:
13 | {{- tpl (toYaml . | nindent 4) $root }}
14 | {{- end }}
15 | name: {{ include "grafana.serviceAccountName" . }}
16 | namespace: {{ include "grafana.namespace" . }}
17 | {{- end }}
18 |
--------------------------------------------------------------------------------
/api-server/service/template/role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: Role
3 | metadata:
4 | name: agent
5 | namespace: collie-agent
6 | labels:
7 | app.kubernetes.io/name: agent
8 | app.kubernetes.io/instance: agent
9 | app.kubernetes.io/version: "v1"
10 | app.kubernetes.io/managed-by: collie
11 | rules:
12 | # ---
13 | # Required for proportional vertical cluster autoscaler to adjust agent requests/limits.
14 | # ---
15 | - apiGroups:
16 | - "apps"
17 | resources:
18 | - deployments
19 | resourceNames:
20 | - agent
21 | verbs:
22 | - patch
--------------------------------------------------------------------------------
/collie/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/deployment/scripts/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: api-server
5 | namespace: collie-server
6 | spec:
7 | rules:
8 | - http:
9 | paths:
10 | - path: /portal
11 | pathType: Prefix
12 | backend:
13 | service:
14 | name: portal
15 | port:
16 | name: http
17 | - path: /
18 | pathType: Prefix
19 | backend:
20 | service:
21 | name: api-server
22 | port:
23 | name: http
24 |
25 |
--------------------------------------------------------------------------------
/deployment/scripts/minikube/deploy-server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | helm list -n collie-server -a | grep api-server > /dev/null 2>&1
4 | if [ $? -eq 0 ]; then
5 | helm uninstall -n collie-server api-server --wait
6 | fi
7 |
8 | helm install -n collie-server api-server ./api-server --wait
9 |
10 | sleep 10
11 |
12 | source ./kill-processes-by-keyword.sh "kubectl port-forward -n collie-server --address 0.0.0.0 services/api-server"
13 | kubectl port-forward -n collie-server --address 0.0.0.0 services/api-server 8080:8080 &
14 |
15 | sleep 2
16 |
17 | echo
18 | echo Open browser: http://collie-dev.org:8080/collie/portal/login
19 | echo
20 |
21 |
--------------------------------------------------------------------------------
/dashboard/cert/req-nanw.conf:
--------------------------------------------------------------------------------
1 | [req]
2 | distinguished_name = req_distinguished_name
3 | req_extensions = v3_req
4 | prompt = no
5 | [req_distinguished_name]
6 | C = US
7 | ST = CA
8 | L = Palo Alto
9 | O = Omnissa
10 | OU = EUC
11 | CN = nanw.eng.omnissa.com
12 | [v3_req]
13 | basicConstraints = critical, CA:FALSE
14 | subjectKeyIdentifier = hash
15 | keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement, dataEncipherment
16 | extendedKeyUsage = critical, serverAuth
17 | subjectAltName = @alt_names
18 | [alt_names]
19 | DNS.1 = nanw.eng.omnissa.com
20 | DNS.2 = collie.eng.omnissa.com
21 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: {{ include "..fullname" . }}
5 | labels:
6 | {{- include "..labels" . | nindent 4 }}
7 | data:
8 | OAUTH_CSP_CLIENTID: {{ .Values.oauth.csp_clientid | b64enc }}
9 | OAUTH_CSP_CLIENTSECRET: {{ .Values.oauth.csp_clientsecret | b64enc }}
10 | OAUTH_GITLAB_CLIENTID: {{ .Values.oauth.gitlab_clientid | b64enc }}
11 | OAUTH_GITLAB_CLIENTSECRET: {{ .Values.oauth.gitlab_clientsecret | b64enc }}
12 | OAUTH_GOOGLE_CLIENTID: {{ .Values.oauth.google_clientid | b64enc }}
13 | OAUTH_GOOGLE_CLIENTSECRET: {{ .Values.oauth.google_clientsecret | b64enc }}
--------------------------------------------------------------------------------
/deployment/scripts/azure/deploy-server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | helm list -n collie-server -a | grep api-server > /dev/null 2>&1
4 | if [ $? -eq 0 ]; then
5 | helm uninstall -n collie-server api-server --wait
6 | fi
7 |
8 | cd ../../helm-charts
9 | helm install -n collie-server api-server ./api-server --wait
10 |
11 | sleep 10
12 |
13 | #source ./kill-processes-by-keyword.sh "kubectl port-forward -n collie-server --address 0.0.0.0 services/api-server"
14 | #kubectl port-forward -n collie-server --address 0.0.0.0 services/api-server 8080:8080 &
15 |
16 | sleep 2
17 |
18 | echo
19 | echo Open browser: http://collie-dev.org:8080/collie/portal/login
20 | echo
21 |
22 |
--------------------------------------------------------------------------------
/collie/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --foreground-rgb: 0, 0, 0;
7 | --background-start-rgb: 214, 219, 220;
8 | --background-end-rgb: 255, 255, 255;
9 | }
10 |
11 | @media (prefers-color-scheme: dark) {
12 | :root {
13 | --foreground-rgb: 255, 255, 255;
14 | --background-start-rgb: 0, 0, 0;
15 | --background-end-rgb: 0, 0, 0;
16 | }
17 | }
18 |
19 | body {
20 | color: rgb(var(--foreground-rgb));
21 | background: linear-gradient(
22 | to bottom,
23 | transparent,
24 | rgb(var(--background-end-rgb))
25 | )
26 | rgb(var(--background-start-rgb));
27 | }
28 |
--------------------------------------------------------------------------------
/collie/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import './globals.css'
2 | import type { Metadata } from 'next'
3 | import { Inter } from 'next/font/google'
4 | import { ClerkProvider } from '@clerk/nextjs'
5 |
6 | const inter = Inter({ subsets: ['latin'] })
7 |
8 | export const metadata: Metadata = {
9 | title: 'Collie - K8s Compliance Dashboard',
10 | description: 'K8s Compliance Dashboard',
11 | }
12 |
13 | export default function RootLayout({
14 | children,
15 | }: {
16 | children: React.ReactNode
17 | }) {
18 | return (
19 |
20 |
21 | {children}
22 |
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/deployment/scripts/azure/delete-all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source ./delete-agent.sh
4 |
5 | helm list -n collie-server -a | grep api-server > /dev/null 2>&1
6 | if [ $? -eq 0 ]; then
7 | helm uninstall -n collie-server api-server --wait
8 | fi
9 | helm list -n collie-server -a | grep grafana > /dev/null 2>&1
10 | if [ $? -eq 0 ]; then
11 | helm uninstall -n collie-server grafana --wait
12 | fi
13 | helm list -n collie-server -a | grep es > /dev/null 2>&1
14 | if [ $? -eq 0 ]; then
15 | helm uninstall -n collie-server es --wait
16 | fi
17 |
18 | kubectl get ns/collie-server > /dev/null 2>&1
19 | if [ $? -eq 0 ]; then
20 | kubectl delete namespace collie-server --wait
21 | fi
22 |
23 |
24 |
--------------------------------------------------------------------------------
/deployment/scripts/es-recreate-index.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ORG_ID=elastic
4 | INDEX_URL=$ES_URL/collie-k8s-$ORG_ID
5 |
6 | curl -k -u $ES_KEY -X DELETE "$INDEX_URL"
7 |
8 | curl -k -u $ES_KEY -X PUT -H "Content-Type: application/json" -d '
9 | {
10 | "mappings": {
11 | "properties": {
12 | "resource": {
13 | "dynamic": false,
14 | "properties": {
15 | "metadata": {
16 | "properties": {
17 | "name": {
18 | "type": "text"
19 | },
20 | "namespace": {
21 | "type": "text"
22 | }
23 | }
24 | }
25 | }
26 | }
27 | }
28 | }
29 | }' $INDEX_URL
30 |
31 |
--------------------------------------------------------------------------------
/collie/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/collie/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "collie",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@clerk/nextjs": "^4.24.2",
13 | "next": "latest",
14 | "react": "latest",
15 | "react-dom": "latest"
16 | },
17 | "devDependencies": {
18 | "@types/node": "latest",
19 | "@types/react": "latest",
20 | "@types/react-dom": "latest",
21 | "autoprefixer": "latest",
22 | "eslint": "latest",
23 | "eslint-config-next": "latest",
24 | "postcss": "latest",
25 | "tailwindcss": "latest",
26 | "typescript": "latest"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/tests/test-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.testFramework.enabled }}
2 | apiVersion: v1
3 | kind: ConfigMap
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}-test
6 | namespace: {{ include "grafana.namespace" . }}
7 | annotations:
8 | "helm.sh/hook": test-success
9 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
10 | labels:
11 | {{- include "grafana.labels" . | nindent 4 }}
12 | data:
13 | run.sh: |-
14 | @test "Test Health" {
15 | url="http://{{ include "grafana.fullname" . }}/api/health"
16 |
17 | code=$(wget --server-response --spider --timeout 90 --tries 10 ${url} 2>&1 | awk '/^ HTTP/{print $2}')
18 | [ "$code" == "200" ]
19 | }
20 | {{- end }}
21 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/tests/test-role.yaml:
--------------------------------------------------------------------------------
1 | {{- if and (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") .Values.testFramework.enabled .Values.rbac.pspEnabled }}
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}-test
6 | namespace: {{ include "grafana.namespace" . }}
7 | annotations:
8 | "helm.sh/hook": test-success
9 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
10 | labels:
11 | {{- include "grafana.labels" . | nindent 4 }}
12 | rules:
13 | - apiGroups: ['policy']
14 | resources: ['podsecuritypolicies']
15 | verbs: ['use']
16 | resourceNames: [{{ include "grafana.fullname" . }}-test]
17 | {{- end }}
18 |
--------------------------------------------------------------------------------
/deployment/scripts/minikube/delete-all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source ./kill-processes-by-keyword.sh "kubectl port-forward -n collie-server"
4 |
5 | source ./delete-agent.sh
6 |
7 | helm list -n collie-server -a | grep api-server > /dev/null 2>&1
8 | if [ $? -eq 0 ]; then
9 | helm uninstall -n collie-server api-server --wait
10 | fi
11 | helm list -n collie-server -a | grep grafana > /dev/null 2>&1
12 | if [ $? -eq 0 ]; then
13 | helm uninstall -n collie-server grafana --wait
14 | fi
15 | helm list -n collie-server -a | grep es > /dev/null 2>&1
16 | if [ $? -eq 0 ]; then
17 | helm uninstall -n collie-server es --wait
18 | fi
19 |
20 | kubectl get ns/collie-server > /dev/null 2>&1
21 | if [ $? -eq 0 ]; then
22 | kubectl delete namespace collie-server --wait
23 | fi
24 |
25 |
26 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/rbac.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: Role
3 | metadata:
4 | name: {{ include "..fullname" . }}
5 | labels:
6 | {{- include "..labels" . | nindent 4 }}
7 | rules:
8 | - apiGroups:
9 | - "apps"
10 | resources:
11 | - deployments
12 | resourceNames:
13 | - api-server
14 | verbs:
15 | - patch
16 |
17 | ---
18 | apiVersion: rbac.authorization.k8s.io/v1
19 | kind: RoleBinding
20 | metadata:
21 | name: {{ include "..fullname" . }}
22 | labels:
23 | {{- include "..labels" . | nindent 4 }}
24 |
25 | roleRef:
26 | apiGroup: rbac.authorization.k8s.io
27 | kind: Role
28 | name: api-server
29 | subjects:
30 | - kind: ServiceAccount
31 | name: api-server
32 | namespace: collie-server
33 |
34 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/poddisruptionbudget.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.podDisruptionBudget }}
2 | apiVersion: {{ include "grafana.podDisruptionBudget.apiVersion" . }}
3 | kind: PodDisruptionBudget
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | {{- with .Values.labels }}
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | spec:
13 | {{- with .Values.podDisruptionBudget.minAvailable }}
14 | minAvailable: {{ . }}
15 | {{- end }}
16 | {{- with .Values.podDisruptionBudget.maxUnavailable }}
17 | maxUnavailable: {{ . }}
18 | {{- end }}
19 | selector:
20 | matchLabels:
21 | {{- include "grafana.selectorLabels" . | nindent 6 }}
22 | {{- end }}
23 |
--------------------------------------------------------------------------------
/deployment/scripts/upgrade-minikube.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | minikube delete && \
4 | sudo rm -rf /usr/local/bin/minikube && \
5 | sudo curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && \
6 | sudo chmod +x minikube && \
7 | sudo cp minikube /usr/local/bin/ && \
8 | sudo rm minikube && \
9 | minikube start &&\
10 |
11 | # Enabling addons: ingress, dashboard
12 | minikube addons enable ingress && \
13 | minikube addons enable dashboard && \
14 | minikube addons enable metrics-server && \
15 | # Showing enabled addons
16 | echo '\n\n\033[4;33m Enabled Addons \033[0m' && \
17 | minikube addons list | grep STATUS && minikube addons list | grep enabled && \
18 |
19 | # Showing current status of Minikube
20 | echo '\n\n\033[4;33m Current status of Minikube \033[0m' && minikube status
--------------------------------------------------------------------------------
/api-server/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/collie-api-server-amd64 .
3 | docker rmi --force collie.azurecr.io/collie-api-server:1
4 | docker build -t collie.azurecr.io/collie-api-server:1 .
5 |
6 | swagger:
7 | go get -u github.com/swaggo/swag/cmd/swag
8 | swag init
9 |
10 | checkupdate:
11 | go list -u -m all
12 | update:
13 | go get -u
14 | generate:
15 | go generate ./...
16 |
17 | push:
18 | docker push collie.azurecr.io/collie-api-server:1
19 |
20 | deploy:
21 | cat ../helm-charts/api-server.yaml | envsubst | kubectl apply -f -
22 |
23 | SHELL := /bin/bash
24 | run:
25 | source ./.env && go run .
26 |
27 | test:
28 | go test ./... -race
29 |
30 | lint:
31 | go vet ./...
32 | gofmt -w -s .
33 | golangci-lint run
34 |
35 | release: build push
36 |
--------------------------------------------------------------------------------
/api-server/model/error.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package model
17 |
18 | import "errors"
19 |
20 | var (
21 | // ErrNoRow example
22 | ErrNoRow = errors.New("no rows in result set")
23 | )
24 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/headless-service.yaml:
--------------------------------------------------------------------------------
1 | {{- $sts := list "sts" "StatefulSet" "statefulset" -}}
2 | {{- if or .Values.headlessService (and .Values.persistence.enabled (not .Values.persistence.existingClaim) (has .Values.persistence.type $sts)) }}
3 | apiVersion: v1
4 | kind: Service
5 | metadata:
6 | name: {{ include "grafana.fullname" . }}-headless
7 | namespace: {{ include "grafana.namespace" . }}
8 | labels:
9 | {{- include "grafana.labels" . | nindent 4 }}
10 | {{- with .Values.annotations }}
11 | annotations:
12 | {{- toYaml . | nindent 4 }}
13 | {{- end }}
14 | spec:
15 | clusterIP: None
16 | selector:
17 | {{- include "grafana.selectorLabels" . | nindent 4 }}
18 | type: ClusterIP
19 | ports:
20 | - name: {{ .Values.gossipPortName }}-tcp
21 | port: 9094
22 | {{- end }}
23 |
--------------------------------------------------------------------------------
/api-server/model/admin.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package model
17 |
18 | // Admin example
19 | type Admin struct {
20 | ID int `json:"id" example:"1"`
21 | Name string `json:"name" example:"admin name"`
22 | }
23 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/rolebinding.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.rbac.create }}
2 | apiVersion: {{ include "grafana.rbac.apiVersion" . }}
3 | kind: RoleBinding
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | {{- with .Values.annotations }}
10 | annotations:
11 | {{- toYaml . | nindent 4 }}
12 | {{- end }}
13 | roleRef:
14 | apiGroup: rbac.authorization.k8s.io
15 | kind: Role
16 | {{- if .Values.rbac.useExistingRole }}
17 | name: {{ .Values.rbac.useExistingRole }}
18 | {{- else }}
19 | name: {{ include "grafana.fullname" . }}
20 | {{- end }}
21 | subjects:
22 | - kind: ServiceAccount
23 | name: {{ include "grafana.serviceAccountName" . }}
24 | namespace: {{ include "grafana.namespace" . }}
25 | {{- end }}
26 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/tests/test-rolebinding.yaml:
--------------------------------------------------------------------------------
1 | {{- if and (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") .Values.testFramework.enabled .Values.rbac.pspEnabled }}
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: RoleBinding
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}-test
6 | namespace: {{ include "grafana.namespace" . }}
7 | annotations:
8 | "helm.sh/hook": test-success
9 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
10 | labels:
11 | {{- include "grafana.labels" . | nindent 4 }}
12 | roleRef:
13 | apiGroup: rbac.authorization.k8s.io
14 | kind: Role
15 | name: {{ include "grafana.fullname" . }}-test
16 | subjects:
17 | - kind: ServiceAccount
18 | name: {{ include "grafana.serviceAccountNameTest" . }}
19 | namespace: {{ include "grafana.namespace" . }}
20 | {{- end }}
21 |
--------------------------------------------------------------------------------
/dashboard/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | # You can override the included template(s) by including variable overrides
2 | # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
3 | # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
4 | # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
5 | # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
6 | # Note that environment variables can be set in several places
7 | # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
8 | stages:
9 | - test
10 | sast:
11 | stage: test
12 | include:
13 | - template: Security/SAST.gitlab-ci.yml
14 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/clusterrolebinding.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.rbac.create (or (not .Values.rbac.namespaced) .Values.rbac.extraClusterRoleRules) }}
2 | kind: ClusterRoleBinding
3 | apiVersion: rbac.authorization.k8s.io/v1
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}-clusterrolebinding
6 | labels:
7 | {{- include "grafana.labels" . | nindent 4 }}
8 | {{- with .Values.annotations }}
9 | annotations:
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | subjects:
13 | - kind: ServiceAccount
14 | name: {{ include "grafana.serviceAccountName" . }}
15 | namespace: {{ include "grafana.namespace" . }}
16 | roleRef:
17 | kind: ClusterRole
18 | {{- if .Values.rbac.useExistingRole }}
19 | name: {{ .Values.rbac.useExistingRole }}
20 | {{- else }}
21 | name: {{ include "grafana.fullname" . }}-clusterrole
22 | {{- end }}
23 | apiGroup: rbac.authorization.k8s.io
24 | {{- end }}
25 |
--------------------------------------------------------------------------------
/deployment/scripts/deploy-api-server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | B64CMD="base64"
4 |
5 | source .env
6 |
7 | # Delete deployments
8 | kubectl delete namespace collie-server collie-agent --wait=true
9 |
10 | # redeploy
11 | export ES_KEY_B64=$(echo -n $ES_KEY | $B64CMD)
12 | export OAUTH_CSP_CLIENTID_B64=$(echo -n $OAUTH_CSP_CLIENTID | $B64CMD)
13 | export OAUTH_CSP_CLIENTSECRET_B64=$(echo -n $OAUTH_CSP_CLIENTSECRET | $B64CMD)
14 | export OAUTH_GITLAB_CLIENTID_B64=$(echo -n $OAUTH_GITLAB_CLIENTID | $B64CMD)
15 | export OAUTH_GITLAB_CLIENTSECRET_B64=$(echo -n $OAUTH_GITLAB_CLIENTSECRET | $B64CMD)
16 |
17 | envsubst < api-server.yaml | kubectl apply -f -
18 | kubectl wait deployment -n collie-server api-server --for condition=Available=True --timeout=90s
19 | AUTH_TOKEN=gitlab/$(source auth-gitlab.sh | jq -r '.access_token')
20 | sleep 10
21 | curl -skH "Authorization: $AUTH_TOKEN" https://collie.eng.omnissa.com/collie/api/v1/onboarding/bootstrap
22 |
23 |
--------------------------------------------------------------------------------
/k8s-agent/internal/config/version.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package config
17 |
18 | import "fmt"
19 |
20 | type AgentVersion struct {
21 | GitCommit, GitRef, Version string
22 | }
23 |
24 | func (a *AgentVersion) String() string {
25 | return fmt.Sprintf("GitCommit=%q GitRef=%q Version=%q", a.GitCommit, a.GitRef, a.Version)
26 | }
27 |
--------------------------------------------------------------------------------
/deployment/scripts/minikube/debug-image.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | NAMESPACE=collie-server
4 | TARGET_CONFIG=pod/elasticsearch-master-0
5 |
6 | kubectl -n $NAMESPACE get $TARGET_CONFIG -ojson | jq . > pod-debug1.json
7 |
8 | #jq "del(.spec.containers[0].readinessProbe)" pod-debug.json > pod-debug.json
9 | jq "del(.spec.initContainers[0].command)" pod-debug1.json > pod-debug2.json
10 | jq "del(.spec.initContainers[0].args)" pod-debug2.json > pod-debug3.json
11 | jq '.spec.initContainers[0] += {command: ["/bin/bash"], args: ["-c", "while true; do sleep 30; done;"]}' pod-debug3.json > pod-debug4.json
12 | jq '.metadata.name = "debug-image"' pod-debug4.json > pod-debug5.json
13 |
14 | cat pod-debug5.json | yq -P > pod-debug.yml
15 |
16 | kubectl -n $NAMESPACE delete pod debug-image
17 | sleep 20
18 | kubectl -n $NAMESPACE apply -f pod-debug.yml
19 | sleep 20
20 | kubectl -n $NAMESPACE exec -it debug-image -- /bin/bash
21 |
22 | # rm -f pod-debug*.json
23 | # rm -f pod-debug*.yml
24 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/tests/test-podsecuritypolicy.yaml:
--------------------------------------------------------------------------------
1 | {{- if and (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") .Values.testFramework.enabled .Values.rbac.pspEnabled }}
2 | apiVersion: policy/v1beta1
3 | kind: PodSecurityPolicy
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}-test
6 | annotations:
7 | "helm.sh/hook": test-success
8 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
9 | labels:
10 | {{- include "grafana.labels" . | nindent 4 }}
11 | spec:
12 | allowPrivilegeEscalation: true
13 | privileged: false
14 | hostNetwork: false
15 | hostIPC: false
16 | hostPID: false
17 | fsGroup:
18 | rule: RunAsAny
19 | seLinux:
20 | rule: RunAsAny
21 | supplementalGroups:
22 | rule: RunAsAny
23 | runAsUser:
24 | rule: RunAsAny
25 | volumes:
26 | - configMap
27 | - downwardAPI
28 | - emptyDir
29 | - projected
30 | - csi
31 | - secret
32 | {{- end }}
33 |
--------------------------------------------------------------------------------
/api-server/controller/controller.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package controller
17 |
18 | // Controller example
19 | type Controller struct {
20 | }
21 |
22 | // NewController example
23 | func NewController() *Controller {
24 | return &Controller{}
25 | }
26 |
27 | // Message example
28 | type Message struct {
29 | Message string `json:"message" example:"message"`
30 | }
31 |
--------------------------------------------------------------------------------
/dashboard/collie/grafana-export-dashboards.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="grafana-dashboards"
4 |
5 | # Iterate through dashboards using the current API Key
6 | for dashboard_uid in $(curl -sS -u $COLLIE_GRAFANA_CREDENTIAL $COLLIE_GRAFANA_URL/api/search\?query\=\& | jq -r '.[] | select( .type | contains("dash-db")) | .uid'); do
7 | url=$(echo $COLLIE_GRAFANA_URL/api/dashboards/uid/$dashboard_uid | tr -d '\r')
8 | dashboard_json=$(curl -sS -u $COLLIE_GRAFANA_CREDENTIAL $url)
9 | dashboard_title=$(echo $dashboard_json | jq -r '.dashboard | .title' | sed -r 's/[ \/]+/_/g')
10 | dashboard_version=$(echo $dashboard_json | jq -r '.dashboard | .version')
11 | folder_title="$(echo $dashboard_json | jq -r '.meta | .folderTitle')"
12 |
13 | echo "Creating: ${DIR}/${folder_title}/${dashboard_title}_v${dashboard_version}.json"
14 | mkdir -p "${DIR}/${folder_title}"
15 | echo ${dashboard_json} | jq -r {meta:.meta}+.dashboard > "${DIR}/${folder_title}/${dashboard_title}_v${dashboard_version}.json"
16 | done
--------------------------------------------------------------------------------
/deployment/scripts/auth-gitlab.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source .env
4 |
5 | GITLAB_URL="https://gitlab.eng.omnissa.com"
6 | SCOPES="read_user"
7 |
8 | # Request an OAuth access token from GitLab
9 | response=$(curl --silent --request POST \
10 | --url "$GITLAB_URL/oauth/token" \
11 | --header 'content-type: application/x-www-form-urlencoded' \
12 | --data "client_id=$OAUTH_GITLAB_CLIENTID&client_secret=$OAUTH_GITLAB_CLIENTSECRET&grant_type=client_credentials&scope=$SCOPES"
13 | )
14 |
15 | echo $response
16 |
17 | # Extract the access token from the response using jq
18 | # ACCESS_TOKEN=$(echo "$response" | jq -r '.access_token')
19 | # echo Access token: $ACCESS_TOKEN
20 | # echo Token info: $(curl -s $GITLAB_URL/oauth/token/info?access_token=$ACCESS_TOKEN)
21 |
22 | # USER_RESPONSE=$(curl -s $GITLAB_URL/oauth/userinfo?access_token=$ACCESS_TOKEN)
23 | # echo OAuth user info: $USER_RESPONSE
24 |
25 | # USER_RESPONSE=$(curl -s $GITLAB_URL/api/v4/user?access_token=$ACCESS_TOKEN)
26 | # echo Gitlab info: $USER_RESPONSE
27 |
28 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: grafana
3 | version: 6.57.1
4 | appVersion: 9.5.3
5 | kubeVersion: "^1.8.0-0"
6 | description: The leading tool for querying and visualizing time series and metrics.
7 | home: https://grafana.net
8 | icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png
9 | sources:
10 | - https://github.com/grafana/grafana
11 | - https://github.com/grafana/helm-charts
12 | annotations:
13 | "artifacthub.io/links": |
14 | - name: Chart Source
15 | url: https://github.com/grafana/helm-charts
16 | - name: Upstream Project
17 | url: https://github.com/grafana/grafana
18 | maintainers:
19 | - name: zanhsieh
20 | email: zanhsieh@gmail.com
21 | - name: rtluckie
22 | email: rluckie@cisco.com
23 | - name: maorfr
24 | email: maor.friedman@redhat.com
25 | - name: Xtigyro
26 | email: miroslav.hadzhiev@gmail.com
27 | - name: torstenwalter
28 | email: mail@torstenwalter.de
29 | engine: gotpl
30 | type: application
--------------------------------------------------------------------------------
/deployment/scripts/bad-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | deployment.kubernetes.io/revision: "1"
6 | labels:
7 | app.kubernetes.io/instance: helloworld-web
8 | app.kubernetes.io/name: helloworld-web
9 | app.kubernetes.io/version: v0.42.1
10 | name: helloworld-web
11 | namespace: default
12 | spec:
13 | replicas: 1
14 | revisionHistoryLimit: 10
15 | selector:
16 | matchLabels:
17 | app.kubernetes.io/instance: helloworld-web
18 | app.kubernetes.io/name: helloworld-web
19 | template:
20 | metadata:
21 | labels:
22 | app.kubernetes.io/instance: helloworld-web
23 | app.kubernetes.io/name: helloworld-web
24 | spec:
25 | containers:
26 | - env:
27 | - name: PROVIDER
28 | value: aks
29 | image: crccheck/hello-world
30 | imagePullPolicy: IfNotPresent
31 | name: helloworld-web
32 | ports:
33 | - containerPort: 80
34 | hostPort: 8080
35 | hostIP: 10.0.0.1
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/hpa.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.autoscaling.enabled }}
2 | apiVersion: autoscaling/v2beta1
3 | kind: HorizontalPodAutoscaler
4 | metadata:
5 | name: {{ include "..fullname" . }}
6 | labels:
7 | {{- include "..labels" . | nindent 4 }}
8 | spec:
9 | scaleTargetRef:
10 | apiVersion: apps/v1
11 | kind: Deployment
12 | name: {{ include "..fullname" . }}
13 | minReplicas: {{ .Values.autoscaling.minReplicas }}
14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }}
15 | metrics:
16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
17 | - type: Resource
18 | resource:
19 | name: cpu
20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
21 | {{- end }}
22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
23 | - type: Resource
24 | resource:
25 | name: memory
26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
27 | {{- end }}
28 | {{- end }}
29 |
--------------------------------------------------------------------------------
/k8s-agent/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | # This file is a template, and might need editing before it works on your project.
2 | image: grpc/go
3 |
4 | before_script:
5 | - export REPO_NAME=`echo $CI_PROJECT_URL|sed 's/.*:\/\///g;'`
6 | - mkdir -p $GOPATH/src/$(dirname $REPO_NAME)
7 | - ln -svf $CI_PROJECT_DIR $GOPATH/src/$REPO_NAME
8 | - cd $GOPATH/src/$REPO_NAME
9 | - go get
10 |
11 | stages:
12 | - test
13 | - build
14 |
15 | format:
16 | stage: test
17 | script:
18 | - go fmt $(go list ./... | grep -v /vendor/)
19 | - go vet $(go list ./... | grep -v /vendor/)
20 |
21 | test:
22 | stage: test
23 | script:
24 | - go test -race $(go list ./... | grep -v /vendor/)
25 |
26 | compile:
27 | stage: build
28 | script:
29 | - go get github.com/micro/protoc-gen-micro
30 | - protoc --proto_path=$GOPATH:. --micro_out=. --go_out=. $GOPATH/src/$REPO_NAME/proto/greeter.proto
31 | - go build -race -ldflags "-extldflags '-static'" -o $CI_PROJECT_DIR/service
32 | artifacts:
33 | paths:
34 | - service
35 |
--------------------------------------------------------------------------------
/api-server/httputil/error.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package httputil
17 |
18 | import "github.com/gin-gonic/gin"
19 |
20 | func Abort(ctx *gin.Context, code int, err error) {
21 | er := HTTPError{
22 | Code: code,
23 | Message: err.Error(),
24 | }
25 | ctx.AbortWithStatusJSON(code, er)
26 | }
27 |
28 | type HTTPError struct {
29 | Code int `json:"code" example:"400"`
30 | Message string `json:"message" example:"status bad request"`
31 | }
32 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/dashboards-json-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.dashboards }}
2 | {{ $files := .Files }}
3 | {{- range $provider, $dashboards := .Values.dashboards }}
4 | apiVersion: v1
5 | kind: ConfigMap
6 | metadata:
7 | name: {{ include "grafana.fullname" $ }}-dashboards-{{ $provider }}
8 | namespace: {{ include "grafana.namespace" $ }}
9 | labels:
10 | {{- include "grafana.labels" $ | nindent 4 }}
11 | dashboard-provider: {{ $provider }}
12 | {{- if $dashboards }}
13 | data:
14 | {{- $dashboardFound := false }}
15 | {{- range $key, $value := $dashboards }}
16 | {{- if (or (hasKey $value "json") (hasKey $value "file")) }}
17 | {{- $dashboardFound = true }}
18 | {{- print $key | nindent 2 }}.json:
19 | {{- if hasKey $value "json" }}
20 | |-
21 | {{- $value.json | nindent 6 }}
22 | {{- end }}
23 | {{- if hasKey $value "file" }}
24 | {{- toYaml ( $files.Get $value.file ) | nindent 4}}
25 | {{- end }}
26 | {{- end }}
27 | {{- end }}
28 | {{- if not $dashboardFound }}
29 | {}
30 | {{- end }}
31 | {{- end }}
32 | ---
33 | {{- end }}
34 |
35 | {{- end }}
36 |
--------------------------------------------------------------------------------
/dashboard/cert/gen-self-signed.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | rm -f self-signed-root.key
4 | rm -f self-signed-root.crt
5 | rm -f self-signed-root.srl
6 |
7 | # Generate self-signed root CA
8 | openssl req -config req-self-signed-root.conf -newkey rsa:2048 -x509 -days 3650 -nodes -sha256 -out self-signed-root.crt -keyout self-signed-root.key
9 |
10 | #openssl x509 -text -noout -purpose -ocsp_uri -in self-signed-root.crt
11 |
12 | # Verify
13 | #openssl rsa -check -noout -in self-signed-root.key
14 | #openssl x509 -noout -modulus -in self-signed-root.crt | openssl md5
15 | #openssl rsa -noout -modulus -in self-signed-root.key | openssl md5
16 |
17 | # Sign nanw
18 | openssl x509 -req -days 3650 -sha256 -CA self-signed-root.crt -CAkey self-signed-root.key -CAcreateserial -in nanw.csr -extfile v3_ext.txt -out nanw.crt
19 |
20 | # Verify
21 | #openssl rsa -check -noout -in nanw.key
22 | #openssl x509 -noout -modulus -in nanw.crt | openssl md5
23 | #openssl rsa -noout -modulus -in nanw.key | openssl md5
24 |
25 | #openssl verify -verbose -CAfile self-signed-root.crt nanw.crt
26 |
27 | openssl x509 -text -noout -purpose -ocsp_uri -in nanw.crt
28 |
--------------------------------------------------------------------------------
/api-server/config/oauth-default.yaml:
--------------------------------------------------------------------------------
1 | oauth:
2 | hostUrl: http://localhost:8080
3 | csp:
4 | host: https://console-stg.cloud.omnissa.com
5 | authUrl: ${oauth.csp.host}/csp/gateway/discovery
6 | tokenUrl: ${oauth.csp.host}/csp/gateway/am/api/auth/token
7 | jwksUrl: ${oauth.csp.host}/csp/gateway/am/api/auth/token-public-key?format=jwks
8 | redirectUrl: ${collie.url}/collie/oauth/callback/csp
9 | #clientId:
10 | #clientSecret:
11 | orgId: e7923078-6663-4178-9555-bcd5a036693e
12 | issuer: https://gaz-preview.csp-vidm-prod.com
13 | gitlab:
14 | host: https://gitlab.eng.omnissa.com
15 | authUrl: ${oauth.gitlab.host}/oauth/authorize
16 | tokenUrl: ${oauth.gitlab.host}/oauth/token
17 | redirectUrl: ${collie.url}/collie/oauth/callback/gitlab
18 | #clientId:
19 | #clientSecret:
20 | google:
21 | authUrl: https://accounts.google.com/o/oauth2/auth
22 | tokenUrl: https://accounts.google.com/o/oauth2/token
23 | redirectUrl: ${collie.url}/collie/oauth/callback/google
24 | #clientId:
25 | #clientSecret:
26 |
--------------------------------------------------------------------------------
/k8s-agent/internal/reporter/collie_client_interface.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package reporter
17 |
18 | import (
19 | "collie-agent/internal/model"
20 | )
21 |
22 | type ICollieClient interface {
23 | Info() error
24 | ReportClusterInfo(info model.ClusterInfo)
25 | ReportResource(data interface{})
26 | ReportActivity(operation string, resource string)
27 | ReportError(operation string, resource string, e error)
28 | ReportCompliance(data *model.Compliance)
29 | ReportBulk(docs []*any)
30 | ReportCompletion()
31 | }
32 |
--------------------------------------------------------------------------------
/deployment/scripts/minikube/deploy-grafana.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | helm list -n collie-server -a | grep grafana > /dev/null 2>&1
4 | if [ $? -eq 0 ]; then
5 | helm uninstall -n collie-server grafana --wait
6 | fi
7 |
8 |
9 | export ES_PASSWORD=$(kubectl get -n collie-server secret/elasticsearch-master-credentials -o jsonpath="{.data.password}" | base64 -d)
10 |
11 | envsubst < ./grafana/values-custom.yaml > ./grafana/tmp.yaml
12 | helm install -n collie-server grafana ./grafana -f ./grafana/values.yaml -f ./grafana/tmp.yaml --wait
13 | rm ./grafana/tmp.yaml
14 |
15 | sleep 10
16 |
17 | source ./kill-processes-by-keyword.sh "kubectl port-forward -n collie-server --address 0.0.0.0 services/grafana"
18 | kubectl port-forward -n collie-server --address 0.0.0.0 services/grafana 3000:3000 &
19 |
20 | sleep 5
21 |
22 | GRAFANA_PWD=$(kubectl get secret --namespace collie-server grafana -o jsonpath="{.data.admin-password}" | base64 --decode)
23 |
24 |
25 | curl -X POST \
26 | -H "Content-Type: application/json" \
27 | -u "admin:$GRAFANA_PWD" \
28 | http://collie-dev.org:3000/api/dashboards/db?orgId=1 \
29 | -d @./grafana/dashboards/workaround-helm.json
30 |
31 |
32 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/clusterrole.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.rbac.create (or (not .Values.rbac.namespaced) .Values.rbac.extraClusterRoleRules) (not .Values.rbac.useExistingRole) }}
2 | kind: ClusterRole
3 | apiVersion: rbac.authorization.k8s.io/v1
4 | metadata:
5 | labels:
6 | {{- include "grafana.labels" . | nindent 4 }}
7 | {{- with .Values.annotations }}
8 | annotations:
9 | {{- toYaml . | nindent 4 }}
10 | {{- end }}
11 | name: {{ include "grafana.fullname" . }}-clusterrole
12 | {{- if or .Values.sidecar.dashboards.enabled .Values.rbac.extraClusterRoleRules .Values.sidecar.datasources.enabled .Values.sidecar.plugins.enabled .Values.sidecar.alerts.enabled }}
13 | rules:
14 | {{- if or .Values.sidecar.dashboards.enabled .Values.sidecar.datasources.enabled .Values.sidecar.plugins.enabled .Values.sidecar.alerts.enabled }}
15 | - apiGroups: [""] # "" indicates the core API group
16 | resources: ["configmaps", "secrets"]
17 | verbs: ["get", "watch", "list"]
18 | {{- end}}
19 | {{- with .Values.rbac.extraClusterRoleRules }}
20 | {{- toYaml . | nindent 2 }}
21 | {{- end}}
22 | {{- else }}
23 | rules: []
24 | {{- end}}
25 | {{- end}}
26 |
--------------------------------------------------------------------------------
/deployment/scripts/azure/deploy-grafana.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | helm list -n collie-server -a | grep grafana > /dev/null 2>&1
4 | if [ $? -eq 0 ]; then
5 | helm uninstall -n collie-server grafana --wait
6 | fi
7 |
8 |
9 | export ES_PASSWORD=$(kubectl get -n collie-server secret/elasticsearch-master-credentials -o jsonpath="{.data.password}" | base64 -d)
10 |
11 | cd ../../helm-charts
12 | envsubst < ./grafana/values-custom.yaml > ./grafana/tmp.yaml
13 | helm install -n collie-server grafana ./grafana -f ./grafana/values.yaml -f ./grafana/tmp.yaml --wait
14 | rm ./grafana/tmp.yaml
15 |
16 | sleep 10
17 |
18 | #source ./kill-processes-by-keyword.sh "kubectl port-forward -n collie-server --address 0.0.0.0 services/grafana"
19 | #kubectl port-forward -n collie-server --address 0.0.0.0 services/grafana 3000:3000 &
20 |
21 | sleep 5
22 |
23 | GRAFANA_PWD=$(kubectl get secret --namespace collie-server grafana -o jsonpath="{.data.admin-password}" | base64 --decode)
24 |
25 |
26 | curl -X POST \
27 | -H "Content-Type: application/json" \
28 | -u "admin:$GRAFANA_PWD" \
29 | http://collie-dev.org:3000/api/dashboards/db?orgId=1 \
30 | -d @./grafana/dashboards/workaround-helm.json
31 |
32 |
33 |
--------------------------------------------------------------------------------
/api-server/service/template/clustervpa-configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: agent-autoscaler
5 | namespace: collie-agent
6 | labels:
7 | app.kubernetes.io/name: agent
8 | app.kubernetes.io/instance: agent
9 | app.kubernetes.io/version: "v1"
10 | app.kubernetes.io/managed-by: collie
11 |
12 | data:
13 | # Increase memory requests/limits by 256Mi for every 20 nodes. round_up(nodes/nodes_per_step)*step
14 | # For example, for 150 nodes: round_up(150/20)*256Mi=2048Mi
15 |
16 | # in case of large nodes, cores-per-step will ensure that we continue to scale the agent
17 | agent-autoscaler: |-
18 | {
19 | "agent": {
20 | "requests": {
21 | "memory": {
22 | "base": "0",
23 | "max": "8Gi",
24 | "step": "256Mi",
25 | "nodesPerStep": 20,
26 | "coresPerStep": 480
27 | }
28 | },
29 | "limits": {
30 | "memory": {
31 | "base": "0",
32 | "max": "8Gi",
33 | "step": "256Mi",
34 | "nodesPerStep": 20,
35 | "coresPerStep": 480
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/image-renderer-service.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.imageRenderer.enabled .Values.imageRenderer.service.enabled }}
2 | apiVersion: v1
3 | kind: Service
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}-image-renderer
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.imageRenderer.labels" . | nindent 4 }}
9 | {{- with .Values.imageRenderer.service.labels }}
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | {{- with .Values.imageRenderer.service.annotations }}
13 | annotations:
14 | {{- toYaml . | nindent 4 }}
15 | {{- end }}
16 | spec:
17 | type: ClusterIP
18 | {{- with .Values.imageRenderer.service.clusterIP }}
19 | clusterIP: {{ . }}
20 | {{- end }}
21 | ports:
22 | - name: {{ .Values.imageRenderer.service.portName }}
23 | port: {{ .Values.imageRenderer.service.port }}
24 | protocol: TCP
25 | targetPort: {{ .Values.imageRenderer.service.targetPort }}
26 | {{- with .Values.imageRenderer.appProtocol }}
27 | appProtocol: {{ . }}
28 | {{- end }}
29 | selector:
30 | {{- include "grafana.imageRenderer.selectorLabels" . | nindent 4 }}
31 | {{- end }}
32 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/pvc.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) (eq .Values.persistence.type "pvc")}}
2 | apiVersion: v1
3 | kind: PersistentVolumeClaim
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | {{- with .Values.persistence.extraPvcLabels }}
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | {{- with .Values.persistence.annotations }}
13 | annotations:
14 | {{- toYaml . | nindent 4 }}
15 | {{- end }}
16 | {{- with .Values.persistence.finalizers }}
17 | finalizers:
18 | {{- toYaml . | nindent 4 }}
19 | {{- end }}
20 | spec:
21 | accessModes:
22 | {{- range .Values.persistence.accessModes }}
23 | - {{ . | quote }}
24 | {{- end }}
25 | resources:
26 | requests:
27 | storage: {{ .Values.persistence.size | quote }}
28 | {{- with .Values.persistence.storageClassName }}
29 | storageClassName: {{ . }}
30 | {{- end }}
31 | {{- with .Values.persistence.selectorLabels }}
32 | selector:
33 | matchLabels:
34 | {{- toYaml . | nindent 6 }}
35 | {{- end }}
36 | {{- end }}
37 |
--------------------------------------------------------------------------------
/deployment/scripts/README.md:
--------------------------------------------------------------------------------
1 | # Tutorial
2 |
3 | ### Onboard new K8S cluster, with Collie.eng.omnissa.com
4 |
5 | Get bootstrap command
6 | ```
7 | curl -skH "Authorization: bc3dcc07f8263dda643d1703cd5254f2" https://collie.eng.omnissa.com/collie/api/v1/onboarding/bootstrap-cmd
8 | ```
9 |
10 | It will generate a command line like below. Make sure your current kubectl points to the taregt context, then execute the command.
11 |
12 | ```
13 | curl -skH "Authorization: Token bc3dcc07f8263dda643d1703cd5254f2" "https://collie.eng.omnissa.com/collie/api/v1/onboarding/agent.yaml?provider=AKS" | kubectl apply -f -
14 | ```
15 |
16 | Navigate to dashboard
17 | ```
18 | https://collie.eng.omnissa.com/d/qIbLYbT4z/k8s-compliance-report?orgId=1
19 | ```
20 |
21 | ### Start local dev server (not using collie.eng.omnissa.com)
22 | ```
23 | cd collie/api-server
24 | cp collie/helm-charts/.env.example .env
25 | vi .env
26 | source .env
27 | go run .
28 | ```
29 |
30 | ### Deploy local cluster
31 | ```
32 | cd collie/helm-charts
33 | cp .env.example .env
34 | vi .env
35 | source .env
36 | envsubst < api-server.yaml | kubectl apply -f -
37 | ```
38 |
39 | ####Onboard agent
40 |
41 | ```
42 | curl http://localhost:8080/api/v1/onboarding/bootstrap-cmd
43 | ```
44 |
--------------------------------------------------------------------------------
/deployment/helm-charts/Makefile:
--------------------------------------------------------------------------------
1 | default: test
2 |
3 | .ONESHELL:
4 |
5 | .PHONY: help
6 | help: ## Display this help
7 | @awk 'BEGIN {FS = ":.*##"; printf "Usage: make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-10s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
8 |
9 | .PHONY: build
10 | build: ## Build helm-tester docker image
11 | cd ../helpers/helm-tester && \
12 | for i in {1..5}; do docker build -t helm-tester . && break || sleep 15; done
13 |
14 | .PHONY: deps
15 | deps: ## Update helm charts dependencies
16 | helm dependency update
17 |
18 | .PHONY: lint
19 | lint: ## Lint helm templates
20 | helm lint --strict ./
21 |
22 | .PHONY: lint-python
23 | lint-python: ## Lint python scripts
24 | black --diff --check --exclude='ve/|venv/' .
25 |
26 | .PHONY: pytest
27 | pytest: ## Run python tests
28 | pytest -sv --color=yes
29 |
30 | .PHONY: template
31 | template: ## Render chart templates
32 | helm template ./
33 |
34 | .PHONY: test
35 | test: ## Run all tests in a docker container
36 | docker run --rm -i --user "$$(id -u):$$(id -g)" -v $$(pwd)/../:/app -w /app/$$(basename $$(pwd)) helm-tester make test-all
37 |
38 | .PHONY: test-all ## Run all tests
39 | test-all: deps lint template pytest
40 |
41 |
42 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: api-server
3 | description: A Helm chart for Kubernetes
4 |
5 | # A chart can be either an 'application' or a 'library' chart.
6 | #
7 | # Application charts are a collection of templates that can be packaged into versioned archives
8 | # to be deployed.
9 | #
10 | # Library charts provide useful utilities or functions for the chart developer. They're included as
11 | # a dependency of application charts to inject those utilities and functions into the rendering
12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed.
13 | type: application
14 |
15 | # This is the chart version. This version number should be incremented each time you make changes
16 | # to the chart and its templates, including the app version.
17 | # Versions are expected to follow Semantic Versioning (https://semver.org/)
18 | version: 0.1.0
19 |
20 | # This is the version number of the application being deployed. This version number should be
21 | # incremented each time you make changes to the application. Versions are not expected to
22 | # follow Semantic Versioning. They should reflect the version the application is using.
23 | # It is recommended to use it with quotes.
24 | appVersion: "1.16.0"
25 |
--------------------------------------------------------------------------------
/dashboard/cert/self-signed-root.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDPDCCAiQCCQDiYx7I1uzREzANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJV
3 | UzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVBhbG8gQWx0bzEPMA0GA1UECgwGVk13
4 | YXJlMQwwCgYDVQQLDANFVUMxETAPBgNVBAMMCG5hbnctZGV2MB4XDTIyMDkxMzIx
5 | MTg1MloXDTMyMDkxMDIxMTg1MlowYDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNB
6 | MRIwEAYDVQQHDAlQYWxvIEFsdG8xDzANBgNVBAoMBlZNd2FyZTEMMAoGA1UECwwD
7 | RVVDMREwDwYDVQQDDAhuYW53LWRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
8 | AQoCggEBANYo30Xy4VtLErRznTlwF6mB5D6M0t5WYqGYPsN9ApPBrOWXxP3JkhYW
9 | LiNHX1wwUSTlyE9pY4jamkQSiKTQr5Op8ahW/cftp1pDJOCwWg1RKxyM29+l3iSt
10 | zMjS3IZxcbwwg+wAqJ9uJ+UT6EWzUCYLhY9E9/kGGcf0kocy0KcPKIX6HKg3q1LQ
11 | HCvFlf/D+Z5uW+m0sFxEqKOTO0asDV0Rs956Gi9/rdlwVnncIdabXEK5lPM92NaM
12 | N4hvfPpBGPthxPF7OvCPNfWm/5hlBRjHS7qVYOZSsZ/D6tWMaFTv9dLBUwPW0c5k
13 | l/Uiy80/63j9+WCQ/rkQzYUexT8xwIECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
14 | yEsppXNxrmX/LzuStWWaNxI2JRScknFQ5pu3Ny/OL9E2pWo32CpgqJH5X8casNv8
15 | +v0gJGoIEfVjXJFB8mCepLuT5ZW2sXCnqPFtovTZNLdb3kdF9Qggf99y5HNZQqJb
16 | bGnP1o+K18MxNTF4GBsV3BM7w4qov6uNPJhIcVP+SjkD/QuQyC0hDNNjhQ1+X8JC
17 | +Ap1zYJhf64VWRk4qAw5+KP1q5pNL0ny2eU2J7a0idRPnHqA0pgtNj9kgGx0e/Gr
18 | ReOAg77lbkqRtDYn3SHfgY6mS58hcs9TKkhxsry6d9HjHiJaHwpPtvDVTAE8xfgK
19 | MATqmYuWokzNsUjG0N37Yw==
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/secret.yaml:
--------------------------------------------------------------------------------
1 | {{- if or (and (not .Values.admin.existingSecret) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION)) (and .Values.ldap.enabled (not .Values.ldap.existingSecret)) }}
2 | apiVersion: v1
3 | kind: Secret
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | {{- with .Values.annotations }}
10 | annotations:
11 | {{- toYaml . | nindent 4 }}
12 | {{- end }}
13 | type: Opaque
14 | data:
15 | {{- if and (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) (not .Values.admin.existingSecret) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD) }}
16 | admin-user: {{ .Values.adminUser | b64enc | quote }}
17 | {{- if .Values.adminPassword }}
18 | admin-password: {{ .Values.adminPassword | b64enc | quote }}
19 | {{- else }}
20 | admin-password: {{ include "grafana.password" . }}
21 | {{- end }}
22 | {{- end }}
23 | {{- if not .Values.ldap.existingSecret }}
24 | ldap-toml: {{ tpl .Values.ldap.config $ | b64enc | quote }}
25 | {{- end }}
26 | {{- end }}
27 |
--------------------------------------------------------------------------------
/dashboard/cert/nanw.csr:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE REQUEST-----
2 | MIIDUTCCAjkCAQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQH
3 | DAlQYWxvIEFsdG8xDzANBgNVBAoMBlZNd2FyZTEMMAoGA1UECwwDRVVDMRwwGgYD
4 | VQQDDBNuYW53LmVuZy52bXdhcmUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
5 | MIIBCgKCAQEArquASj3cEjfEGl5t7lQBAN8ltGyN/k+eox84HFpegvfzHPhnNoNJ
6 | TIptTTtEChmLOh8aWiZFMCgglS9Qf5OPCBmbtu9Ng7YzkKfj73Oio7iPngaS9V3O
7 | mVq1ujp4fB2jOAx5I0WlrZS1hW22l+bziMsTXAT150mx5nyPGHN0i0CCwlqjxJwL
8 | ttf1fqlYvtCorntdSCJK4I9IKCdNLjxHovyrzjLqwKLww0mRb0HuALnhu49EieVK
9 | rBTLr1weEyrsaleQKtnOJkZAuazJJsdt4w9hSbyCJ3extCyT6urIkl0wKMNvpIHx
10 | KCiKrhrDqfucXTTKBJWfT7WdqVTAQdX9JwIDAQABoIGgMIGdBgkqhkiG9w0BCQ4x
11 | gY8wgYwwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUCXCTin5bwu+zegZyBBv1lGsB
12 | bQ4wDgYDVR0PAQH/BAQDAgP4MBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMDUGA1Ud
13 | EQQuMCyCE25hbncuZW5nLnZtd2FyZS5jb22CFWNvbGxpZS5lbmcudm13YXJlLmNv
14 | bTANBgkqhkiG9w0BAQsFAAOCAQEALRqXk3mgXYmhNJTi/XC28neqydwV1aq5C66T
15 | /3iYUYwOUbaAjS8xbLaAv2VSWbRj44NwRuYdVZmKznpetVe7km+mZeQ0jdZl4GhO
16 | r3Q+cA66c6zg/1xQ0/d3pcmIpWIynj2+HCYLVpChpf0EGyVBCBYTD4BBx5lf4zZC
17 | L4QgLmLCDgr9i6W2ERqGC4/+FmlGSN3PCN0OMR7cVuFK56O7Bj++4IeFwzFvHQo3
18 | 4dsJD07zGsgFqq5kKA1PSbcuW7kDfALJUmeDX1UCvIePT5ql6CS99rEHU8Pkctbg
19 | Ytk6YvAgWptP3VP1Ca1NZWdZxfzEzW6ZVZFGrbO93BMwvhXV2Q==
20 | -----END CERTIFICATE REQUEST-----
21 |
--------------------------------------------------------------------------------
/k8s-agent/internal/services/version/version.go:
--------------------------------------------------------------------------------
1 | //go:generate mockgen -destination ./mock/version.go . Interface
2 | package version
3 |
4 | import (
5 | "fmt"
6 | "regexp"
7 | "strconv"
8 |
9 | "github.com/sirupsen/logrus"
10 | "k8s.io/apimachinery/pkg/version"
11 | "k8s.io/client-go/kubernetes"
12 | )
13 |
14 | type Interface interface {
15 | Full() string
16 | MinorInt() int
17 | }
18 |
19 | func Get(log logrus.FieldLogger, clientset kubernetes.Interface) (Interface, error) {
20 | cs, ok := clientset.(*kubernetes.Clientset)
21 | if !ok {
22 | return nil, fmt.Errorf("expected clientset to be of type *kubernetes.Clientset but was %T", clientset)
23 | }
24 |
25 | sv, err := cs.ServerVersion()
26 | if err != nil {
27 | return nil, fmt.Errorf("getting server version: %w", err)
28 | }
29 |
30 | log.Infof("kubernetes version %s.%s", sv.Major, sv.Minor)
31 |
32 | m, err := strconv.Atoi(regexp.MustCompile(`^(\d+)`).FindString(sv.Minor))
33 | if err != nil {
34 | return nil, fmt.Errorf("parsing minor version: %w", err)
35 | }
36 |
37 | return &Version{v: sv, m: m}, nil
38 | }
39 |
40 | type Version struct {
41 | v *version.Info
42 | m int
43 | }
44 |
45 | func (v *Version) Full() string {
46 | return v.v.Major + "." + v.v.Minor
47 | }
48 |
49 | func (v *Version) MinorInt() int {
50 | return v.m
51 | }
52 |
--------------------------------------------------------------------------------
/collie/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/values-custom.yaml:
--------------------------------------------------------------------------------
1 | # values.yaml
2 |
3 | ## Grafana configuration
4 | service:
5 | type: ClusterIP
6 | port: 3000
7 | ingress:
8 | enabled: false
9 | annotations:
10 | kubernetes.io/ingress.class: nginx
11 | hosts:
12 | - collie-dev.org
13 | paths:
14 | - /
15 | adminUser: admin
16 | adminPassword: admin
17 |
18 |
19 | ## Elasticsearch datasource configuration
20 | datasources:
21 | datasources.yaml:
22 | apiVersion: 1
23 | datasources:
24 | - name: es-collie-k8s-elastic
25 | type: elasticsearch
26 | access: proxy
27 | url: https://elasticsearch-master:9200
28 | database: collie-k8s-elastic
29 | basicAuth: true
30 | basicAuthUser: elastic
31 | isDefault: true
32 | readOnly: false
33 | editable: true
34 | jsonData:
35 | esVersion: 8
36 | tlsSkipVerify: true
37 | secureJsonData:
38 | basicAuthPassword: ${ES_PASSWORD}
39 |
40 | grafana.ini:
41 | auth.anonymous:
42 | enabled: true
43 | org_name: "Main Org." # Change it to the name of your default organization
44 | org_role: "Viewer" # The role assigned to anonymous users in the organization
45 |
46 | dashboards:
47 | default:
48 | # some-dashboard:
49 | # json: |
50 | # $RAW_JSON
51 | custom-dashboard:
52 | file: dashboards/custom-dashboard.json
53 |
--------------------------------------------------------------------------------
/api-server/model/bottle.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package model
17 |
18 | // Bottle example
19 | type Bottle struct {
20 | ID int `json:"id" example:"1"`
21 | Name string `json:"name" example:"bottle_name"`
22 | Account Account `json:"account"`
23 | }
24 |
25 | // BottlesAll example
26 | func BottlesAll() ([]Bottle, error) {
27 | return bottles, nil
28 | }
29 |
30 | // BottleOne example
31 | func BottleOne(id int) (*Bottle, error) {
32 | for _, v := range bottles {
33 | if id == v.ID {
34 | return &v, nil
35 | }
36 | }
37 | return nil, ErrNoRow
38 | }
39 |
40 | var bottles = []Bottle{
41 | {ID: 1, Name: "bottle_1", Account: Account{ID: 1, Name: "accout_1"}},
42 | {ID: 2, Name: "bottle_2", Account: Account{ID: 2, Name: "accout_2"}},
43 | {ID: 3, Name: "bottle_3", Account: Account{ID: 3, Name: "accout_3"}},
44 | }
45 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/role.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.rbac.create (not .Values.rbac.useExistingRole) -}}
2 | apiVersion: {{ include "grafana.rbac.apiVersion" . }}
3 | kind: Role
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | {{- with .Values.annotations }}
10 | annotations:
11 | {{- toYaml . | nindent 4 }}
12 | {{- end }}
13 | {{- if or .Values.rbac.pspEnabled (and .Values.rbac.namespaced (or .Values.sidecar.dashboards.enabled .Values.sidecar.datasources.enabled .Values.sidecar.plugins.enabled .Values.rbac.extraRoleRules)) }}
14 | rules:
15 | {{- if and .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }}
16 | - apiGroups: ['extensions']
17 | resources: ['podsecuritypolicies']
18 | verbs: ['use']
19 | resourceNames: [{{ include "grafana.fullname" . }}]
20 | {{- end }}
21 | {{- if and .Values.rbac.namespaced (or .Values.sidecar.dashboards.enabled .Values.sidecar.datasources.enabled .Values.sidecar.plugins.enabled) }}
22 | - apiGroups: [""] # "" indicates the core API group
23 | resources: ["configmaps", "secrets"]
24 | verbs: ["get", "watch", "list"]
25 | {{- end }}
26 | {{- with .Values.rbac.extraRoleRules }}
27 | {{- toYaml . | nindent 2 }}
28 | {{- end}}
29 | {{- else }}
30 | rules: []
31 | {{- end }}
32 | {{- end }}
33 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/configmap-dashboard-provider.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.sidecar.dashboards.enabled .Values.sidecar.dashboards.SCProvider }}
2 | apiVersion: v1
3 | kind: ConfigMap
4 | metadata:
5 | labels:
6 | {{- include "grafana.labels" . | nindent 4 }}
7 | {{- with .Values.annotations }}
8 | annotations:
9 | {{- toYaml . | nindent 4 }}
10 | {{- end }}
11 | name: {{ include "grafana.fullname" . }}-config-dashboards
12 | namespace: {{ include "grafana.namespace" . }}
13 | data:
14 | provider.yaml: |-
15 | apiVersion: 1
16 | providers:
17 | - name: '{{ .Values.sidecar.dashboards.provider.name }}'
18 | orgId: {{ .Values.sidecar.dashboards.provider.orgid }}
19 | {{- if not .Values.sidecar.dashboards.provider.foldersFromFilesStructure }}
20 | folder: '{{ .Values.sidecar.dashboards.provider.folder }}'
21 | {{- end }}
22 | type: {{ .Values.sidecar.dashboards.provider.type }}
23 | disableDeletion: {{ .Values.sidecar.dashboards.provider.disableDelete }}
24 | allowUiUpdates: {{ .Values.sidecar.dashboards.provider.allowUiUpdates }}
25 | updateIntervalSeconds: {{ .Values.sidecar.dashboards.provider.updateIntervalSeconds | default 30 }}
26 | options:
27 | foldersFromFilesStructure: {{ .Values.sidecar.dashboards.provider.foldersFromFilesStructure }}
28 | path: {{ .Values.sidecar.dashboards.folder }}{{- with .Values.sidecar.dashboards.defaultFolderName }}/{{ . }}{{- end }}
29 | {{- end }}
30 |
--------------------------------------------------------------------------------
/collie/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/k8s-agent/internal/model/model.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package model
17 |
18 | type ComplianceRecord struct {
19 | Timestamp string `json:"@timestamp"`
20 | OrgId string `json:"orgId"`
21 | ClusterId string `json:"clusterId"`
22 | RuleId string `json:"ruleId"`
23 | Severity string `json:"severity"`
24 | Url string `json:"url"`
25 | Data map[string]string `json:"data"`
26 | }
27 |
28 | type ClusterInfo struct {
29 | Provider string `json:"provider"`
30 |
31 | Data interface{} `json:"data"`
32 | }
33 |
34 | type Compliance struct {
35 | Plugin string `json:"plugin"`
36 | RuleId string `json:"ruleId"`
37 | Category string `json:"category"`
38 | Subcategory string `json:"subcategory"`
39 | Description string `json:"description"`
40 | Status string `json:"status"` //FAIL, PASS, WARN
41 | Remediation string `json:"remediation"`
42 | }
43 |
--------------------------------------------------------------------------------
/api-server/util/util.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package util
17 |
18 | import (
19 | "crypto/rand"
20 | "encoding/hex"
21 | "encoding/json"
22 | "fmt"
23 | "reflect"
24 | )
25 |
26 | func RandomString(n int) string {
27 | bytes := make([]byte, n)
28 | _, err := rand.Read(bytes)
29 | if err != nil {
30 | panic(fmt.Errorf("Error generating random: %s", err))
31 | }
32 | return hex.EncodeToString(bytes)
33 | }
34 |
35 | func ToJson(data interface{}) string {
36 | b, err := json.MarshalIndent(data, "", " ")
37 | if err != nil {
38 | return "Error: " + err.Error()
39 | }
40 | return string(b)
41 | }
42 |
43 | func DeepCopyMap(from map[string]interface{}) map[string]interface{} {
44 | to := make(map[string]interface{})
45 |
46 | for k, v := range from {
47 | if v != nil && reflect.TypeOf(v).Kind() == reflect.Map {
48 | valueMap := v.(map[string]interface{})
49 | to[k] = DeepCopyMap(valueMap)
50 | } else {
51 | to[k] = v
52 | }
53 | }
54 | return to
55 | }
56 |
--------------------------------------------------------------------------------
/api-server/service/persist/persist.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package persist
17 |
18 | import (
19 | "errors"
20 |
21 | _ "k8s.io/utils/lru"
22 | )
23 |
24 | var (
25 | // TODO: change to persist store
26 | collections map[string]Store
27 | )
28 |
29 | func init() {
30 | collections = map[string]Store{}
31 | }
32 |
33 | type Store interface {
34 | Put(id string, data interface{})
35 | Get(id string) (interface{}, error)
36 | }
37 |
38 | type storeImpl struct {
39 | data map[string]interface{}
40 | }
41 |
42 | func Collection(name string) Store {
43 | s, ok := collections[name]
44 | if !ok {
45 | s = &storeImpl{data: map[string]interface{}{}}
46 | collections[name] = s
47 | }
48 | return s
49 | }
50 |
51 | func (s *storeImpl) Put(id string, data interface{}) {
52 | s.data[id] = data
53 | }
54 |
55 | func (s *storeImpl) Get(id string) (interface{}, error) {
56 | v, exist := s.data[id]
57 | if exist {
58 | return v, nil
59 | }
60 | return nil, errors.New("Item not found: " + id)
61 | }
62 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/podsecuritypolicy.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.rbac.pspEnabled (.Capabilities.APIVersions.Has "policy/v1beta1/PodSecurityPolicy") }}
2 | apiVersion: policy/v1beta1
3 | kind: PodSecurityPolicy
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}
6 | labels:
7 | {{- include "grafana.labels" . | nindent 4 }}
8 | annotations:
9 | seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
10 | seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default'
11 | {{- if .Values.rbac.pspUseAppArmor }}
12 | apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
13 | apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'
14 | {{- end }}
15 | spec:
16 | privileged: false
17 | allowPrivilegeEscalation: false
18 | requiredDropCapabilities:
19 | # Default set from Docker, with DAC_OVERRIDE and CHOWN
20 | - ALL
21 | volumes:
22 | - 'configMap'
23 | - 'emptyDir'
24 | - 'projected'
25 | - 'csi'
26 | - 'secret'
27 | - 'downwardAPI'
28 | - 'persistentVolumeClaim'
29 | hostNetwork: false
30 | hostIPC: false
31 | hostPID: false
32 | runAsUser:
33 | rule: 'RunAsAny'
34 | seLinux:
35 | rule: 'RunAsAny'
36 | supplementalGroups:
37 | rule: 'MustRunAs'
38 | ranges:
39 | # Forbid adding the root group.
40 | - min: 1
41 | max: 65535
42 | fsGroup:
43 | rule: 'MustRunAs'
44 | ranges:
45 | # Forbid adding the root group.
46 | - min: 1
47 | max: 65535
48 | readOnlyRootFilesystem: false
49 | {{- end }}
50 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/servicemonitor.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceMonitor.enabled }}
2 | ---
3 | apiVersion: monitoring.coreos.com/v1
4 | kind: ServiceMonitor
5 | metadata:
6 | name: {{ include "grafana.fullname" . }}
7 | {{- if .Values.serviceMonitor.namespace }}
8 | namespace: {{ tpl .Values.serviceMonitor.namespace . }}
9 | {{- else }}
10 | namespace: {{ include "grafana.namespace" . }}
11 | {{- end }}
12 | labels:
13 | {{- include "grafana.labels" . | nindent 4 }}
14 | {{- with .Values.serviceMonitor.labels }}
15 | {{- toYaml . | nindent 4 }}
16 | {{- end }}
17 | spec:
18 | endpoints:
19 | - port: {{ .Values.service.portName }}
20 | {{- with .Values.serviceMonitor.interval }}
21 | interval: {{ . }}
22 | {{- end }}
23 | {{- with .Values.serviceMonitor.scrapeTimeout }}
24 | scrapeTimeout: {{ . }}
25 | {{- end }}
26 | honorLabels: true
27 | path: {{ .Values.serviceMonitor.path }}
28 | scheme: {{ .Values.serviceMonitor.scheme }}
29 | {{- with .Values.serviceMonitor.tlsConfig }}
30 | tlsConfig:
31 | {{- toYaml . | nindent 6 }}
32 | {{- end }}
33 | {{- with .Values.serviceMonitor.relabelings }}
34 | relabelings:
35 | {{- toYaml . | nindent 6 }}
36 | {{- end }}
37 | jobLabel: "{{ .Release.Name }}"
38 | selector:
39 | matchLabels:
40 | {{- include "grafana.selectorLabels" . | nindent 6 }}
41 | namespaceSelector:
42 | matchNames:
43 | - {{ include "grafana.namespace" . }}
44 | {{- with .Values.serviceMonitor.targetLabels }}
45 | targetLabels:
46 | {{- toYaml . | nindent 4 }}
47 | {{- end }}
48 | {{- end }}
49 |
--------------------------------------------------------------------------------
/api-server/service/org/org.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package org
17 |
18 | import (
19 | "collie-api-server/config"
20 | "collie-api-server/service/persist"
21 | )
22 |
23 | type OrgInfo struct {
24 | OrgId string
25 | EsKey string
26 | GrafanaOrgId string
27 | }
28 |
29 | var (
30 | orgColl persist.Store
31 | )
32 |
33 | func init() {
34 | orgColl = persist.Collection("org")
35 | }
36 |
37 | func Get(orgId string) (*OrgInfo, error) {
38 | ret, err := orgColl.Get(orgId)
39 | if err != nil {
40 | return nil, err
41 | }
42 | return ret.(*OrgInfo), nil
43 | }
44 |
45 | func EnsureOnboard(orgId string) (*OrgInfo, error) {
46 | orgInfo, err := Get(orgId)
47 | if err != nil {
48 | orgInfo = &OrgInfo{}
49 | orgInfo.EsKey = createEsTenant(orgId)
50 | orgInfo.GrafanaOrgId = createGrafanaTenant(orgId)
51 | orgColl.Put(orgId, orgInfo)
52 | }
53 |
54 | return orgInfo, nil
55 | }
56 |
57 | func createEsTenant(orgId string) string {
58 | return config.Get().EsKey
59 | }
60 |
61 | func createGrafanaTenant(orgId string) string {
62 | return "1"
63 | }
64 |
--------------------------------------------------------------------------------
/deployment/scripts/minikube/deploy-es.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | helm list -n collie-server -a | grep es > /dev/null 2>&1
4 | if [ $? -eq 0 ]; then
5 | helm uninstall -n collie-server es --wait
6 | fi
7 |
8 | echo Deploy ES...
9 | helm install -n collie-server es elastic/elasticsearch -f ./es/values.yaml -f ./es/values-custom.yaml --wait
10 |
11 | sleep 10
12 |
13 | export ES_PASSWORD=$(kubectl get -n collie-server secret/elasticsearch-master-credentials -o jsonpath="{.data.password}" | base64 -d)
14 |
15 | ES_USER=$(kubectl get -n collie-server secret/elasticsearch-master-credentials -o jsonpath="{.data.username}" | base64 -d)
16 |
17 | ES_AUTH=$ES_USER:$ES_PASSWORD
18 | ES_URL=https://collie-dev.org:9200
19 |
20 | echo ES_AUTH: $ES_AUTH
21 | Echo Forwarding ES in background...
22 |
23 | source ./kill-processes-by-keyword.sh "kubectl port-forward -n collie-server --address 0.0.0.0 services/elasticsearch-master"
24 | kubectl port-forward -n collie-server --address 0.0.0.0 services/elasticsearch-master 9200:9200 &
25 |
26 | sleep 5
27 |
28 | Echo Clean up ES data...
29 | curl -k -u $ES_AUTH -X DELETE $ES_URL/collie-k8s-elastic
30 | Echo Recreate ES index...
31 | curl -k -u $ES_AUTH -X PUT -H "Content-Type: application/json" -d '
32 | {
33 | "mappings": {
34 | "properties": {
35 | "resource": {
36 | "dynamic": false,
37 | "properties": {
38 | "metadata": {
39 | "properties": {
40 | "name": {
41 | "type": "text"
42 | },
43 | "namespace": {
44 | "type": "text"
45 | }
46 | }
47 | }
48 | }
49 | }
50 | }
51 | }
52 | }' $ES_URL/collie-k8s-elastic
53 |
54 | echo
55 |
56 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/networkpolicy.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.networkPolicy.enabled }}
2 | apiVersion: networking.k8s.io/v1
3 | kind: NetworkPolicy
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | {{- with .Values.labels }}
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | {{- with .Values.annotations }}
13 | annotations:
14 | {{- toYaml . | nindent 4 }}
15 | {{- end }}
16 | spec:
17 | policyTypes:
18 | {{- if .Values.networkPolicy.ingress }}
19 | - Ingress
20 | {{- end }}
21 | {{- if .Values.networkPolicy.egress.enabled }}
22 | - Egress
23 | {{- end }}
24 | podSelector:
25 | matchLabels:
26 | {{- include "grafana.selectorLabels" . | nindent 6 }}
27 |
28 | {{- if .Values.networkPolicy.egress.enabled }}
29 | egress:
30 | - ports:
31 | {{ .Values.networkPolicy.egress.ports | toJson }}
32 | {{- end }}
33 | {{- if .Values.networkPolicy.ingress }}
34 | ingress:
35 | - ports:
36 | - port: {{ .Values.service.targetPort }}
37 | {{- if not .Values.networkPolicy.allowExternal }}
38 | from:
39 | - podSelector:
40 | matchLabels:
41 | {{ include "grafana.fullname" . }}-client: "true"
42 | {{- with .Values.networkPolicy.explicitNamespacesSelector }}
43 | - namespaceSelector:
44 | {{- toYaml . | nindent 12 }}
45 | {{- end }}
46 | - podSelector:
47 | matchLabels:
48 | {{- include "grafana.labels" . | nindent 14 }}
49 | role: read
50 | {{- end }}
51 | {{- end }}
52 | {{- end }}
53 |
--------------------------------------------------------------------------------
/deployment/scripts/azure/deploy-es.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | helm list -n collie-server -a | grep es > /dev/null 2>&1
4 | if [ $? -eq 0 ]; then
5 | helm uninstall -n collie-server es --wait
6 | fi
7 |
8 | echo Deploy ES...
9 | cd ../../helm-charts
10 | helm install -n collie-server es elastic/elasticsearch -f ./es/values.yaml -f ./es/values-custom.yaml --wait
11 |
12 | sleep 10
13 |
14 | export ES_PASSWORD=$(kubectl get -n collie-server secret/elasticsearch-master-credentials -o jsonpath="{.data.password}" | base64 -d)
15 |
16 | ES_USER=$(kubectl get -n collie-server secret/elasticsearch-master-credentials -o jsonpath="{.data.username}" | base64 -d)
17 |
18 | ES_AUTH=$ES_USER:$ES_PASSWORD
19 | ES_URL=https://collie-dev.org:9200
20 |
21 | echo ES_AUTH: $ES_AUTH
22 | Echo Forwarding ES in background...
23 |
24 | source ./kill-processes-by-keyword.sh "kubectl port-forward -n collie-server --address 0.0.0.0 services/elasticsearch-master"
25 | kubectl port-forward -n collie-server --address 0.0.0.0 services/elasticsearch-master 9200:9200 &
26 |
27 | sleep 5
28 |
29 | Echo Clean up ES data...
30 | curl -k -u $ES_AUTH -X DELETE $ES_URL/collie-k8s-elastic
31 | Echo Recreate ES index...
32 | curl -k -u $ES_AUTH -X PUT -H "Content-Type: application/json" -d '
33 | {
34 | "mappings": {
35 | "properties": {
36 | "resource": {
37 | "dynamic": false,
38 | "properties": {
39 | "metadata": {
40 | "properties": {
41 | "name": {
42 | "type": "text"
43 | },
44 | "namespace": {
45 | "type": "text"
46 | }
47 | }
48 | }
49 | }
50 | }
51 | }
52 | }
53 | }' $ES_URL/collie-k8s-elastic
54 |
55 | echo
56 |
57 |
--------------------------------------------------------------------------------
/deployment/scripts/portal.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: portal
5 | namespace: collie-server
6 | labels:
7 | app.kubernetes.io/name: portal
8 | app.kubernetes.io/instance: portal
9 | app.kubernetes.io/version: "v1"
10 | app.kubernetes.io/managed-by: collie
11 |
12 | spec:
13 | replicas: 1
14 | selector:
15 | matchLabels:
16 | app.kubernetes.io/name: portal
17 | app.kubernetes.io/instance: portal
18 | template:
19 | metadata:
20 | labels:
21 | app.kubernetes.io/name: portal
22 | app.kubernetes.io/instance: portal
23 | spec:
24 | containers:
25 | - name: portal
26 | image: collie.azurecr.io/collie-portal:1
27 | imagePullPolicy: IfNotPresent
28 | ports:
29 | - containerPort: 8080
30 | name: http
31 | - containerPort: 9876
32 | name: healthz
33 | readinessProbe:
34 | httpGet:
35 | port: healthz
36 | livenessProbe:
37 | httpGet:
38 | port: healthz
39 | ---
40 | apiVersion: v1
41 | kind: Service
42 | metadata:
43 | labels:
44 | app.kubernetes.io/instance: portal
45 | app.kubernetes.io/managed-by: collie
46 | app.kubernetes.io/name: portal
47 | app.kubernetes.io/version: v1
48 | name: portal
49 | namespace: collie-server
50 | spec:
51 | internalTrafficPolicy: Cluster
52 | ipFamilies:
53 | - IPv4
54 | ipFamilyPolicy: SingleStack
55 | ports:
56 | - name: http
57 | port: 8080
58 | protocol: TCP
59 | targetPort: 8080
60 | selector:
61 | app.kubernetes.io/instance: portal
62 | app.kubernetes.io/name: portal
63 | sessionAffinity: None
64 | type: ClusterIP
65 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/tests/test.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.testFramework.enabled }}
2 | {{- $root := . }}
3 | apiVersion: v1
4 | kind: Pod
5 | metadata:
6 | name: {{ include "grafana.fullname" . }}-test
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | annotations:
10 | "helm.sh/hook": test-success
11 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
12 | namespace: {{ include "grafana.namespace" . }}
13 | spec:
14 | serviceAccountName: {{ include "grafana.serviceAccountNameTest" . }}
15 | {{- with .Values.testFramework.securityContext }}
16 | securityContext:
17 | {{- toYaml . | nindent 4 }}
18 | {{- end }}
19 | {{- if or .Values.image.pullSecrets .Values.global.imagePullSecrets }}
20 | imagePullSecrets:
21 | {{- include "grafana.imagePullSecrets" (dict "root" $root "imagePullSecrets" .Values.image.pullSecrets) | nindent 4 }}
22 | {{- end }}
23 | {{- with .Values.nodeSelector }}
24 | nodeSelector:
25 | {{- toYaml . | nindent 4 }}
26 | {{- end }}
27 | {{- with .Values.affinity }}
28 | affinity:
29 | {{- tpl (toYaml .) $root | nindent 4 }}
30 | {{- end }}
31 | {{- with .Values.tolerations }}
32 | tolerations:
33 | {{- toYaml . | nindent 4 }}
34 | {{- end }}
35 | containers:
36 | - name: {{ .Release.Name }}-test
37 | image: "{{ .Values.testFramework.image}}:{{ .Values.testFramework.tag }}"
38 | imagePullPolicy: "{{ .Values.testFramework.imagePullPolicy}}"
39 | command: ["/opt/bats/bin/bats", "-t", "/tests/run.sh"]
40 | volumeMounts:
41 | - mountPath: /tests
42 | name: tests
43 | readOnly: true
44 | volumes:
45 | - name: tests
46 | configMap:
47 | name: {{ include "grafana.fullname" . }}-test
48 | restartPolicy: Never
49 | {{- end }}
50 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | 1. Get the application URL by running these commands:
2 | {{- if .Values.ingress.enabled }}
3 | {{- range $host := .Values.ingress.hosts }}
4 | {{- range .paths }}
5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
6 | {{- end }}
7 | {{- end }}
8 | {{- else if contains "NodePort" .Values.service.type }}
9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "..fullname" . }})
10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
11 | echo http://$NODE_IP:$NODE_PORT
12 | {{- else if contains "LoadBalancer" .Values.service.type }}
13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available.
14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "..fullname" . }}'
15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "..fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
16 | echo http://$SERVICE_IP:{{ .Values.service.port }}
17 | {{- else if contains "ClusterIP" .Values.service.type }}
18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "..name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19 | export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
20 | echo "Visit http://127.0.0.1:8080 to use your application"
21 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
22 | {{- end }}
23 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/image-renderer-servicemonitor.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.imageRenderer.serviceMonitor.enabled }}
2 | ---
3 | apiVersion: monitoring.coreos.com/v1
4 | kind: ServiceMonitor
5 | metadata:
6 | name: {{ include "grafana.fullname" . }}-image-renderer
7 | {{- if .Values.imageRenderer.serviceMonitor.namespace }}
8 | namespace: {{ tpl .Values.imageRenderer.serviceMonitor.namespace . }}
9 | {{- else }}
10 | namespace: {{ include "grafana.namespace" . }}
11 | {{- end }}
12 | labels:
13 | {{- include "grafana.imageRenderer.labels" . | nindent 4 }}
14 | {{- with .Values.imageRenderer.serviceMonitor.labels }}
15 | {{- toYaml . | nindent 4 }}
16 | {{- end }}
17 | spec:
18 | endpoints:
19 | - port: {{ .Values.imageRenderer.service.portName }}
20 | {{- with .Values.imageRenderer.serviceMonitor.interval }}
21 | interval: {{ . }}
22 | {{- end }}
23 | {{- with .Values.imageRenderer.serviceMonitor.scrapeTimeout }}
24 | scrapeTimeout: {{ . }}
25 | {{- end }}
26 | honorLabels: true
27 | path: {{ .Values.imageRenderer.serviceMonitor.path }}
28 | scheme: {{ .Values.imageRenderer.serviceMonitor.scheme }}
29 | {{- with .Values.imageRenderer.serviceMonitor.tlsConfig }}
30 | tlsConfig:
31 | {{- toYaml . | nindent 6 }}
32 | {{- end }}
33 | {{- with .Values.imageRenderer.serviceMonitor.relabelings }}
34 | relabelings:
35 | {{- toYaml . | nindent 6 }}
36 | {{- end }}
37 | jobLabel: "{{ .Release.Name }}-image-renderer"
38 | selector:
39 | matchLabels:
40 | {{- include "grafana.imageRenderer.selectorLabels" . | nindent 6 }}
41 | namespaceSelector:
42 | matchNames:
43 | - {{ include "grafana.namespace" . }}
44 | {{- with .Values.imageRenderer.serviceMonitor.targetLabels }}
45 | targetLabels:
46 | {{- toYaml . | nindent 4 }}
47 | {{- end }}
48 | {{- end }}
49 |
--------------------------------------------------------------------------------
/api-server/controller/report.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package controller
17 |
18 | import (
19 | "github.com/gin-gonic/gin"
20 | "log"
21 | "net/http"
22 | )
23 |
24 | // SyncStart godoc
25 | //
26 | // @Summary Indicating a sync has been started
27 | // @Description Indicating a sync has been started
28 | // @Tags agent
29 | // @Accept json
30 | // @Produce json
31 | // @Success 200 {object} string
32 | // @Failure 400 {object} httputil.HTTPError
33 | // @Failure 404 {object} httputil.HTTPError
34 | // @Failure 500 {object} httputil.HTTPError
35 | // @Router /agent/sync-start [post]
36 | func (c *Controller) SyncStart(ctx *gin.Context) {
37 | log.Printf("SyncStart")
38 | ctx.String(http.StatusOK, "")
39 | }
40 |
41 | // PostDiscoveryComplete godoc
42 | //
43 | // @Summary Indicating a sync has complete
44 | // @Description Indicating a sync has complete
45 | // @Tags agent
46 | // @Accept json
47 | // @Produce json
48 | // @Success 200 {object} string
49 | // @Failure 400 {object} httputil.HTTPError
50 | // @Failure 404 {object} httputil.HTTPError
51 | // @Failure 500 {object} httputil.HTTPError
52 | // @Router /agent/sync-complete [post]
53 | func (c *Controller) SyncComplete(ctx *gin.Context) {
54 | log.Printf("SyncComplete")
55 | ctx.String(http.StatusOK, "")
56 | }
57 |
--------------------------------------------------------------------------------
/api-server/service/auth/authInfo.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package auth
17 |
18 | import (
19 | "collie-api-server/util"
20 | "reflect"
21 | )
22 |
23 | type AuthInfo interface {
24 | OrgId() string
25 | Username() string
26 | Get(name string) string
27 | GetAny(name string) interface{}
28 | Set(name string, value interface{})
29 | AsMap() map[string]interface{}
30 | }
31 |
32 | type defaultAuthInfo struct {
33 | data map[string]interface{}
34 | }
35 |
36 | func FromMap(data map[string]interface{}) AuthInfo {
37 | return defaultAuthInfo{data: util.DeepCopyMap(data)}
38 | }
39 |
40 | func (t defaultAuthInfo) OrgId() string {
41 | return "elastic"
42 | //return t.Get("orgId")
43 | }
44 |
45 | func (t defaultAuthInfo) Username() string {
46 | return t.Get("username")
47 | }
48 |
49 | func (t defaultAuthInfo) Get(name string) string {
50 | v, ok := t.data[name]
51 | if !ok {
52 | return ""
53 | }
54 | if reflect.TypeOf(v) == reflect.TypeOf("") {
55 | return v.(string)
56 | }
57 | return ""
58 | }
59 |
60 | func (t defaultAuthInfo) GetAny(name string) interface{} {
61 | return t.data[name]
62 | }
63 |
64 | func (t defaultAuthInfo) Set(name string, value interface{}) {
65 | t.data[name] = value
66 | }
67 |
68 | func (t defaultAuthInfo) AsMap() map[string]interface{} {
69 | return util.DeepCopyMap(t.data)
70 | }
71 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.service.enabled }}
2 | {{- $root := . }}
3 | apiVersion: v1
4 | kind: Service
5 | metadata:
6 | name: {{ include "grafana.fullname" . }}
7 | namespace: {{ include "grafana.namespace" . }}
8 | labels:
9 | {{- include "grafana.labels" . | nindent 4 }}
10 | {{- with .Values.service.labels }}
11 | {{- toYaml . | nindent 4 }}
12 | {{- end }}
13 | {{- with .Values.service.annotations }}
14 | annotations:
15 | {{- tpl (toYaml . | nindent 4) $root }}
16 | {{- end }}
17 | spec:
18 | {{- if (or (eq .Values.service.type "ClusterIP") (empty .Values.service.type)) }}
19 | type: ClusterIP
20 | {{- with .Values.service.clusterIP }}
21 | clusterIP: {{ . }}
22 | {{- end }}
23 | {{- else if eq .Values.service.type "LoadBalancer" }}
24 | type: {{ .Values.service.type }}
25 | {{- with .Values.service.loadBalancerIP }}
26 | loadBalancerIP: {{ . }}
27 | {{- end }}
28 | {{- with .Values.service.loadBalancerSourceRanges }}
29 | loadBalancerSourceRanges:
30 | {{- toYaml . | nindent 4 }}
31 | {{- end }}
32 | {{- else }}
33 | type: {{ .Values.service.type }}
34 | {{- end }}
35 | {{- with .Values.service.externalIPs }}
36 | externalIPs:
37 | {{- toYaml . | nindent 4 }}
38 | {{- end }}
39 | ports:
40 | - name: {{ .Values.service.portName }}
41 | port: {{ .Values.service.port }}
42 | protocol: TCP
43 | targetPort: {{ .Values.service.targetPort }}
44 | {{- with .Values.service.appProtocol }}
45 | appProtocol: {{ . }}
46 | {{- end }}
47 | {{- if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }}
48 | nodePort: {{ .Values.service.nodePort }}
49 | {{- end }}
50 | {{- with .Values.extraExposePorts }}
51 | {{- tpl (toYaml . | nindent 4) $root }}
52 | {{- end }}
53 | selector:
54 | {{- include "grafana.selectorLabels" . | nindent 4 }}
55 | {{- end }}
56 |
--------------------------------------------------------------------------------
/api-server/service/template/cluster-role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: agent
5 | labels:
6 | app.kubernetes.io/name: agent
7 | app.kubernetes.io/instance: agent
8 | app.kubernetes.io/version: "v1"
9 | app.kubernetes.io/managed-by: collie
10 |
11 | rules:
12 | # ---
13 | # Required for cost savings estimation features.
14 | # ---
15 | - apiGroups:
16 | - ""
17 | resources:
18 | - pods
19 | - nodes
20 | - replicationcontrollers
21 | - persistentvolumeclaims
22 | - persistentvolumes
23 | - services
24 | - namespaces
25 | - events
26 | verbs:
27 | - get
28 | - list
29 | - watch
30 | - apiGroups:
31 | - ""
32 | resources:
33 | - namespaces
34 | verbs:
35 | - get
36 | - apiGroups:
37 | - "apps"
38 | resources:
39 | - deployments
40 | - replicasets
41 | - daemonsets
42 | - statefulsets
43 | verbs:
44 | - get
45 | - list
46 | - watch
47 | - apiGroups:
48 | - "storage.k8s.io"
49 | resources:
50 | - storageclasses
51 | - csinodes
52 | verbs:
53 | - get
54 | - list
55 | - watch
56 | - apiGroups:
57 | - "batch"
58 | resources:
59 | - jobs
60 | verbs:
61 | - get
62 | - list
63 | - watch
64 | - apiGroups:
65 | - "autoscaling"
66 | resources:
67 | - horizontalpodautoscalers
68 | verbs:
69 | - get
70 | - list
71 | - watch
72 | - apiGroups:
73 | - "coordination.k8s.io"
74 | resources:
75 | - leases
76 | verbs:
77 | - create
78 | - get
79 | - list
80 | - watch
81 | - update
82 | - apiGroups:
83 | - "metrics.k8s.io"
84 | resources:
85 | - pods
86 | verbs:
87 | - get
88 | - list
89 | - nonResourceURLs:
90 | - "/version"
91 | verbs:
92 | - "get"
--------------------------------------------------------------------------------
/deployment/k8s/azure/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "resourceName": {
6 | "value": "collie"
7 | },
8 | "location": {
9 | "value": "westus2"
10 | },
11 | "dnsPrefix": {
12 | "value": "collie-dns"
13 | },
14 | "kubernetesVersion": {
15 | "value": "1.25.6"
16 | },
17 | "networkPlugin": {
18 | "value": "azure"
19 | },
20 | "enableRBAC": {
21 | "value": true
22 | },
23 | "nodeResourceGroup": {
24 | "value": "MC_collie_collie_westus2"
25 | },
26 | "upgradeChannel": {
27 | "value": "patch"
28 | },
29 | "adminGroupObjectIDs": {
30 | "value": []
31 | },
32 | "disableLocalAccounts": {
33 | "value": false
34 | },
35 | "azureRbac": {
36 | "value": false
37 | },
38 | "enablePrivateCluster": {
39 | "value": false
40 | },
41 | "enableAzurePolicy": {
42 | "value": false
43 | },
44 | "enableSecretStoreCSIDriver": {
45 | "value": false
46 | },
47 | "vmssNodePool": {
48 | "value": true
49 | },
50 | "vnetSubnetID": {
51 | "value": "/subscriptions/a201beee-0dd8-4336-b889-89b223a1a393/resourceGroups/collie/providers/Microsoft.Network/virtualNetworks/collie-vnet/subnets/default"
52 | },
53 | "serviceCidr": {
54 | "value": "10.0.0.0/16"
55 | },
56 | "dnsServiceIP": {
57 | "value": "10.0.0.10"
58 | },
59 | "acrName": {
60 | "value": "collie"
61 | },
62 | "acrResourceGroup": {
63 | "value": "nanw"
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | Expand the name of the chart.
3 | */}}
4 | {{- define "..name" -}}
5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6 | {{- end }}
7 |
8 | {{/*
9 | Create a default fully qualified app name.
10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11 | If release name contains chart name it will be used as a full name.
12 | */}}
13 | {{- define "..fullname" -}}
14 | {{- if .Values.fullnameOverride }}
15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16 | {{- else }}
17 | {{- $name := default .Chart.Name .Values.nameOverride }}
18 | {{- if contains $name .Release.Name }}
19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }}
20 | {{- else }}
21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22 | {{- end }}
23 | {{- end }}
24 | {{- end }}
25 |
26 | {{/*
27 | Create chart name and version as used by the chart label.
28 | */}}
29 | {{- define "..chart" -}}
30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31 | {{- end }}
32 |
33 | {{/*
34 | Common labels
35 | */}}
36 | {{- define "..labels" -}}
37 | helm.sh/chart: {{ include "..chart" . }}
38 | {{ include "..selectorLabels" . }}
39 | {{- if .Chart.AppVersion }}
40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41 | {{- end }}
42 | app.kubernetes.io/managed-by: {{ .Release.Service }}
43 | {{- end }}
44 |
45 | {{/*
46 | Selector labels
47 | */}}
48 | {{- define "..selectorLabels" -}}
49 | app.kubernetes.io/name: {{ include "..name" . }}
50 | app.kubernetes.io/instance: {{ .Release.Name }}
51 | {{- end }}
52 |
53 | {{/*
54 | Create the name of the service account to use
55 | */}}
56 | {{- define "..serviceAccountName" -}}
57 | {{- if .Values.serviceAccount.create }}
58 | {{- default (include "..fullname" .) .Values.serviceAccount.name }}
59 | {{- else }}
60 | {{- default "default" .Values.serviceAccount.name }}
61 | {{- end }}
62 | {{- end }}
63 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/hpa.yaml:
--------------------------------------------------------------------------------
1 | {{- $sts := list "sts" "StatefulSet" "statefulset" -}}
2 | {{- if .Values.autoscaling.enabled }}
3 | apiVersion: {{ include "grafana.hpa.apiVersion" . }}
4 | kind: HorizontalPodAutoscaler
5 | metadata:
6 | name: {{ include "grafana.fullname" . }}
7 | namespace: {{ include "grafana.namespace" . }}
8 | labels:
9 | app.kubernetes.io/name: {{ include "grafana.name" . }}
10 | helm.sh/chart: {{ include "grafana.chart" . }}
11 | app.kubernetes.io/managed-by: {{ .Release.Service }}
12 | app.kubernetes.io/instance: {{ .Release.Name }}
13 | spec:
14 | scaleTargetRef:
15 | apiVersion: apps/v1
16 | {{- if has .Values.persistence.type $sts }}
17 | kind: StatefulSet
18 | {{- else }}
19 | kind: Deployment
20 | {{- end }}
21 | name: {{ include "grafana.fullname" . }}
22 | minReplicas: {{ .Values.autoscaling.minReplicas }}
23 | maxReplicas: {{ .Values.autoscaling.maxReplicas }}
24 | metrics:
25 | {{- if .Values.autoscaling.targetMemory }}
26 | - type: Resource
27 | resource:
28 | name: memory
29 | {{- if eq (include "grafana.hpa.apiVersion" .) "autoscaling/v2beta1" }}
30 | targetAverageUtilization: {{ .Values.autoscaling.targetMemory }}
31 | {{- else }}
32 | target:
33 | type: Utilization
34 | averageUtilization: {{ .Values.autoscaling.targetMemory }}
35 | {{- end }}
36 | {{- end }}
37 | {{- if .Values.autoscaling.targetCPU }}
38 | - type: Resource
39 | resource:
40 | name: cpu
41 | {{- if eq (include "grafana.hpa.apiVersion" .) "autoscaling/v2beta1" }}
42 | targetAverageUtilization: {{ .Values.autoscaling.targetCPU }}
43 | {{- else }}
44 | target:
45 | type: Utilization
46 | averageUtilization: {{ .Values.autoscaling.targetCPU }}
47 | {{- end }}
48 | {{- end }}
49 | {{- if .Values.autoscaling.behavior }}
50 | behavior: {{ toYaml .Values.autoscaling.behavior | nindent 4 }}
51 | {{- end }}
52 | {{- end }}
53 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/image-renderer-hpa.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.imageRenderer.enabled .Values.imageRenderer.autoscaling.enabled }}
2 | apiVersion: {{ include "grafana.hpa.apiVersion" . }}
3 | kind: HorizontalPodAutoscaler
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}-image-renderer
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | app.kubernetes.io/name: {{ include "grafana.name" . }}-image-renderer
9 | helm.sh/chart: {{ include "grafana.chart" . }}
10 | app.kubernetes.io/managed-by: {{ .Release.Service }}
11 | app.kubernetes.io/instance: {{ .Release.Name }}
12 | spec:
13 | scaleTargetRef:
14 | apiVersion: apps/v1
15 | kind: Deployment
16 | name: {{ include "grafana.fullname" . }}-image-renderer
17 | minReplicas: {{ .Values.imageRenderer.autoscaling.minReplicas }}
18 | maxReplicas: {{ .Values.imageRenderer.autoscaling.maxReplicas }}
19 | metrics:
20 | {{- if .Values.imageRenderer.autoscaling.targetMemory }}
21 | - type: Resource
22 | resource:
23 | name: memory
24 | {{- if eq (include "grafana.hpa.apiVersion" .) "autoscaling/v2beta1" }}
25 | targetAverageUtilization: {{ .Values.imageRenderer.autoscaling.targetMemory }}
26 | {{- else }}
27 | target:
28 | type: Utilization
29 | averageUtilization: {{ .Values.imageRenderer.autoscaling.targetMemory }}
30 | {{- end }}
31 | {{- end }}
32 | {{- if .Values.imageRenderer.autoscaling.targetCPU }}
33 | - type: Resource
34 | resource:
35 | name: cpu
36 | {{- if eq (include "grafana.hpa.apiVersion" .) "autoscaling/v2beta1" }}
37 | targetAverageUtilization: {{ .Values.imageRenderer.autoscaling.targetCPU }}
38 | {{- else }}
39 | target:
40 | type: Utilization
41 | averageUtilization: {{ .Values.imageRenderer.autoscaling.targetCPU }}
42 | {{- end }}
43 | {{- end }}
44 | {{- if .Values.imageRenderer.autoscaling.behavior }}
45 | behavior: {{ toYaml .Values.imageRenderer.autoscaling.behavior | nindent 4 }}
46 | {{- end }}
47 | {{- end }}
48 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.ingress.enabled -}}
2 | {{- $fullName := include "..fullname" . -}}
3 | {{- $svcPort := .Values.service.port -}}
4 | {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
5 | {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
6 | {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
7 | {{- end }}
8 | {{- end }}
9 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
10 | apiVersion: networking.k8s.io/v1
11 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
12 | apiVersion: networking.k8s.io/v1beta1
13 | {{- else -}}
14 | apiVersion: extensions/v1beta1
15 | {{- end }}
16 | kind: Ingress
17 | metadata:
18 | name: {{ $fullName }}
19 | labels:
20 | {{- include "..labels" . | nindent 4 }}
21 | {{- with .Values.ingress.annotations }}
22 | annotations:
23 | {{- toYaml . | nindent 4 }}
24 | {{- end }}
25 | spec:
26 | {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
27 | ingressClassName: {{ .Values.ingress.className }}
28 | {{- end }}
29 | {{- if .Values.ingress.tls }}
30 | tls:
31 | {{- range .Values.ingress.tls }}
32 | - hosts:
33 | {{- range .hosts }}
34 | - {{ . | quote }}
35 | {{- end }}
36 | secretName: {{ .secretName }}
37 | {{- end }}
38 | {{- end }}
39 | rules:
40 | {{- range .Values.ingress.hosts }}
41 | - host: {{ .host | quote }}
42 | http:
43 | paths:
44 | {{- range .paths }}
45 | - path: {{ .path }}
46 | {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
47 | pathType: {{ .pathType }}
48 | {{- end }}
49 | backend:
50 | {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
51 | service:
52 | name: {{ $fullName }}
53 | port:
54 | number: {{ $svcPort }}
55 | {{- else }}
56 | serviceName: {{ $fullName }}
57 | servicePort: {{ $svcPort }}
58 | {{- end }}
59 | {{- end }}
60 | {{- end }}
61 | {{- end }}
62 |
--------------------------------------------------------------------------------
/api-server/middleware/auth.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package middleware
17 |
18 | import (
19 | "errors"
20 | "log"
21 | "net/http"
22 | "net/url"
23 |
24 | "github.com/gin-gonic/gin"
25 |
26 | "collie-api-server/config"
27 | "collie-api-server/httputil"
28 | authSvc "collie-api-server/service/auth"
29 | )
30 |
31 | var loginUrl string = config.Require("oauth.hostUrl") + "/collie/portal/login"
32 |
33 | func handleAuth(c *gin.Context) bool {
34 | token := c.GetHeader("Authorization")
35 | if token == "" {
36 | t, err := c.Request.Cookie("auth")
37 | if err != nil {
38 | //log.Printf("auth failed. token=%s, err=%s", token, err.Error())
39 | return false
40 | }
41 | t2, err := url.QueryUnescape(t.Value)
42 | if err != nil {
43 | log.Printf("auth failed. token=%s, err=%s", t, err.Error())
44 | return false
45 | }
46 | token = t2
47 | }
48 | authInfo, err := authSvc.Authenticate(token)
49 |
50 | if err != nil {
51 | log.Printf("auth failed. token=%s, err=%s", token, err.Error())
52 | return false
53 | }
54 | c.Set("auth", authInfo)
55 | return true
56 | }
57 |
58 | func RedirectToLoginOnAuthFailure(c *gin.Context) {
59 | if handleAuth(c) {
60 | c.Next()
61 | } else {
62 | c.Redirect(http.StatusTemporaryRedirect, loginUrl)
63 | }
64 | }
65 |
66 | func Authenticate(c *gin.Context) {
67 | if handleAuth(c) {
68 | c.Next()
69 | } else {
70 | httputil.Abort(c, http.StatusUnauthorized, errors.New("Authorization failed"))
71 | }
72 | }
73 |
74 | func GetAuth(c *gin.Context) authSvc.AuthInfo {
75 | authInfo, exist := c.Get("auth")
76 | if !exist {
77 | // should never happen. Should be blocked by middleware
78 | panic("Missing auth info. Should be handled by middleware")
79 | }
80 | return authInfo.(authSvc.AuthInfo)
81 | }
82 |
--------------------------------------------------------------------------------
/api-server/service/agent-template.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package service
17 |
18 | import (
19 | "bytes"
20 | "collie-api-server/config"
21 | _ "embed"
22 | b64 "encoding/base64"
23 | "text/template"
24 | )
25 |
26 | type AgentYamlParams struct {
27 | ApiKey string
28 | ApiUrl string
29 | EsUrl string
30 | EsKey string
31 | Provider string
32 | Image string
33 | AgentId string
34 | }
35 |
36 | var (
37 | //go:embed template/agent.yaml
38 | templateAgentYaml string
39 |
40 | //go:embed template/kube-bench-job.yaml
41 | templateKubeBenchJob string
42 |
43 | //go:embed template/kube-hunter-job.yaml
44 | templateKubeHunterJob string
45 | )
46 |
47 | func GenerageAgentYaml(provider string, apiKey string, esKey string, agentId string) (string, error) {
48 |
49 | cfg := config.Get()
50 | t, err := template.New("agent.yaml").
51 | Option("missingkey=error").
52 | Parse(templateAgentYaml)
53 | if err != nil {
54 | return "", err
55 | }
56 |
57 | data := AgentYamlParams{
58 | ApiUrl: cfg.ApiURL,
59 | ApiKey: b64encode(apiKey), // secret, need encoding
60 | EsUrl: cfg.EsURL,
61 | EsKey: b64encode(esKey), // secret, need encoding
62 | Provider: provider,
63 | Image: cfg.AgentImage,
64 | AgentId: agentId,
65 | }
66 |
67 | var buffer bytes.Buffer
68 | err = t.Execute(&buffer, data)
69 | if err != nil {
70 | return "", err
71 | }
72 | text := buffer.String()
73 |
74 | text = combineK8sYaml(text, templateKubeBenchJob)
75 | text = combineK8sYaml(text, templateKubeHunterJob)
76 |
77 | return text, nil
78 | }
79 |
80 | func combineK8sYaml(src1 string, src2 string) string {
81 | return src1 + "\n---\n" + src2
82 | }
83 |
84 | func b64encode(val string) string {
85 | return b64.StdEncoding.EncodeToString([]byte(val))
86 | }
87 |
--------------------------------------------------------------------------------
/CONTRIBUTING_CLA.md:
--------------------------------------------------------------------------------
1 | # Contributing to compliance-dashboard-for-kubernetes
2 |
3 | We welcome contributions from the community and first want to thank you for taking the time to contribute!
4 |
5 |
6 | ## Ways to contribute
7 |
8 | We welcome many different types of contributions and not all of them need a Pull request. Contributions may include:
9 |
10 | * New features and proposals
11 | * Documentation
12 | * Bug fixes
13 | * Issue Triage
14 | * Answering questions and giving feedback
15 | * Helping to onboard new contributors
16 | * Other related activities
17 |
18 | ## Getting started
19 |
20 | ### Development Environment Setup
21 | WIP
22 |
23 | ### Build
24 | WIP
25 |
26 | ### Run and Test
27 | WIP
28 |
29 | ### Common Errors
30 |
31 |
32 | ## Contribution Flow
33 |
34 | This is a rough outline of what a contributor's workflow looks like:
35 |
36 | * Make a fork of the repository within your GitHub account
37 | * Create a topic branch in your fork from where you want to base your work
38 | * Make commits of logical units
39 | * Make sure your commit messages are with the proper format, quality and descriptiveness (see below)
40 | * Push your changes to the topic branch in your fork
41 | * Create a pull request containing that commit
42 |
43 | We follow the GitHub workflow and you can find more details on the [GitHub flow documentation](https://docs.github.com/en/get-started/quickstart/github-flow).
44 |
45 |
46 | ### Pull Request Checklist
47 |
48 | Before submitting your pull request, we advise you to use the following:
49 |
50 | 1. Check if your code changes will pass both code linting checks and unit tests.
51 | 2. Ensure your commit messages are descriptive. We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/). Be sure to include any related GitHub issue references in the commit message. See [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) for referencing issues and commits.
52 | 3. Check the commits and commits messages and ensure they are free from typos.
53 |
54 | ## Reporting Bugs and Creating Issues
55 |
56 | For specifics on what to include in your report, please follow the guidelines in the issue and pull request templates when available.
57 |
58 |
59 | ## Ask for Help
60 |
61 | The best way to reach us with a question when contributing is to ask on:
62 |
63 | * The original GitHub issue
64 | * The developer mailing list
65 |
66 |
67 | ## Additional Resources
68 |
69 |
70 |
--------------------------------------------------------------------------------
/deployment.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Mac
4 | Install homebrew: https://brew.sh/
5 | Install kubectl: https://formulae.brew.sh/formula/kubernetes-cli
6 | Install minikube: https://minikube.sigs.k8s.io/docs/start/
7 | Install helm chart: https://helm.sh/docs/intro/quickstart/
8 |
9 | Mac
10 | Install/upgrade kubectl:
11 | brew upgrade kubectl
12 | brew link --overwrite kubernetes-cli
13 |
14 | Install/upgrade minikube:
15 | brew unlink minikube
16 | brew install minikube
17 | brew link minikube
18 |
19 | Install/upgrade helm:
20 | brew install helm
21 |
22 | Update helm repo:
23 | helm repo add grafana https://grafana.github.io/helm-charts
24 | helm repo add elastic https://helm.elastic.co
25 |
26 | minikube addons enable default-storageclass
27 | minikube addons enable storage-provisioner
28 |
29 | Install ES
30 | helm install -n collie-server es elastic/elasticsearch -f ./es/values.yaml --wait
31 |
32 | Get ES password
33 | export ES_PASSWORD=$(kubectl get -n collie-server secret/elasticsearch-master-credentials -o jsonpath="{.data.password}" | base64 -d)
34 | ES_USER=$(kubectl get -n collie-server secret/elasticsearch-master-credentials -o jsonpath="{.data.username}" | base64 -d)
35 | ES_AUTH=$ES_USER:$ES_PASSWORD
36 | ES_URL=https://collie-dev.org:9200
37 |
38 | Create ES index
39 | ```
40 | curl -k -u $ES_AUTH -X PUT -H "Content-Type: application/json" -d '
41 | {
42 | "mappings": {
43 | "properties": {
44 | "resource": {
45 | "dynamic": false,
46 | "properties": {
47 | "metadata": {
48 | "properties": {
49 | "name": {
50 | "type": "text"
51 | },
52 | "namespace": {
53 | "type": "text"
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 | }
61 | }' $ES_URL/collie-k8s-elastic
62 | ```
63 |
64 | Install Grafana
65 | envsubst < ./grafana/values.yaml > tmp.yaml
66 |
67 | #helm install grafana grafana/grafana -f ./grafana/values.yaml -n collie-server --set "grafana.datasources.\"datasources.yaml\".datasources[0].secureJsonData.basicAuthPassword=$ES_PASSWORD"
68 |
69 | helm install grafana grafana/grafana -f tmp.yaml -n collie-server --wait
70 | rm tmp.yaml
71 |
72 | Forward grafana and ES
73 | kubectl port-forward -n collie-server --address 0.0.0.0 services/elasticsearch-master 9200:9200 & \
74 | kubectl port-forward -n collie-server --address 0.0.0.0 services/grafana 3000:3000 & \
75 |
76 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- if (and (not .Values.useStatefulSet) (or (not .Values.persistence.enabled) (eq .Values.persistence.type "pvc"))) }}
2 | apiVersion: apps/v1
3 | kind: Deployment
4 | metadata:
5 | name: {{ include "grafana.fullname" . }}
6 | namespace: {{ include "grafana.namespace" . }}
7 | labels:
8 | {{- include "grafana.labels" . | nindent 4 }}
9 | {{- with .Values.labels }}
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | {{- with .Values.annotations }}
13 | annotations:
14 | {{- toYaml . | nindent 4 }}
15 | {{- end }}
16 | spec:
17 | {{- if and (not .Values.autoscaling.enabled) (.Values.replicas) }}
18 | replicas: {{ .Values.replicas }}
19 | {{- end }}
20 | revisionHistoryLimit: {{ .Values.revisionHistoryLimit }}
21 | selector:
22 | matchLabels:
23 | {{- include "grafana.selectorLabels" . | nindent 6 }}
24 | {{- with .Values.deploymentStrategy }}
25 | strategy:
26 | {{- toYaml . | trim | nindent 4 }}
27 | {{- end }}
28 | template:
29 | metadata:
30 | labels:
31 | {{- include "grafana.selectorLabels" . | nindent 8 }}
32 | {{- with .Values.podLabels }}
33 | {{- toYaml . | nindent 8 }}
34 | {{- end }}
35 | annotations:
36 | checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
37 | checksum/dashboards-json-config: {{ include (print $.Template.BasePath "/dashboards-json-configmap.yaml") . | sha256sum }}
38 | checksum/sc-dashboard-provider-config: {{ include (print $.Template.BasePath "/configmap-dashboard-provider.yaml") . | sha256sum }}
39 | {{- if and (or (and (not .Values.admin.existingSecret) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD)) (and .Values.ldap.enabled (not .Values.ldap.existingSecret))) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }}
40 | checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
41 | {{- end }}
42 | {{- if .Values.envRenderSecret }}
43 | checksum/secret-env: {{ include (print $.Template.BasePath "/secret-env.yaml") . | sha256sum }}
44 | {{- end }}
45 | kubectl.kubernetes.io/default-container: {{ .Chart.Name }}
46 | {{- with .Values.podAnnotations }}
47 | {{- toYaml . | nindent 8 }}
48 | {{- end }}
49 | spec:
50 | {{- include "grafana.pod" . | nindent 6 }}
51 | {{- end }}
52 |
--------------------------------------------------------------------------------
/api-server/service/oauth/common/common.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 | */
17 | package common
18 |
19 | import (
20 | "collie-api-server/util"
21 | "context"
22 | b64 "encoding/base64"
23 | "fmt"
24 | "net/http"
25 |
26 | "golang.org/x/oauth2"
27 | lru "k8s.io/utils/lru"
28 | )
29 |
30 | var (
31 | stateLRU *lru.Cache = lru.New(1024)
32 | )
33 |
34 | func init() {
35 | }
36 |
37 | func GetAuthUrl(oauthConfig *oauth2.Config) string {
38 | state := util.RandomString(8)
39 | codeVerifier := b64encode(util.RandomString(44))
40 | stateLRU.Add(state, codeVerifier)
41 |
42 | //codeChallenge := oauth2.SetAuthURLParam("code_challenge", codeVerifier)
43 | //codeChallengeMethod := oauth2.SetAuthURLParam("code_challenge_method", "S256")
44 |
45 | //return oauthConfig.AuthCodeURL(state, codeChallenge, codeChallengeMethod)
46 | return oauthConfig.AuthCodeURL(state)
47 | }
48 |
49 | func b64encode(val string) string {
50 | var RawURLEncoding = b64.URLEncoding.WithPadding(b64.NoPadding)
51 | return RawURLEncoding.EncodeToString([]byte(val))
52 | }
53 |
54 | func HandleCallback(oauthConfig *oauth2.Config, state string, code string) (*oauth2.Token, error, int) {
55 |
56 | _, present := stateLRU.Get(state)
57 | if !present {
58 | return nil, fmt.Errorf("Invalid state parameter: %s", state), http.StatusBadRequest
59 | }
60 | stateLRU.Remove(state)
61 |
62 | token, err := oauthConfig.Exchange(context.Background(), code)
63 | if err != nil {
64 | return nil, err, http.StatusInternalServerError
65 | }
66 |
67 | //log.Println("Callback received, exchange token...")
68 | //token, err := oauthConfig.Exchange(context.Background(), code, oauth2.SetAuthURLParam("code_verifier", codeVerifier.(string)))
69 | //if err != nil {
70 | // return nil, err, http.StatusInternalServerError
71 | //}
72 | //log.Println("Auth complete. Token received.")
73 | return token, nil, http.StatusOK
74 | }
75 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: {{ include "..fullname" . }}
5 | labels:
6 | {{- include "..labels" . | nindent 4 }}
7 | spec:
8 | {{- if not .Values.autoscaling.enabled }}
9 | replicas: {{ .Values.replicaCount }}
10 | {{- end }}
11 | selector:
12 | matchLabels:
13 | {{- include "..selectorLabels" . | nindent 6 }}
14 | template:
15 | metadata:
16 | {{- with .Values.podAnnotations }}
17 | annotations:
18 | {{- toYaml . | nindent 8 }}
19 | {{- end }}
20 | labels:
21 | {{- include "..selectorLabels" . | nindent 8 }}
22 | spec:
23 | {{- with .Values.imagePullSecrets }}
24 | imagePullSecrets:
25 | {{- toYaml . | nindent 8 }}
26 | {{- end }}
27 | serviceAccountName: {{ include "..serviceAccountName" . }}
28 | automountServiceAccountToken: true
29 | securityContext:
30 | {{- toYaml .Values.podSecurityContext | nindent 8 }}
31 | containers:
32 | - name: {{ .Chart.Name }}
33 | securityContext:
34 | {{- toYaml .Values.securityContext | nindent 12 }}
35 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
36 | imagePullPolicy: {{ .Values.image.pullPolicy }}
37 | env:
38 | - name: PPROF_PORT
39 | value: "6060"
40 | envFrom:
41 | - configMapRef:
42 | name: {{ include "..fullname" . }}
43 | - secretRef:
44 | name: {{ include "..fullname" . }}
45 | - secretRef:
46 | name: elasticsearch-master-credentials
47 | ports:
48 | - name: healthz
49 | containerPort: 9876
50 | protocol: TCP
51 | - name: http
52 | containerPort: 8080
53 | protocol: TCP
54 | livenessProbe:
55 | httpGet:
56 | port: healthz
57 | readinessProbe:
58 | httpGet:
59 | port: healthz
60 | resources:
61 | {{- toYaml .Values.resources | nindent 12 }}
62 | {{- with .Values.nodeSelector }}
63 | nodeSelector:
64 | {{- toYaml . | nindent 8 }}
65 | {{- end }}
66 | {{- with .Values.affinity }}
67 | affinity:
68 | {{- toYaml . | nindent 8 }}
69 | {{- end }}
70 | {{- with .Values.tolerations }}
71 | tolerations:
72 | {{- toYaml . | nindent 8 }}
73 | {{- end }}
74 |
--------------------------------------------------------------------------------
/dashboard/cert/nanw.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIGzzCCBbegAwIBAgIQCnYpIDwD8ksaMcV83/eQAzANBgkqhkiG9w0BAQsFADBP
3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSkwJwYDVQQDEyBE
4 | aWdpQ2VydCBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTAeFw0yMjA5MTQwMDAwMDBa
5 | Fw0yMzEwMTUyMzU5NTlaMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
6 | bmlhMRIwEAYDVQQHEwlQYWxvIEFsdG8xFTATBgNVBAoTDFZNd2FyZSwgSW5jLjEc
7 | MBoGA1UEAxMTbmFudy5lbmcudm13YXJlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
8 | ggEPADCCAQoCggEBAK6rgEo93BI3xBpebe5UAQDfJbRsjf5PnqMfOBxaXoL38xz4
9 | ZzaDSUyKbU07RAoZizofGlomRTAoIJUvUH+TjwgZm7bvTYO2M5Cn4+9zoqO4j54G
10 | kvVdzplatbo6eHwdozgMeSNFpa2UtYVttpfm84jLE1wE9edJseZ8jxhzdItAgsJa
11 | o8ScC7bX9X6pWL7QqK57XUgiSuCPSCgnTS48R6L8q84y6sCi8MNJkW9B7gC54buP
12 | RInlSqwUy69cHhMq7GpXkCrZziZGQLmsySbHbeMPYUm8gid3sbQsk+rqyJJdMCjD
13 | b6SB8Sgoiq4aw6n7nF00ygSVn0+1nalUwEHV/ScCAwEAAaOCA4kwggOFMB8GA1Ud
14 | IwQYMBaAFLdrouqoqoSMeeq02g+YssWVdrn0MB0GA1UdDgQWBBQJcJOKflvC77N6
15 | BnIEG/WUawFtDjA1BgNVHREELjAsghNuYW53LmVuZy52bXdhcmUuY29tghVjb2xs
16 | aWUuZW5nLnZtd2FyZS5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG
17 | AQUFBwMBBggrBgEFBQcDAjCBjwYDVR0fBIGHMIGEMECgPqA8hjpodHRwOi8vY3Js
18 | My5kaWdpY2VydC5jb20vRGlnaUNlcnRUTFNSU0FTSEEyNTYyMDIwQ0ExLTQuY3Js
19 | MECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUTFNSU0FT
20 | SEEyNTYyMDIwQ0ExLTQuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQICMCkwJwYIKwYB
21 | BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzB/BggrBgEFBQcBAQRz
22 | MHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBJBggrBgEF
23 | BQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTUlNB
24 | U0hBMjU2MjAyMENBMS0xLmNydDAJBgNVHRMEAjAAMIIBfQYKKwYBBAHWeQIEAgSC
25 | AW0EggFpAWcAdQDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYM8
26 | 5d8TAAAEAwBGMEQCIDkuFqnuujXX5kBWNKGvEqF/fqUstx3R8Rgpow+URV5uAiAE
27 | stDDN242vZH3tw0vNnkT4AqcKX6CZj+NmbRuo1yURAB2ADXPGRu/sWxXvw+tTG1C
28 | y7u2JyAmUeo/4SrvqAPDO9ZMAAABgzzl3ukAAAQDAEcwRQIgKsMap3e2pwPGhW+G
29 | crGTR/pDhKSXX6fIXX3nhHaD8XcCIQC7ZgkuLbufzS4nEjqu6WxwIJD4LUW/Chii
30 | uPiDAG3kaAB2ALNzdwfhhFD4Y4bWBancEQlKeS2xZwwLh9zwAw55NqWaAAABgzzl
31 | 31wAAAQDAEcwRQIgaiKCxnJYzfpiQxsIeHt/t7pOxLABIbHRw7CLKRtDeeACIQDO
32 | uaZVe3FdhdM4BDQSMXrn+pI2G8SBeA3EUOexJZu/2jANBgkqhkiG9w0BAQsFAAOC
33 | AQEAW9CJSmfFRrZ++eCjXP9wfDzwlaGKBJmi3XdgDNnQqDIbdFh7nGOH44Hn9csH
34 | nGXNvmLqhqFd/sWxq9j/K3xOKd7XRdpiXczhqr5EvVg9tkeOD030XwNJMo3ZHLh2
35 | 4jK2Q+r9Zrs2ebsMY44zjS+F9I2T0EZ1cT/9/stIgy6zKPVjj3epvQr8obZ3+DMp
36 | DXSIdaXYN/UbDw7CsL7tRroeui6DCN73e2J1c6XYP8al+LQCfbl7J1xdc1oCnNOD
37 | K7KPY+zYpVHsTJa9ARVilgL7CfficQQgim1UlTwgqmp5vh6xCNmQ6474AEy50Ql4
38 | 2uzHZsPo7iTCu8RCfE+XpQCeaA==
39 | -----END CERTIFICATE-----
40 |
--------------------------------------------------------------------------------
/dashboard/cert/nanw_eng_vmware_com.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIGzzCCBbegAwIBAgIQCnYpIDwD8ksaMcV83/eQAzANBgkqhkiG9w0BAQsFADBP
3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSkwJwYDVQQDEyBE
4 | aWdpQ2VydCBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTAeFw0yMjA5MTQwMDAwMDBa
5 | Fw0yMzEwMTUyMzU5NTlaMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
6 | bmlhMRIwEAYDVQQHEwlQYWxvIEFsdG8xFTATBgNVBAoTDFZNd2FyZSwgSW5jLjEc
7 | MBoGA1UEAxMTbmFudy5lbmcudm13YXJlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
8 | ggEPADCCAQoCggEBAK6rgEo93BI3xBpebe5UAQDfJbRsjf5PnqMfOBxaXoL38xz4
9 | ZzaDSUyKbU07RAoZizofGlomRTAoIJUvUH+TjwgZm7bvTYO2M5Cn4+9zoqO4j54G
10 | kvVdzplatbo6eHwdozgMeSNFpa2UtYVttpfm84jLE1wE9edJseZ8jxhzdItAgsJa
11 | o8ScC7bX9X6pWL7QqK57XUgiSuCPSCgnTS48R6L8q84y6sCi8MNJkW9B7gC54buP
12 | RInlSqwUy69cHhMq7GpXkCrZziZGQLmsySbHbeMPYUm8gid3sbQsk+rqyJJdMCjD
13 | b6SB8Sgoiq4aw6n7nF00ygSVn0+1nalUwEHV/ScCAwEAAaOCA4kwggOFMB8GA1Ud
14 | IwQYMBaAFLdrouqoqoSMeeq02g+YssWVdrn0MB0GA1UdDgQWBBQJcJOKflvC77N6
15 | BnIEG/WUawFtDjA1BgNVHREELjAsghNuYW53LmVuZy52bXdhcmUuY29tghVjb2xs
16 | aWUuZW5nLnZtd2FyZS5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG
17 | AQUFBwMBBggrBgEFBQcDAjCBjwYDVR0fBIGHMIGEMECgPqA8hjpodHRwOi8vY3Js
18 | My5kaWdpY2VydC5jb20vRGlnaUNlcnRUTFNSU0FTSEEyNTYyMDIwQ0ExLTQuY3Js
19 | MECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUTFNSU0FT
20 | SEEyNTYyMDIwQ0ExLTQuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQICMCkwJwYIKwYB
21 | BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzB/BggrBgEFBQcBAQRz
22 | MHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBJBggrBgEF
23 | BQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTUlNB
24 | U0hBMjU2MjAyMENBMS0xLmNydDAJBgNVHRMEAjAAMIIBfQYKKwYBBAHWeQIEAgSC
25 | AW0EggFpAWcAdQDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYM8
26 | 5d8TAAAEAwBGMEQCIDkuFqnuujXX5kBWNKGvEqF/fqUstx3R8Rgpow+URV5uAiAE
27 | stDDN242vZH3tw0vNnkT4AqcKX6CZj+NmbRuo1yURAB2ADXPGRu/sWxXvw+tTG1C
28 | y7u2JyAmUeo/4SrvqAPDO9ZMAAABgzzl3ukAAAQDAEcwRQIgKsMap3e2pwPGhW+G
29 | crGTR/pDhKSXX6fIXX3nhHaD8XcCIQC7ZgkuLbufzS4nEjqu6WxwIJD4LUW/Chii
30 | uPiDAG3kaAB2ALNzdwfhhFD4Y4bWBancEQlKeS2xZwwLh9zwAw55NqWaAAABgzzl
31 | 31wAAAQDAEcwRQIgaiKCxnJYzfpiQxsIeHt/t7pOxLABIbHRw7CLKRtDeeACIQDO
32 | uaZVe3FdhdM4BDQSMXrn+pI2G8SBeA3EUOexJZu/2jANBgkqhkiG9w0BAQsFAAOC
33 | AQEAW9CJSmfFRrZ++eCjXP9wfDzwlaGKBJmi3XdgDNnQqDIbdFh7nGOH44Hn9csH
34 | nGXNvmLqhqFd/sWxq9j/K3xOKd7XRdpiXczhqr5EvVg9tkeOD030XwNJMo3ZHLh2
35 | 4jK2Q+r9Zrs2ebsMY44zjS+F9I2T0EZ1cT/9/stIgy6zKPVjj3epvQr8obZ3+DMp
36 | DXSIdaXYN/UbDw7CsL7tRroeui6DCN73e2J1c6XYP8al+LQCfbl7J1xdc1oCnNOD
37 | K7KPY+zYpVHsTJa9ARVilgL7CfficQQgim1UlTwgqmp5vh6xCNmQ6474AEy50Ql4
38 | 2uzHZsPo7iTCu8RCfE+XpQCeaA==
39 | -----END CERTIFICATE-----
40 |
--------------------------------------------------------------------------------
/api-server/commonms/base.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package commonms
17 |
18 | import (
19 | "collie-api-server/config"
20 | "context"
21 | "errors"
22 | "github.com/sirupsen/logrus"
23 | "runtime/debug"
24 | "sigs.k8s.io/controller-runtime/pkg/manager/signals"
25 | )
26 |
27 | func RunApp(fnRun func(config.Config, *logrus.Entry, context.Context, chan error) error) {
28 |
29 | cfg := config.Get()
30 |
31 | logger := logrus.New()
32 | logger.SetLevel(logrus.Level(cfg.Log.Level))
33 | log := logger.WithField("version", "local")
34 |
35 | logBuildInfo(log)
36 |
37 | log.Printf("%v", cfg)
38 |
39 | ctx := signals.SetupSignalHandler()
40 | ctx, ctxCancel := context.WithCancel(ctx)
41 | defer ctxCancel()
42 |
43 | exitCh := make(chan error, 10)
44 | go watchExitErrors(ctx, log, exitCh, ctxCancel)
45 | closeHealthz := StartHealthz(cfg, log, exitCh)
46 | defer closeHealthz()
47 |
48 | if err := fnRun(cfg, log, ctx, exitCh); err != nil {
49 | log.Fatalf("agent failed: %v", err)
50 | }
51 |
52 | log.Println("Exit")
53 | }
54 |
55 | func logBuildInfo(log *logrus.Entry) {
56 | // - vcs.revision: the revision identifier for the current commit or checkout
57 | // - vcs.time: the modification time associated with vcs.revision, in RFC3339 format
58 | interestedFields := map[string]int{"vcs.revision": 1, "vcs.time": 1}
59 | if bi, ok := debug.ReadBuildInfo(); ok {
60 | log.Printf(bi.GoVersion)
61 | for _, v := range bi.Settings {
62 | if _, ok := interestedFields[v.Key]; ok {
63 | log.Println(v.Key, v.Value)
64 | }
65 | }
66 | }
67 | }
68 |
69 | // if any errors are observed on exitCh, context cancel is called, and all errors in the channel are logged
70 | func watchExitErrors(ctx context.Context, log *logrus.Entry, exitCh chan error, ctxCancel func()) {
71 | select {
72 | case err := <-exitCh:
73 | if err != nil && !errors.Is(err, context.Canceled) {
74 | log.Errorf("Stopped with an error: %v", err)
75 | }
76 | ctxCancel()
77 | case <-ctx.Done():
78 | return
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/statefulset.yaml:
--------------------------------------------------------------------------------
1 | {{- $sts := list "sts" "StatefulSet" "statefulset" -}}
2 | {{- if (or (.Values.useStatefulSet) (and .Values.persistence.enabled (not .Values.persistence.existingClaim) (has .Values.persistence.type $sts)))}}
3 | apiVersion: apps/v1
4 | kind: StatefulSet
5 | metadata:
6 | name: {{ include "grafana.fullname" . }}
7 | namespace: {{ include "grafana.namespace" . }}
8 | labels:
9 | {{- include "grafana.labels" . | nindent 4 }}
10 | {{- with .Values.annotations }}
11 | annotations:
12 | {{- toYaml . | nindent 4 }}
13 | {{- end }}
14 | spec:
15 | replicas: {{ .Values.replicas }}
16 | selector:
17 | matchLabels:
18 | {{- include "grafana.selectorLabels" . | nindent 6 }}
19 | serviceName: {{ include "grafana.fullname" . }}-headless
20 | template:
21 | metadata:
22 | labels:
23 | {{- include "grafana.selectorLabels" . | nindent 8 }}
24 | {{- with .Values.podLabels }}
25 | {{- toYaml . | nindent 8 }}
26 | {{- end }}
27 | annotations:
28 | checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
29 | checksum/dashboards-json-config: {{ include (print $.Template.BasePath "/dashboards-json-configmap.yaml") . | sha256sum }}
30 | checksum/sc-dashboard-provider-config: {{ include (print $.Template.BasePath "/configmap-dashboard-provider.yaml") . | sha256sum }}
31 | {{- if and (or (and (not .Values.admin.existingSecret) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD__FILE) (not .Values.env.GF_SECURITY_ADMIN_PASSWORD)) (and .Values.ldap.enabled (not .Values.ldap.existingSecret))) (not .Values.env.GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION) }}
32 | checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
33 | {{- end }}
34 | kubectl.kubernetes.io/default-container: {{ .Chart.Name }}
35 | {{- with .Values.podAnnotations }}
36 | {{- toYaml . | nindent 8 }}
37 | {{- end }}
38 | spec:
39 | {{- include "grafana.pod" . | nindent 6 }}
40 | {{- if .Values.persistence.enabled}}
41 | volumeClaimTemplates:
42 | - metadata:
43 | name: storage
44 | spec:
45 | accessModes: {{ .Values.persistence.accessModes }}
46 | storageClassName: {{ .Values.persistence.storageClassName }}
47 | resources:
48 | requests:
49 | storage: {{ .Values.persistence.size }}
50 | {{- with .Values.persistence.selectorLabels }}
51 | selector:
52 | matchLabels:
53 | {{- toYaml . | nindent 10 }}
54 | {{- end }}
55 | {{- end }}
56 | {{- end }}
57 |
--------------------------------------------------------------------------------
/deployment/scripts/redeploy-api-server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source .env
4 |
5 | if [[ "$OSTYPE" == "linux-gnu"* ]]; then
6 | B64CMD="base64 --wrap=0"
7 | elif [[ "$OSTYPE" == "darwin"* ]]; then
8 | B64CMD="base64"
9 | elif [[ "$OSTYPE" == "cygwin" ]]; then
10 | echo "TODO: OSTYPE cygwin"
11 | exit 1
12 | elif [[ "$OSTYPE" == "msys" ]]; then
13 | echo "TODO: OSTYPE msys"
14 | exit 1
15 | elif [[ "$OSTYPE" == "win32" ]]; then
16 | echo "TODO: OSTYPE win32"
17 | exit 1
18 | elif [[ "$OSTYPE" == "freebsd"* ]]; then
19 | echo "TODO: OSTYPE freebsd"
20 | exit 1
21 | else
22 | echo "TODO: unknown OSTYPE "$OSTYPE
23 | exit 1
24 | fi
25 |
26 | # Delete deployments
27 | kubectl delete namespace collie-server collie-agent --wait=true
28 | sleep 30
29 |
30 | minikube image rm collie.azurecr.io/collie-api-server:1
31 | minikube image rm collie.azurecr.io/collie-agent:1
32 |
33 | # clear data
34 | source es-recreate-index.sh
35 |
36 | # redeploy
37 | export ES_KEY_B64=$(echo -n $ES_KEY | base64 --wrap=0)
38 | export OAUTH_CSP_CLIENTID_B64=$(echo -n $OAUTH_CSP_CLIENTID | $B64CMD)
39 | export OAUTH_CSP_CLIENTSECRET_B64=$(echo -n $OAUTH_CSP_CLIENTSECRET | $B64CMD)
40 | export OAUTH_GITLAB_CLIENTID_B64=$(echo -n $OAUTH_GITLAB_CLIENTID | $B64CMD)
41 | export OAUTH_GITLAB_CLIENTSECRET_B64=$(echo -n $OAUTH_GITLAB_CLIENTSECRET | $B64CMD)
42 |
43 | envsubst < api-server.yaml | kubectl apply -f -
44 | kubectl wait deployment -n collie-server api-server --for condition=Available=True --timeout=90s
45 | AUTH_TOKEN=gitlab/$(source auth-gitlab.sh | jq -r '.access_token')
46 | sleep 10
47 | BOOTSTRAP_CMD=$(curl -skH "Authorization: $AUTH_TOKEN" https://collie.eng.omnissa.com/collie/api/v1/onboarding/bootstrap | jq -r ".cmd")
48 |
49 | AGENT_ID=$(echo $BOOTSTRAP_CMD | sed -n 's/.*aid\=\(.*\)\".*/\1/p')
50 |
51 | echo -e '#!/bin/bash\n' > deploy-agent.sh
52 | echo $BOOTSTRAP_CMD >> deploy-agent.sh
53 | chmod +x deploy-agent.sh
54 |
55 | source ./deploy-agent.sh
56 | sleep 30
57 | kubectl -n collie-agent logs deployment/agent
58 |
59 | # wait for data appear in ES
60 |
61 | echo $RESP
62 |
63 | for i in 1 2
64 | do
65 | RESP=$(curl -skS -u $COLLIE_ES_USERNAME:$COLLIE_ES_PASSWORD "$COLLIE_ES_URL/collie-k8s-elastic/_search?q=a:$AGENT_ID" | jq ".hits.hits[0]._source")
66 |
67 | if [ "$RESP" = "null" ]; then
68 | echo "Not ready yet..."
69 | sleep 10
70 | else
71 | echo "$RESP"
72 | echo "OK"
73 | kubectl delete namespace collie-agent --wait=true
74 | exit 0
75 | fi
76 | done
77 |
78 | echo "Failed waiting for agent report in ES"
79 | exit 1
80 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/image-renderer-network-policy.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.imageRenderer.enabled .Values.imageRenderer.networkPolicy.limitIngress }}
2 | ---
3 | apiVersion: networking.k8s.io/v1
4 | kind: NetworkPolicy
5 | metadata:
6 | name: {{ include "grafana.fullname" . }}-image-renderer-ingress
7 | namespace: {{ include "grafana.namespace" . }}
8 | annotations:
9 | comment: Limit image-renderer ingress traffic from grafana
10 | spec:
11 | podSelector:
12 | matchLabels:
13 | {{- include "grafana.imageRenderer.selectorLabels" . | nindent 6 }}
14 | {{- with .Values.imageRenderer.podLabels }}
15 | {{- toYaml . | nindent 6 }}
16 | {{- end }}
17 |
18 | policyTypes:
19 | - Ingress
20 | ingress:
21 | - ports:
22 | - port: {{ .Values.imageRenderer.service.targetPort }}
23 | protocol: TCP
24 | from:
25 | - namespaceSelector:
26 | matchLabels:
27 | kubernetes.io/metadata.name: {{ include "grafana.namespace" . }}
28 | podSelector:
29 | matchLabels:
30 | {{- include "grafana.selectorLabels" . | nindent 14 }}
31 | {{- with .Values.podLabels }}
32 | {{- toYaml . | nindent 14 }}
33 | {{- end }}
34 | {{- with .Values.imageRenderer.networkPolicy.extraIngressSelectors -}}
35 | {{ toYaml . | nindent 8 }}
36 | {{- end }}
37 | {{- end }}
38 |
39 | {{- if and .Values.imageRenderer.enabled .Values.imageRenderer.networkPolicy.limitEgress }}
40 | ---
41 | apiVersion: networking.k8s.io/v1
42 | kind: NetworkPolicy
43 | metadata:
44 | name: {{ include "grafana.fullname" . }}-image-renderer-egress
45 | namespace: {{ include "grafana.namespace" . }}
46 | annotations:
47 | comment: Limit image-renderer egress traffic to grafana
48 | spec:
49 | podSelector:
50 | matchLabels:
51 | {{- include "grafana.imageRenderer.selectorLabels" . | nindent 6 }}
52 | {{- with .Values.imageRenderer.podLabels }}
53 | {{- toYaml . | nindent 6 }}
54 | {{- end }}
55 |
56 | policyTypes:
57 | - Egress
58 | egress:
59 | # allow dns resolution
60 | - ports:
61 | - port: 53
62 | protocol: UDP
63 | - port: 53
64 | protocol: TCP
65 | # talk only to grafana
66 | - ports:
67 | - port: {{ .Values.service.targetPort }}
68 | protocol: TCP
69 | to:
70 | - namespaceSelector:
71 | matchLabels:
72 | name: {{ include "grafana.namespace" . }}
73 | podSelector:
74 | matchLabels:
75 | {{- include "grafana.selectorLabels" . | nindent 14 }}
76 | {{- with .Values.podLabels }}
77 | {{- toYaml . | nindent 14 }}
78 | {{- end }}
79 | {{- end }}
80 |
--------------------------------------------------------------------------------
/k8s-agent/internal/commonms/base.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package commonms
17 |
18 | import (
19 | "collie-agent/internal/config"
20 | "context"
21 | "encoding/json"
22 | "errors"
23 | "runtime/debug"
24 |
25 | "github.com/sirupsen/logrus"
26 | "sigs.k8s.io/controller-runtime/pkg/manager/signals"
27 | )
28 |
29 | func RunApp(fnRun func(config.Config, *logrus.Entry, context.Context, chan error) error) {
30 |
31 | cfg := config.Get()
32 |
33 | logger := logrus.New()
34 | logger.SetLevel(logrus.Level(cfg.Log.Level))
35 | log := logger.WithField("version", "local")
36 |
37 | logBuildInfo(log)
38 |
39 | bytes, err := json.MarshalIndent(cfg, "", " ")
40 | if err != nil {
41 | log.Warnf("Error serializing config to json: %s", err)
42 | } else {
43 | log.Println("config:", string(bytes))
44 | }
45 |
46 | ctx := signals.SetupSignalHandler()
47 | ctx, ctxCancel := context.WithCancel(ctx)
48 | defer ctxCancel()
49 |
50 | exitCh := make(chan error, 10)
51 | go watchExitErrors(ctx, log, exitCh, ctxCancel)
52 | closeHealthz := StartHealthz(cfg, log, exitCh)
53 | defer closeHealthz()
54 |
55 | if err := fnRun(cfg, log, ctx, exitCh); err != nil {
56 | log.Fatalf("agent failed: %v", err)
57 | }
58 |
59 | log.Println("Exit")
60 | }
61 |
62 | func logBuildInfo(log *logrus.Entry) {
63 | // - vcs.revision: the revision identifier for the current commit or checkout
64 | // - vcs.time: the modification time associated with vcs.revision, in RFC3339 format
65 | interestedFields := map[string]int{"vcs.revision": 1, "vcs.time": 1}
66 | if bi, ok := debug.ReadBuildInfo(); ok {
67 | log.Printf(bi.GoVersion)
68 | for _, v := range bi.Settings {
69 | if _, ok := interestedFields[v.Key]; ok {
70 | log.Println(v.Key, v.Value)
71 | }
72 | }
73 | }
74 | }
75 |
76 | // if any errors are observed on exitCh, context cancel is called, and all errors in the channel are logged
77 | func watchExitErrors(ctx context.Context, log *logrus.Entry, exitCh chan error, ctxCancel func()) {
78 | select {
79 | case err := <-exitCh:
80 | if err != nil && !errors.Is(err, context.Canceled) {
81 | log.Errorf("Stopped with an error: %v", err)
82 | }
83 | ctxCancel()
84 | case <-ctx.Done():
85 | return
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.ingress.enabled -}}
2 | {{- $ingressApiIsStable := eq (include "grafana.ingress.isStable" .) "true" -}}
3 | {{- $ingressSupportsIngressClassName := eq (include "grafana.ingress.supportsIngressClassName" .) "true" -}}
4 | {{- $ingressSupportsPathType := eq (include "grafana.ingress.supportsPathType" .) "true" -}}
5 | {{- $fullName := include "grafana.fullname" . -}}
6 | {{- $servicePort := .Values.service.port -}}
7 | {{- $ingressPath := .Values.ingress.path -}}
8 | {{- $ingressPathType := .Values.ingress.pathType -}}
9 | {{- $extraPaths := .Values.ingress.extraPaths -}}
10 | apiVersion: {{ include "grafana.ingress.apiVersion" . }}
11 | kind: Ingress
12 | metadata:
13 | name: {{ $fullName }}
14 | namespace: {{ include "grafana.namespace" . }}
15 | labels:
16 | {{- include "grafana.labels" . | nindent 4 }}
17 | {{- with .Values.ingress.labels }}
18 | {{- toYaml . | nindent 4 }}
19 | {{- end }}
20 | {{- with .Values.ingress.annotations }}
21 | annotations:
22 | {{- range $key, $value := . }}
23 | {{ $key }}: {{ tpl $value $ | quote }}
24 | {{- end }}
25 | {{- end }}
26 | spec:
27 | {{- if and $ingressSupportsIngressClassName .Values.ingress.ingressClassName }}
28 | ingressClassName: {{ .Values.ingress.ingressClassName }}
29 | {{- end -}}
30 | {{- with .Values.ingress.tls }}
31 | tls:
32 | {{- tpl (toYaml .) $ | nindent 4 }}
33 | {{- end }}
34 | rules:
35 | {{- if .Values.ingress.hosts }}
36 | {{- range .Values.ingress.hosts }}
37 | - host: {{ tpl . $ }}
38 | http:
39 | paths:
40 | {{- with $extraPaths }}
41 | {{- toYaml . | nindent 10 }}
42 | {{- end }}
43 | - path: {{ $ingressPath }}
44 | {{- if $ingressSupportsPathType }}
45 | pathType: {{ $ingressPathType }}
46 | {{- end }}
47 | backend:
48 | {{- if $ingressApiIsStable }}
49 | service:
50 | name: {{ $fullName }}
51 | port:
52 | number: {{ $servicePort }}
53 | {{- else }}
54 | serviceName: {{ $fullName }}
55 | servicePort: {{ $servicePort }}
56 | {{- end }}
57 | {{- end }}
58 | {{- else }}
59 | - http:
60 | paths:
61 | - backend:
62 | {{- if $ingressApiIsStable }}
63 | service:
64 | name: {{ $fullName }}
65 | port:
66 | number: {{ $servicePort }}
67 | {{- else }}
68 | serviceName: {{ $fullName }}
69 | servicePort: {{ $servicePort }}
70 | {{- end }}
71 | {{- with $ingressPath }}
72 | path: {{ . }}
73 | {{- end }}
74 | {{- if $ingressSupportsPathType }}
75 | pathType: {{ $ingressPathType }}
76 | {{- end }}
77 | {{- end -}}
78 | {{- end }}
79 |
--------------------------------------------------------------------------------
/deployment/helm-charts/api-server/values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for ..
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 |
5 | replicaCount: 1
6 |
7 | image:
8 | repository: collie.azurecr.io/collie-api-server
9 | pullPolicy: IfNotPresent
10 | # Overrides the image tag whose default is the chart appVersion.
11 | tag: 1
12 |
13 | imagePullSecrets: []
14 | nameOverride: ""
15 | fullnameOverride: ""
16 |
17 | serviceAccount:
18 | # Specifies whether a service account should be created
19 | create: true
20 | # Annotations to add to the service account
21 | annotations: {}
22 | # The name of the service account to use.
23 | # If not set and create is true, a name is generated using the fullname template
24 | name: api-server
25 |
26 | podAnnotations: {}
27 |
28 | podSecurityContext:
29 | fsGroup: 1002
30 | runAsGroup: 1002
31 | runAsNonRoot: true
32 | runAsUser: 1002
33 | seccompProfile:
34 | type: RuntimeDefault
35 |
36 | securityContext:
37 | capabilities:
38 | drop:
39 | - ALL
40 | readOnlyRootFilesystem: true
41 | runAsNonRoot: true
42 | runAsUser: 1000
43 | allowPrivilegeEscalation: false
44 |
45 |
46 | service:
47 | type: ClusterIP
48 | port: 8080
49 |
50 | ingress:
51 | enabled: true
52 | className: "nginx"
53 | annotations: {}
54 | # kubernetes.io/ingress.class: nginx
55 | # kubernetes.io/tls-acme: "true"
56 | hosts:
57 | - host: collie-dev.org
58 | paths:
59 | - path: /collie
60 | pathType: Prefix
61 | - path: /swagger
62 | pathType: Prefix
63 | tls: []
64 | # - secretName: chart-example-tls
65 | # hosts:
66 | # - chart-example.local
67 |
68 | resources:
69 | # We usually recommend not to specify default resources and to leave this as a conscious
70 | # choice for the user. This also increases chances charts run on environments with little
71 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
72 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
73 | limits:
74 | cpu: 100m
75 | memory: 128Mi
76 | # requests:
77 | # cpu: 100m
78 | # memory: 128Mi
79 |
80 | autoscaling:
81 | enabled: false
82 | minReplicas: 1
83 | maxReplicas: 100
84 | targetCPUUtilizationPercentage: 80
85 | # targetMemoryUtilizationPercentage: 80
86 |
87 | nodeSelector: {}
88 |
89 | tolerations: []
90 |
91 | affinity: {}
92 |
93 | collie:
94 | url: http://collie-dev.org:8080
95 |
96 | es:
97 | #key: "elastic:asdfasdf"
98 | url: https://collie-dev.org:9200
99 |
100 | grafana:
101 | url: http://collie-dev.org:3000/d/qIbLYbT4z/k8s-compliance-report"
102 |
103 | oauth:
104 | csp_clientid:
105 | csp_clientsecret:
106 | gitlab_clientid:
107 | gitlab_clientsecret:
108 | google_clientid:
109 | google_clientsecret:
110 |
--------------------------------------------------------------------------------
/deployment/scripts/job.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: batch/v1
3 | kind: Job
4 | metadata:
5 | name: kube-bench
6 | namespace: collie-agent
7 | spec:
8 | template:
9 | metadata:
10 | labels:
11 | app: kube-bench
12 | spec:
13 | hostPID: true
14 | containers:
15 | - name: kube-bench
16 | image: collie.azurecr.io/kube-bench:1
17 | command: ["kube-bench"]
18 | volumeMounts:
19 | - name: var-lib-etcd
20 | mountPath: /var/lib/etcd
21 | readOnly: true
22 | - name: var-lib-kubelet
23 | mountPath: /var/lib/kubelet
24 | readOnly: true
25 | - name: var-lib-kube-scheduler
26 | mountPath: /var/lib/kube-scheduler
27 | readOnly: true
28 | - name: var-lib-kube-controller-manager
29 | mountPath: /var/lib/kube-controller-manager
30 | readOnly: true
31 | - name: etc-systemd
32 | mountPath: /etc/systemd
33 | readOnly: true
34 | - name: lib-systemd
35 | mountPath: /lib/systemd/
36 | readOnly: true
37 | - name: srv-kubernetes
38 | mountPath: /srv/kubernetes/
39 | readOnly: true
40 | - name: etc-kubernetes
41 | mountPath: /etc/kubernetes
42 | readOnly: true
43 | # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version.
44 | # You can omit this mount if you specify --version as part of the command.
45 | - name: usr-bin
46 | mountPath: /usr/local/mount-from-host/bin
47 | readOnly: true
48 | - name: etc-cni-netd
49 | mountPath: /etc/cni/net.d/
50 | readOnly: true
51 | - name: opt-cni-bin
52 | mountPath: /opt/cni/bin/
53 | readOnly: true
54 | restartPolicy: Never
55 | volumes:
56 | - name: var-lib-etcd
57 | hostPath:
58 | path: "/var/lib/etcd"
59 | - name: var-lib-kubelet
60 | hostPath:
61 | path: "/var/lib/kubelet"
62 | - name: var-lib-kube-scheduler
63 | hostPath:
64 | path: "/var/lib/kube-scheduler"
65 | - name: var-lib-kube-controller-manager
66 | hostPath:
67 | path: "/var/lib/kube-controller-manager"
68 | - name: etc-systemd
69 | hostPath:
70 | path: "/etc/systemd"
71 | - name: lib-systemd
72 | hostPath:
73 | path: "/lib/systemd"
74 | - name: srv-kubernetes
75 | hostPath:
76 | path: "/srv/kubernetes"
77 | - name: etc-kubernetes
78 | hostPath:
79 | path: "/etc/kubernetes"
80 | - name: usr-bin
81 | hostPath:
82 | path: "/usr/bin"
83 | - name: etc-cni-netd
84 | hostPath:
85 | path: "/etc/cni/net.d/"
86 | - name: opt-cni-bin
87 | hostPath:
88 | path: "/opt/cni/bin/"
89 |
--------------------------------------------------------------------------------
/api-server/service/template/kube-bench-job.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: kube-bench
5 | namespace: collie-agent
6 | spec:
7 | template:
8 | metadata:
9 | labels:
10 | app: kube-bench
11 | spec:
12 | hostPID: true
13 | containers:
14 | - name: kube-bench
15 | image: collie.azurecr.io/kube-bench:1
16 | command: ["kube-bench"]
17 | volumeMounts:
18 | - name: var-lib-etcd
19 | mountPath: /var/lib/etcd
20 | readOnly: true
21 | - name: var-lib-kubelet
22 | mountPath: /var/lib/kubelet
23 | readOnly: true
24 | - name: var-lib-kube-scheduler
25 | mountPath: /var/lib/kube-scheduler
26 | readOnly: true
27 | - name: var-lib-kube-controller-manager
28 | mountPath: /var/lib/kube-controller-manager
29 | readOnly: true
30 | - name: etc-systemd
31 | mountPath: /etc/systemd
32 | readOnly: true
33 | - name: lib-systemd
34 | mountPath: /lib/systemd/
35 | readOnly: true
36 | - name: srv-kubernetes
37 | mountPath: /srv/kubernetes/
38 | readOnly: true
39 | - name: etc-kubernetes
40 | mountPath: /etc/kubernetes
41 | readOnly: true
42 | # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for auto-detecting the Kubernetes version.
43 | # You can omit this mount if you specify --version as part of the command.
44 | - name: usr-bin
45 | mountPath: /usr/local/mount-from-host/bin
46 | readOnly: true
47 | - name: etc-cni-netd
48 | mountPath: /etc/cni/net.d/
49 | readOnly: true
50 | - name: opt-cni-bin
51 | mountPath: /opt/cni/bin/
52 | readOnly: true
53 | restartPolicy: Never
54 | volumes:
55 | - name: var-lib-etcd
56 | hostPath:
57 | path: "/var/lib/etcd"
58 | - name: var-lib-kubelet
59 | hostPath:
60 | path: "/var/lib/kubelet"
61 | - name: var-lib-kube-scheduler
62 | hostPath:
63 | path: "/var/lib/kube-scheduler"
64 | - name: var-lib-kube-controller-manager
65 | hostPath:
66 | path: "/var/lib/kube-controller-manager"
67 | - name: etc-systemd
68 | hostPath:
69 | path: "/etc/systemd"
70 | - name: lib-systemd
71 | hostPath:
72 | path: "/lib/systemd"
73 | - name: srv-kubernetes
74 | hostPath:
75 | path: "/srv/kubernetes"
76 | - name: etc-kubernetes
77 | hostPath:
78 | path: "/etc/kubernetes"
79 | - name: usr-bin
80 | hostPath:
81 | path: "/usr/bin"
82 | - name: etc-cni-netd
83 | hostPath:
84 | path: "/etc/cni/net.d/"
85 | - name: opt-cni-bin
86 | hostPath:
87 | path: "/opt/cni/bin/"
--------------------------------------------------------------------------------
/deployment/helm-charts/grafana/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | 1. Get your '{{ .Values.adminUser }}' user password by running:
2 |
3 | kubectl get secret --namespace {{ include "grafana.namespace" . }} {{ .Values.admin.existingSecret | default (include "grafana.fullname" .) }} -o jsonpath="{.data.{{ .Values.admin.passwordKey | default "admin-password" }}}" | base64 --decode ; echo
4 |
5 |
6 | 2. The Grafana server can be accessed via port {{ .Values.service.port }} on the following DNS name from within your cluster:
7 |
8 | {{ include "grafana.fullname" . }}.{{ include "grafana.namespace" . }}.svc.cluster.local
9 | {{ if .Values.ingress.enabled }}
10 | If you bind grafana to 80, please update values in values.yaml and reinstall:
11 | ```
12 | securityContext:
13 | runAsUser: 0
14 | runAsGroup: 0
15 | fsGroup: 0
16 |
17 | command:
18 | - "setcap"
19 | - "'cap_net_bind_service=+ep'"
20 | - "/usr/sbin/grafana-server &&"
21 | - "sh"
22 | - "/run.sh"
23 | ```
24 | Details refer to https://grafana.com/docs/installation/configuration/#http-port.
25 | Or grafana would always crash.
26 |
27 | From outside the cluster, the server URL(s) are:
28 | {{- range .Values.ingress.hosts }}
29 | http://{{ . }}
30 | {{- end }}
31 | {{- else }}
32 | Get the Grafana URL to visit by running these commands in the same shell:
33 | {{- if contains "NodePort" .Values.service.type }}
34 | export NODE_PORT=$(kubectl get --namespace {{ include "grafana.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "grafana.fullname" . }})
35 | export NODE_IP=$(kubectl get nodes --namespace {{ include "grafana.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}")
36 | echo http://$NODE_IP:$NODE_PORT
37 | {{- else if contains "LoadBalancer" .Values.service.type }}
38 | NOTE: It may take a few minutes for the LoadBalancer IP to be available.
39 | You can watch the status of by running 'kubectl get svc --namespace {{ include "grafana.namespace" . }} -w {{ include "grafana.fullname" . }}'
40 | export SERVICE_IP=$(kubectl get svc --namespace {{ include "grafana.namespace" . }} {{ include "grafana.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
41 | http://$SERVICE_IP:{{ .Values.service.port -}}
42 | {{- else if contains "ClusterIP" .Values.service.type }}
43 | export POD_NAME=$(kubectl get pods --namespace {{ include "grafana.namespace" . }} -l "app.kubernetes.io/name={{ include "grafana.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
44 | kubectl --namespace {{ include "grafana.namespace" . }} port-forward $POD_NAME 3000
45 | {{- end }}
46 | {{- end }}
47 |
48 | 3. Login with the password from step 1 and the username: {{ .Values.adminUser }}
49 |
50 | {{- if not .Values.persistence.enabled }}
51 | #################################################################################
52 | ###### WARNING: Persistence is disabled!!! You will lose your data when #####
53 | ###### the Grafana pod is terminated. #####
54 | #################################################################################
55 | {{- end }}
56 |
--------------------------------------------------------------------------------
/api-server/service/auth/auth.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package auth
17 |
18 | import (
19 | "crypto/rand"
20 | "encoding/hex"
21 | "errors"
22 | "fmt"
23 | "strings"
24 |
25 | lru "k8s.io/utils/lru"
26 |
27 | "collie-api-server/config"
28 | "collie-api-server/service/oauth/csp"
29 | "collie-api-server/service/oauth/gitlab"
30 | "collie-api-server/service/oauth/google"
31 | )
32 |
33 | var (
34 | tokenCache *lru.Cache
35 | )
36 |
37 | func init() {
38 | tokenCache = lru.New(1024)
39 | }
40 |
41 | func generateRandToken(length int) string {
42 | b := make([]byte, length)
43 | if _, err := rand.Read(b); err != nil {
44 | return ""
45 | }
46 | return hex.EncodeToString(b)
47 | }
48 |
49 | func Authenticate(token string) (AuthInfo, error) {
50 | token = strings.TrimPrefix(token, "Bearer ")
51 | token = strings.TrimPrefix(token, "Token ")
52 |
53 | parts := strings.Split(token, "/")
54 | var provider string
55 | var code string
56 | if len(parts) == 1 {
57 | provider = "api"
58 | code = parts[0]
59 | } else {
60 | provider = parts[0]
61 | code = parts[1]
62 | }
63 |
64 | if provider == "api" {
65 | return validateApiToken(code)
66 | } else if provider == "csp" {
67 | t, err := csp.Validate(code)
68 | if err != nil {
69 | return nil, err
70 | }
71 | t["orgId"] = "csp/" + t["context_name"].(string)
72 | return FromMap(t), nil
73 | } else if provider == "gitlab" {
74 | t, err := gitlab.Validate(code)
75 | if err != nil {
76 | return nil, err
77 | }
78 | t["orgId"] = "gitlab/" + fmt.Sprintf("%v", t["id"])
79 | return FromMap(t), nil
80 | } else if provider == "google" {
81 | t, err := google.Validate(code)
82 | if err != nil {
83 | return nil, err
84 | }
85 | t["orgId"] = "google/" + fmt.Sprintf("%v", t["id"])
86 | return FromMap(t), nil
87 | } else {
88 | return nil, errors.New("Invalid auth provider: " + provider)
89 | }
90 | }
91 |
92 | func validateApiToken(code string) (AuthInfo, error) {
93 | authInfo, ok := tokenCache.Get(code)
94 | if !ok {
95 | return nil, errors.New("OTP does not exist")
96 | }
97 | return authInfo.(AuthInfo), nil
98 | }
99 |
100 | func AddToken(token string) {
101 | tokenCache.Add(token, nil)
102 | }
103 |
104 | func GenerateApiKey(authInfo AuthInfo) string {
105 | token := authInfo.OrgId() + ":" + generateRandToken(16)
106 | tokenCache.Add(token, authInfo)
107 | return token
108 | }
109 |
110 | func GenerateEsKey(authInfo AuthInfo) string {
111 | //esUser := authInfo.OrgId()
112 | //esPwd := "gen"
113 | //return esUser + ":" + esPwd
114 | return config.Get().EsKey
115 | }
116 |
--------------------------------------------------------------------------------
/k8s-agent/go.mod:
--------------------------------------------------------------------------------
1 | module collie-agent
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/cenkalti/backoff/v4 v4.2.0
7 | github.com/dustin/go-humanize v1.0.1
8 | github.com/elastic/go-elasticsearch/v8 v8.7.1
9 | github.com/go-resty/resty/v2 v2.7.0
10 | github.com/sirupsen/logrus v1.8.1
11 | github.com/spf13/viper v1.12.0
12 | k8s.io/api v0.25.4
13 | k8s.io/apimachinery v0.25.4
14 | k8s.io/client-go v0.25.4
15 | k8s.io/metrics v0.25.4
16 | sigs.k8s.io/controller-runtime v0.12.2
17 | )
18 |
19 | require (
20 | github.com/davecgh/go-spew v1.1.1 // indirect
21 | github.com/elastic/elastic-transport-go/v8 v8.2.0 // indirect
22 | github.com/emicklei/go-restful/v3 v3.8.0 // indirect
23 | github.com/fsnotify/fsnotify v1.5.4 // indirect
24 | github.com/go-logr/logr v1.2.3 // indirect
25 | github.com/go-openapi/jsonpointer v0.19.5 // indirect
26 | github.com/go-openapi/jsonreference v0.20.0 // indirect
27 | github.com/go-openapi/swag v0.21.1 // indirect
28 | github.com/gogo/protobuf v1.3.2 // indirect
29 | github.com/golang/protobuf v1.5.2 // indirect
30 | github.com/google/gnostic v0.6.9 // indirect
31 | github.com/google/gofuzz v1.2.0 // indirect
32 | github.com/hashicorp/hcl v1.0.0 // indirect
33 | github.com/imdario/mergo v0.3.13 // indirect
34 | github.com/josharian/intern v1.0.0 // indirect
35 | github.com/json-iterator/go v1.1.12 // indirect
36 | github.com/magiconair/properties v1.8.6 // indirect
37 | github.com/mailru/easyjson v0.7.7 // indirect
38 | github.com/mitchellh/mapstructure v1.5.0 // indirect
39 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
40 | github.com/modern-go/reflect2 v1.0.2 // indirect
41 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
42 | github.com/pelletier/go-toml v1.9.5 // indirect
43 | github.com/pelletier/go-toml/v2 v2.0.2 // indirect
44 | github.com/spf13/afero v1.8.2 // indirect
45 | github.com/spf13/cast v1.5.0 // indirect
46 | github.com/spf13/jwalterweatherman v1.1.0 // indirect
47 | github.com/spf13/pflag v1.0.5 // indirect
48 | github.com/subosito/gotenv v1.4.0 // indirect
49 | golang.org/x/net v0.9.0 // indirect
50 | golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26 // indirect
51 | golang.org/x/sys v0.7.0 // indirect
52 | golang.org/x/term v0.7.0 // indirect
53 | golang.org/x/text v0.9.0 // indirect
54 | golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
55 | google.golang.org/appengine v1.6.7 // indirect
56 | google.golang.org/protobuf v1.28.0 // indirect
57 | gopkg.in/inf.v0 v0.9.1 // indirect
58 | gopkg.in/ini.v1 v1.66.6 // indirect
59 | gopkg.in/yaml.v2 v2.4.0 // indirect
60 | gopkg.in/yaml.v3 v3.0.1 // indirect
61 | k8s.io/klog/v2 v2.70.1 // indirect
62 | k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
63 | k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
64 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
65 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
66 | sigs.k8s.io/yaml v1.3.0 // indirect
67 | )
68 |
69 | replace (
70 | github.com/gogo/protobuf v1.1.1 => github.com/gogo/protobuf v1.3.2
71 | github.com/gogo/protobuf v1.2.1 => github.com/gogo/protobuf v1.3.2
72 | github.com/gogo/protobuf v1.3.1 => github.com/gogo/protobuf v1.3.2
73 | )
74 |
75 | replace github.com/chzyer/logex v1.1.10 => github.com/chzyer/logex v1.2.0
76 |
--------------------------------------------------------------------------------
/api-server/model/account.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package model
17 |
18 | import (
19 | "errors"
20 | "fmt"
21 |
22 | uuid "github.com/gofrs/uuid"
23 | )
24 |
25 | // Account example
26 | type Account struct {
27 | ID int `json:"id" example:"1" format:"int64"`
28 | Name string `json:"name" example:"account name"`
29 | UUID uuid.UUID `json:"uuid" example:"550e8400-e29b-41d4-a716-446655440000" format:"uuid"`
30 | }
31 |
32 | // example
33 | var (
34 | ErrNameInvalid = errors.New("name is empty")
35 | )
36 |
37 | // AddAccount example
38 | type AddAccount struct {
39 | Name string `json:"name" example:"account name"`
40 | }
41 |
42 | // Validation example
43 | func (a AddAccount) Validation() error {
44 | switch {
45 | case len(a.Name) == 0:
46 | return ErrNameInvalid
47 | default:
48 | return nil
49 | }
50 | }
51 |
52 | // UpdateAccount example
53 | type UpdateAccount struct {
54 | Name string `json:"name" example:"account name"`
55 | }
56 |
57 | // Validation example
58 | func (a UpdateAccount) Validation() error {
59 | switch {
60 | case len(a.Name) == 0:
61 | return ErrNameInvalid
62 | default:
63 | return nil
64 | }
65 | }
66 |
67 | // AccountsAll example
68 | func AccountsAll(q string) ([]Account, error) {
69 | if q == "" {
70 | return accounts, nil
71 | }
72 | as := []Account{}
73 | for k, v := range accounts {
74 | if q == v.Name {
75 | as = append(as, accounts[k])
76 | }
77 | }
78 | return as, nil
79 | }
80 |
81 | // AccountOne example
82 | func AccountOne(id int) (Account, error) {
83 | for _, v := range accounts {
84 | if id == v.ID {
85 | return v, nil
86 | }
87 | }
88 | return Account{}, ErrNoRow
89 | }
90 |
91 | // Insert example
92 | func (a Account) Insert() (int, error) {
93 | accountMaxID++
94 | a.ID = accountMaxID
95 | a.Name = fmt.Sprintf("account_%d", accountMaxID)
96 | accounts = append(accounts, a)
97 | return accountMaxID, nil
98 | }
99 |
100 | // Delete example
101 | func Delete(id int) error {
102 | for k, v := range accounts {
103 | if id == v.ID {
104 | accounts = append(accounts[:k], accounts[k+1:]...)
105 | return nil
106 | }
107 | }
108 | return fmt.Errorf("account id=%d is not found", id)
109 | }
110 |
111 | // Update example
112 | func (a Account) Update() error {
113 | for k, v := range accounts {
114 | if a.ID == v.ID {
115 | accounts[k].Name = a.Name
116 | return nil
117 | }
118 | }
119 | return fmt.Errorf("account id=%d is not found", a.ID)
120 | }
121 |
122 | var accountMaxID = 3
123 | var accounts = []Account{
124 | {ID: 1, Name: "account_1"},
125 | {ID: 2, Name: "account_2"},
126 | {ID: 3, Name: "account_3"},
127 | }
128 |
--------------------------------------------------------------------------------
/api-server/go.mod:
--------------------------------------------------------------------------------
1 | module collie-api-server
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/cenkalti/backoff/v4 v4.2.1
7 | github.com/elastic/go-elasticsearch/v8 v8.7.1
8 | github.com/gin-contrib/cors v1.4.0
9 | github.com/gin-gonic/gin v1.9.1
10 | github.com/gofrs/uuid v4.2.0+incompatible
11 | github.com/lestrrat-go/jwx/v2 v2.0.11
12 | github.com/sirupsen/logrus v1.9.2
13 | github.com/spf13/viper v1.15.0
14 | github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2
15 | github.com/swaggo/gin-swagger v1.4.2
16 | github.com/swaggo/swag v1.16.1
17 | golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783
18 | k8s.io/utils v0.0.0-20230313181309-38a27ef9d749
19 | sigs.k8s.io/controller-runtime v0.14.6
20 | )
21 |
22 | require (
23 | cloud.google.com/go/compute v1.14.0 // indirect
24 | cloud.google.com/go/compute/metadata v0.2.3 // indirect
25 | github.com/KyleBanks/depth v1.2.1 // indirect
26 | github.com/bytedance/sonic v1.9.1 // indirect
27 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
28 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
29 | github.com/elastic/elastic-transport-go/v8 v8.2.0 // indirect
30 | github.com/fsnotify/fsnotify v1.6.0 // indirect
31 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect
32 | github.com/gin-contrib/sse v0.1.0 // indirect
33 | github.com/go-logr/logr v1.2.3 // indirect
34 | github.com/go-openapi/jsonpointer v0.19.6 // indirect
35 | github.com/go-openapi/jsonreference v0.20.2 // indirect
36 | github.com/go-openapi/spec v0.20.9 // indirect
37 | github.com/go-openapi/swag v0.22.3 // indirect
38 | github.com/go-playground/locales v0.14.1 // indirect
39 | github.com/go-playground/universal-translator v0.18.1 // indirect
40 | github.com/go-playground/validator/v10 v10.14.0 // indirect
41 | github.com/goccy/go-json v0.10.2 // indirect
42 | github.com/golang/protobuf v1.5.2 // indirect
43 | github.com/hashicorp/hcl v1.0.0 // indirect
44 | github.com/josharian/intern v1.0.0 // indirect
45 | github.com/json-iterator/go v1.1.12 // indirect
46 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect
47 | github.com/leodido/go-urn v1.2.4 // indirect
48 | github.com/lestrrat-go/blackmagic v1.0.1 // indirect
49 | github.com/lestrrat-go/httpcc v1.0.1 // indirect
50 | github.com/lestrrat-go/httprc v1.0.4 // indirect
51 | github.com/lestrrat-go/iter v1.0.2 // indirect
52 | github.com/lestrrat-go/option v1.0.1 // indirect
53 | github.com/magiconair/properties v1.8.7 // indirect
54 | github.com/mailru/easyjson v0.7.7 // indirect
55 | github.com/mattn/go-isatty v0.0.19 // indirect
56 | github.com/mitchellh/mapstructure v1.5.0 // indirect
57 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
58 | github.com/modern-go/reflect2 v1.0.2 // indirect
59 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect
60 | github.com/segmentio/asm v1.2.0 // indirect
61 | github.com/spf13/afero v1.9.3 // indirect
62 | github.com/spf13/cast v1.5.0 // indirect
63 | github.com/spf13/jwalterweatherman v1.1.0 // indirect
64 | github.com/spf13/pflag v1.0.5 // indirect
65 | github.com/subosito/gotenv v1.4.2 // indirect
66 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
67 | github.com/ugorji/go/codec v1.2.11 // indirect
68 | golang.org/x/arch v0.3.0 // indirect
69 | golang.org/x/crypto v0.9.0 // indirect
70 | golang.org/x/net v0.10.0 // indirect
71 | golang.org/x/sys v0.8.0 // indirect
72 | golang.org/x/text v0.9.0 // indirect
73 | golang.org/x/tools v0.9.3 // indirect
74 | google.golang.org/appengine v1.6.7 // indirect
75 | google.golang.org/protobuf v1.30.0 // indirect
76 | gopkg.in/ini.v1 v1.67.0 // indirect
77 | gopkg.in/yaml.v3 v3.0.1 // indirect
78 | k8s.io/apimachinery v0.26.1 // indirect
79 | )
80 |
--------------------------------------------------------------------------------
/api-server/controller/portal.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package controller
17 |
18 | import (
19 | "fmt"
20 | "log"
21 | "net/http"
22 | "text/template"
23 | "time"
24 |
25 | "github.com/gin-gonic/gin"
26 | "golang.org/x/oauth2"
27 |
28 | "collie-api-server/config"
29 | "collie-api-server/httputil"
30 | middleware "collie-api-server/middleware"
31 | "collie-api-server/service/oauth/csp"
32 | "collie-api-server/service/oauth/gitlab"
33 | "collie-api-server/service/oauth/google"
34 | "collie-api-server/service/org"
35 | )
36 |
37 | var homeUrl string = config.Require("collie.url") + "/collie/portal"
38 |
39 | func (c *Controller) PortalIndex(ctx *gin.Context) {
40 |
41 | authInfo := middleware.GetAuth(ctx)
42 | orgInfo, err := org.EnsureOnboard(authInfo.OrgId())
43 | if err != nil {
44 | httputil.Abort(ctx, http.StatusInternalServerError, err)
45 | return
46 | }
47 | cfg := config.Get()
48 | data := gin.H{
49 | "grafanaURL": cfg.GrafanaURL, //"https://collie.eng.omnissa.com/d/qIbLYbT4z/k8s-compliance-report"
50 | "grafanaOrgId": orgInfo.GrafanaOrgId, //"1"
51 | }
52 | ctx.HTML(http.StatusOK, "index.html", data)
53 | }
54 |
55 | func (c *Controller) PortalLogin(ctx *gin.Context) {
56 | data := map[string]interface{}{
57 | "cspAuthUrl": csp.GetAuthUrl(),
58 | "gitlabAuthUrl": gitlab.GetAuthUrl(),
59 | "googleAuthUrl": google.GetAuthUrl(),
60 | }
61 | //ctx.HTML(http.StatusOK, "login.html", data)
62 | renderTemplate(ctx, "login.html", data)
63 | }
64 |
65 | func renderTemplate(c *gin.Context, templateName string, data gin.H) {
66 | tmpl := template.Must(template.ParseFiles("assets/" + templateName))
67 |
68 | // Disable auto-escaping of template variables
69 | //tmpl.Option("html").EscapeHTML = false
70 |
71 | err := tmpl.ExecuteTemplate(c.Writer, templateName, data)
72 | if err != nil {
73 | // Handle the error
74 | c.AbortWithError(http.StatusInternalServerError, err)
75 | }
76 | }
77 |
78 | type fnCallback func(string, string) (*oauth2.Token, error, int)
79 |
80 | func callback(c *Controller, handleCallback fnCallback, provider string, ctx *gin.Context) {
81 | state := ctx.Query("state")
82 | code := ctx.Query("code")
83 | token, err, statusCode := handleCallback(state, code)
84 | if err != nil {
85 | log.Printf("Auth failed: %s", err.Error())
86 | httputil.Abort(ctx, statusCode, err)
87 | } else {
88 | age := int(time.Until(token.Expiry).Seconds())
89 | token := provider + "/" + token.AccessToken
90 | ctx.SetCookie("auth", token, age, "", "", false, true)
91 | ctx.Redirect(http.StatusTemporaryRedirect, homeUrl)
92 | }
93 | }
94 |
95 | func (c *Controller) OauthCallback(ctx *gin.Context) {
96 | provider := ctx.Param("provider")
97 | if provider == "csp" {
98 | callback(c, csp.HandleCallback, provider, ctx)
99 | } else if provider == "gitlab" {
100 | callback(c, gitlab.HandleCallback, provider, ctx)
101 | } else if provider == "google" {
102 | callback(c, google.HandleCallback, provider, ctx)
103 | } else {
104 | httputil.Abort(ctx, http.StatusBadRequest, fmt.Errorf("Unknown provider: %s", provider))
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/dashboard/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | wheels/
22 | share/python-wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .nox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | *.py,cover
49 | .hypothesis/
50 | .pytest_cache/
51 | cover/
52 |
53 | # Translations
54 | *.mo
55 | *.pot
56 |
57 | # Django stuff:
58 | *.log
59 | local_settings.py
60 | db.sqlite3
61 | db.sqlite3-journal
62 |
63 | # Flask stuff:
64 | instance/
65 | .webassets-cache
66 |
67 | # Scrapy stuff:
68 | .scrapy
69 |
70 | # Sphinx documentation
71 | docs/_build/
72 |
73 | # PyBuilder
74 | .pybuilder/
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | # For a library or package, you might want to ignore these files since the code is
86 | # intended to run in multiple environments; otherwise, check them in:
87 | # .python-version
88 |
89 | # pipenv
90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
93 | # install all needed dependencies.
94 | #Pipfile.lock
95 |
96 | # poetry
97 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
98 | # This is especially recommended for binary packages to ensure reproducibility, and is more
99 | # commonly ignored for libraries.
100 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
101 | #poetry.lock
102 |
103 | # pdm
104 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
105 | #pdm.lock
106 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
107 | # in version control.
108 | # https://pdm.fming.dev/#use-with-ide
109 | .pdm.toml
110 |
111 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
112 | __pypackages__/
113 |
114 | # Celery stuff
115 | celerybeat-schedule
116 | celerybeat.pid
117 |
118 | # SageMath parsed files
119 | *.sage.py
120 |
121 | # Environments
122 | .venv
123 | env/
124 | venv/
125 | ENV/
126 | env.bak/
127 | venv.bak/
128 |
129 | # Spyder project settings
130 | .spyderproject
131 | .spyproject
132 |
133 | # Rope project settings
134 | .ropeproject
135 |
136 | # mkdocs documentation
137 | /site
138 |
139 | # mypy
140 | .mypy_cache/
141 | .dmypy.json
142 | dmypy.json
143 |
144 | # Pyre type checker
145 | .pyre/
146 |
147 | # pytype static type analyzer
148 | .pytype/
149 |
150 | # Cython debug symbols
151 | cython_debug/
152 |
153 | # PyCharm
154 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
155 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
156 | # and can be added to the global gitignore or merged into this file. For a more nuclear
157 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
158 | #.idea/
159 |
160 | poller/data/
161 | tmp/
162 | data/
163 |
164 |
--------------------------------------------------------------------------------
/api-server/commonms/healthz.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package commonms
17 |
18 | import (
19 | "fmt"
20 | "github.com/sirupsen/logrus"
21 | "net/http"
22 | "time"
23 |
24 | "sigs.k8s.io/controller-runtime/pkg/healthz"
25 |
26 | "collie-api-server/config"
27 | )
28 |
29 | func newHealthzProvider(cfg config.Config, log logrus.FieldLogger) *HealthzProvider {
30 | return &HealthzProvider{
31 | cfg: cfg,
32 | log: log,
33 | initHardTimeout: cfg.Controller.PrepTimeout + cfg.Controller.InitialSleepDuration + cfg.Controller.InitializationTimeoutExtension,
34 | }
35 | }
36 |
37 | type HealthzProvider struct {
38 | cfg config.Config
39 | log logrus.FieldLogger
40 | initHardTimeout time.Duration
41 |
42 | initializeStartedAt *time.Time
43 | lastHealthyActionAt *time.Time
44 | }
45 |
46 | func (h *HealthzProvider) Check(_ *http.Request) (err error) {
47 | defer func() {
48 | if err != nil {
49 | h.log.Warnf("Health check failed due to: %v", err)
50 | }
51 | }()
52 |
53 | if h.lastHealthyActionAt != nil {
54 | if time.Since(*h.lastHealthyActionAt) > h.cfg.Controller.HealthySnapshotIntervalLimit {
55 | return fmt.Errorf("time since initialization or last snapshot sent is over the considered healthy limit of %s", h.cfg.Controller.HealthySnapshotIntervalLimit)
56 | }
57 | return nil
58 | }
59 |
60 | if h.initializeStartedAt != nil {
61 | if time.Since(*h.initializeStartedAt) > h.initHardTimeout {
62 | return fmt.Errorf("controller initialization is taking longer than the hard timeout of %s", h.initHardTimeout)
63 | }
64 | return nil
65 | }
66 |
67 | return nil
68 | }
69 |
70 | func (h *HealthzProvider) Initializing() {
71 | if h.initializeStartedAt == nil {
72 | h.initializeStartedAt = nowPtr()
73 | h.lastHealthyActionAt = nil
74 | }
75 | }
76 |
77 | func (h *HealthzProvider) Initialized() {
78 | h.healthyAction()
79 | }
80 |
81 | func (h *HealthzProvider) SnapshotSent() {
82 | h.healthyAction()
83 | }
84 |
85 | func (h *HealthzProvider) healthyAction() {
86 | h.initializeStartedAt = nil
87 | h.lastHealthyActionAt = nowPtr()
88 | }
89 |
90 | func nowPtr() *time.Time {
91 | now := time.Now()
92 | return &now
93 | }
94 |
95 | func runHealthzEndpoints(cfg config.Config, log *logrus.Entry, controllerCheck healthz.Checker, exitCh chan error) func() {
96 | log.Infof("starting healthz on port: %d", cfg.HealthzPort)
97 | healthzSrv := &http.Server{Addr: portToServerAddr(cfg.HealthzPort), Handler: &healthz.Handler{Checks: map[string]healthz.Checker{
98 | "server": healthz.Ping,
99 | "controller": controllerCheck,
100 | }}}
101 | closeFunc := func() {
102 | if err := healthzSrv.Close(); err != nil {
103 | log.Errorf("closing healthz server: %v", err)
104 | }
105 | }
106 |
107 | go func() {
108 | exitCh <- fmt.Errorf("healthz server: %w", healthzSrv.ListenAndServe())
109 | }()
110 | return closeFunc
111 | }
112 |
113 | func portToServerAddr(port int) string {
114 | return fmt.Sprintf(":%d", port)
115 | }
116 |
117 | func StartHealthz(cfg config.Config, log *logrus.Entry, exitCh chan error) func() {
118 | ctrlHealthz := newHealthzProvider(cfg, log)
119 | closeHealthz := runHealthzEndpoints(cfg, log, ctrlHealthz.Check, exitCh)
120 | return closeHealthz
121 | }
122 |
--------------------------------------------------------------------------------
/k8s-agent/internal/commonms/healthz.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023-2024 Omnissa, LLC.
3 | SPDX-License-Identifier: Apache-2.0
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 | http://www.apache.org/licenses/LICENSE-2.0
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package commonms
17 |
18 | import (
19 | "fmt"
20 | "net/http"
21 | "time"
22 |
23 | "github.com/sirupsen/logrus"
24 |
25 | "sigs.k8s.io/controller-runtime/pkg/healthz"
26 |
27 | "collie-agent/internal/config"
28 | )
29 |
30 | func newHealthzProvider(cfg config.Config, log logrus.FieldLogger) *HealthzProvider {
31 | return &HealthzProvider{
32 | cfg: cfg,
33 | log: log,
34 | initHardTimeout: cfg.Controller.PrepTimeout + cfg.Controller.InitialSleepDuration + cfg.Controller.InitializationTimeoutExtension,
35 | }
36 | }
37 |
38 | type HealthzProvider struct {
39 | cfg config.Config
40 | log logrus.FieldLogger
41 | initHardTimeout time.Duration
42 |
43 | initializeStartedAt *time.Time
44 | lastHealthyActionAt *time.Time
45 | }
46 |
47 | func now() *time.Time {
48 | now := time.Now()
49 | return &now
50 | }
51 |
52 | func (h *HealthzProvider) healthyAction() {
53 | h.initializeStartedAt = nil
54 | h.lastHealthyActionAt = now()
55 | }
56 |
57 | func runHealthzEndpoints(cfg config.Config, log *logrus.Entry, controllerCheck healthz.Checker, exitCh chan error) func() {
58 | log.Infof("starting healthz on port: %d", cfg.HealthzPort)
59 | healthzSrv := &http.Server{Addr: portToServerAddr(cfg.HealthzPort), Handler: &healthz.Handler{Checks: map[string]healthz.Checker{
60 | "server": healthz.Ping,
61 | "controller": controllerCheck,
62 | }}}
63 | closeFunc := func() {
64 | if err := healthzSrv.Close(); err != nil {
65 | log.Errorf("closing healthz server: %v", err)
66 | }
67 | }
68 |
69 | go func() {
70 | exitCh <- fmt.Errorf("healthz server: %w", healthzSrv.ListenAndServe())
71 | }()
72 | return closeFunc
73 | }
74 |
75 | func portToServerAddr(port int) string {
76 | return fmt.Sprintf(":%d", port)
77 | }
78 |
79 | func (h *HealthzProvider) Check(_ *http.Request) (err error) {
80 | defer func() {
81 | if err != nil {
82 | h.log.Warnf("Health check failed due to: %v", err)
83 | }
84 | }()
85 |
86 | if h.lastHealthyActionAt != nil {
87 | if time.Since(*h.lastHealthyActionAt) > h.cfg.Controller.HealthySnapshotIntervalLimit {
88 | return fmt.Errorf("time since initialization or last snapshot sent is over the considered healthy limit of %s", h.cfg.Controller.HealthySnapshotIntervalLimit)
89 | }
90 | return nil
91 | }
92 |
93 | if h.initializeStartedAt != nil {
94 | if time.Since(*h.initializeStartedAt) > h.initHardTimeout {
95 | return fmt.Errorf("controller initialization is taking longer than the hard timeout of %s", h.initHardTimeout)
96 | }
97 | return nil
98 | }
99 |
100 | return nil
101 | }
102 |
103 | func (h *HealthzProvider) Initializing() {
104 | if h.initializeStartedAt == nil {
105 | h.initializeStartedAt = now()
106 | h.lastHealthyActionAt = nil
107 | }
108 | }
109 |
110 | func (h *HealthzProvider) Initialized() {
111 | h.healthyAction()
112 | }
113 |
114 | func (h *HealthzProvider) SnapshotSent() {
115 | h.healthyAction()
116 | }
117 |
118 | func StartHealthz(cfg config.Config, log *logrus.Entry, exitCh chan error) func() {
119 | ctrlHealthz := newHealthzProvider(cfg, log)
120 | closeHealthz := runHealthzEndpoints(cfg, log, ctrlHealthz.Check, exitCh)
121 | return closeHealthz
122 | }
123 |
--------------------------------------------------------------------------------