diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3b06348d..df05aaf0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -63,12 +63,16 @@ jobs: cd operator/ go fmt ./... go vet ./... + - name: Parse Tag + id: parse-tag + run: echo ::set-output name=version::${GITHUB_REF#refs/*/} - name: "Build'n Push Operator" uses: docker/build-push-action@v1 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: scbexperimental/operator + build_args: VERSION=`${{ steps.parse-tag.outputs.version }} tag_with_ref: true tag_with_sha: true path: ./operator/ diff --git a/operator/Dockerfile b/operator/Dockerfile index 6b5600ec..202cb7df 100644 --- a/operator/Dockerfile +++ b/operator/Dockerfile @@ -13,6 +13,7 @@ RUN go mod download COPY main.go main.go COPY apis/ apis/ COPY controllers/ controllers/ +COPY internal/ internal/ COPY utils/ utils/ # Build @@ -21,6 +22,11 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot + +ARG VERSION=unkown +ENV VERSION ENV ${BRANCH} +ENV TELEMETRY_ENABLED "true" + WORKDIR / COPY --from=builder /workspace/manager . USER nonroot:nonroot diff --git a/operator/internal/telemetry/telemetry.go b/operator/internal/telemetry/telemetry.go new file mode 100644 index 00000000..db2c47eb --- /dev/null +++ b/operator/internal/telemetry/telemetry.go @@ -0,0 +1,101 @@ +package telemetry + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + "os" + "time" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/go-logr/logr" + executionv1 "github.com/secureCodeBox/secureCodeBox-v2/operator/apis/execution/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var telemetryInterval = 24 * time.Hour + +// officialScanTypes contains the list of official secureCodeBox Scan Types. +// Unofficial Scan Types should be reported as "other" to avoid leakage of confidential data via the scan-types name +var officialScanTypes map[string]bool = map[string]bool{ + "amass": true, + "kube-hunter": true, + "kubeaudit": true, + "ncrack": true, + "nikto": true, + "nmap": true, + "ssh-scan": true, + "sslyze": true, + "trivy": true, + "wpscan": true, + "zap-baseline": true, + "zap-api-scan": true, + "zap-full-scan": true, +} + +// telemetryData submitted by operator +type telemetryData struct { + Version string `json:"version"` + InstalledScanTypes []string `json:"installedScanTypes"` +} + +// Loop Submits Telemetry Data in a regular interval +func Loop(apiClient client.Client, log logr.Logger) { + log.Info("The Operator sends anonymous telemetry data, to give the team an overview how much the secureCodeBox is used. Find out more at https://www.securecodebox.io/telemetry") + + // Wait until controller cache is initialized + time.Sleep(10 * time.Second) + + for { + var version string + if envVersion, ok := os.LookupEnv("VERSION"); ok { + version = envVersion + } else { + version = "unkown" + } + + ctx := context.Background() + + installedScanTypes := map[string]bool{} + var scanTypes executionv1.ScanTypeList + err := apiClient.List(ctx, &scanTypes, client.InNamespace(metav1.NamespaceAll)) + + if err != nil { + log.Error(err, "Failed to list ScanTypes") + } + for _, scanType := range scanTypes.Items { + installedScanTypes[scanType.Name] = true + } + + installedScanTypesList := []string{} + for key := range installedScanTypes { + if _, ok := officialScanTypes[key]; ok { + installedScanTypesList = append(installedScanTypesList, key) + } else { + installedScanTypesList = append(installedScanTypesList, "other") + } + } + + log.Info("Submitting Anonymous Telemetry Data", "Version", version, "InstalledScanTypes", installedScanTypesList) + + reqBody, err := json.Marshal(telemetryData{ + Version: version, + InstalledScanTypes: installedScanTypesList, + }) + + if err != nil { + log.Error(err, "Failed to encode telemetry data to json") + } + response, err := http.Post("https://telemetry.chase.securecodebox.io/v1/submit", "application/json", bytes.NewBuffer(reqBody)) + if err != nil { + log.Error(err, "Failed to send telemetry data") + } + if response != nil { + response.Body.Close() + } + + time.Sleep(telemetryInterval) + } +} diff --git a/operator/main.go b/operator/main.go index ac4dc3e8..ef84fa47 100644 --- a/operator/main.go +++ b/operator/main.go @@ -30,6 +30,7 @@ import ( executionv1 "github.com/secureCodeBox/secureCodeBox-v2/operator/apis/execution/v1" executioncontroller "github.com/secureCodeBox/secureCodeBox-v2/operator/controllers/execution" scancontroller "github.com/secureCodeBox/secureCodeBox-v2/operator/controllers/execution/scans" + "github.com/secureCodeBox/secureCodeBox-v2/operator/internal/telemetry" // +kubebuilder:scaffold:imports ) @@ -87,6 +88,10 @@ func main() { } // +kubebuilder:scaffold:builder + if enabled, ok := os.LookupEnv("TELEMETRY_ENABLED"); ok && enabled == "true" { + go telemetry.Loop(mgr.GetClient(), ctrl.Log.WithName("telemetry")) + } + setupLog.Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { setupLog.Error(err, "problem running manager") diff --git a/operator/templates/NOTES.txt b/operator/templates/NOTES.txt new file mode 100644 index 00000000..98256c15 --- /dev/null +++ b/operator/templates/NOTES.txt @@ -0,0 +1,15 @@ +secureCodeBox Operator Deployed 🚀 + +The operator can orchestrate the execution of various security scanning tools inside of your cluster. +You can find a list of all officially supported scanners here: https://www.securecodebox.io/integrations/ +The website also lists other integrations, like persisting scan results to DefectDojo or Elasticsearch. + +{{ if .Values.telemetryEnabled -}} +The operator send out regular telemetry pings to a central service. +This lets us, the secureCodeBox team, get a grasp on how much the secureCodeBox is used. +The submitted data is chosen to be as anonymous as possible. +You can find a complete report of the data submitted and links to the source-code at: https://www.securecodebox.io/telemetry +The first ping is send one hour after the install, you can prevent this by upgrading the chart and setting `telemetryEnabled` to `false`. +{{ else -}} +Telemetry data collection has been disabled. +{{ end -}} \ No newline at end of file diff --git a/operator/templates/manager/manager.yaml b/operator/templates/manager/manager.yaml index 2ee84703..721f70d1 100644 --- a/operator/templates/manager/manager.yaml +++ b/operator/templates/manager/manager.yaml @@ -28,6 +28,8 @@ spec: imagePullPolicy: {{ .Values.image.pullPolicy }} name: manager env: + - name: TELEMETRY_ENABLED + value: {{ .Values.telemetryEnabled | quote }} # TODO: integrate with cert manager and auto gen a cert for minio {{- if .Values.minio.enabled }} - name: S3_USE_SSL diff --git a/operator/values.yaml b/operator/values.yaml index 2a6e396d..c2e7a44d 100644 --- a/operator/values.yaml +++ b/operator/values.yaml @@ -2,6 +2,9 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# telemetryEnabled -- The Operator sends anonymous telemetry data, to give the team an overview how much the secureCodeBox is used. Find out more at https://www.securecodebox.io/telemetry +telemetryEnabled: true + image: registry: docker.io repository: scbexperimental/operator @@ -45,4 +48,4 @@ resources: memory: 30Mi requests: cpu: 100m - memory: 20Mi \ No newline at end of file + memory: 20Mi