From 99b9c24b0afc0673a7defe0ea928d71ad92bad24 Mon Sep 17 00:00:00 2001 From: Zachary Winnerman <98712682+zwinnerman-fleetdm@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:35:40 -0500 Subject: [PATCH] Vulnerabilities run via crontab job (#9938) # Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Documented any API changes (docs/Using-Fleet/REST-API.md or docs/Contributing/API-for-contributors.md) - [ ] Documented any permissions changes - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)). --------- Co-authored-by: Benjamin Edwards --- .../fleet/templates/cronjobs.yaml | 344 ++++++++++++++++++ .../fleet/templates/deployment.yaml | 6 +- .../lambda/deploy_terraform/fleet/values.yaml | 3 + .../lambda/deploy_terraform/main.tf | 12 +- 4 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/templates/cronjobs.yaml diff --git a/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/templates/cronjobs.yaml b/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/templates/cronjobs.yaml new file mode 100644 index 0000000000..4563b9c0a3 --- /dev/null +++ b/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/templates/cronjobs.yaml @@ -0,0 +1,344 @@ +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + labels: + app: fleet + chart: fleet + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + name: {{ .Values.fleetName }} + namespace: {{ .Release.Namespace }} +spec: + schedule: "{{ .Values.crons.vulnerabilities }}" + jobTemplate: + spec: + template: + spec: + restartPolicy: Never + containers: + - name: {{ .Values.fleetName }} + imagePullPolicy: Always + command: [/usr/bin/fleet] + args: ["vuln_processing"] + image: {{ .Values.imageRepo }}:{{ .Values.imageTag }} + ports: + - name: {{ .Values.fleetName }} + containerPort: {{ .Values.fleet.listenPort }} + resources: + limits: + cpu: {{ .Values.resources.limits.cpu }} + memory: {{ .Values.resources.limits.memory }} + requests: + cpu: {{ .Values.resources.requests.cpu }} + memory: {{ .Values.resources.requests.memory }} + env: + ## BEGIN FLEET SECTION + - name: FLEET_SERVER_SANDBOX_ENABLED + value: "1" + - name: FLEET_LICENSE_ENFORCE_HOST_LIMIT + value: "true" + - name: FLEET_VULNERABILITIES_DATABASES_PATH + value: /tmp/vuln + {{- if ne .Values.packaging.enrollSecret "" }} + - name: FLEET_PACKAGING_GLOBAL_ENROLL_SECRET + value: "{{ .Values.packaging.enrollSecret }}" + - name: FLEET_PACKAGING_S3_BUCKET + value: "{{ .Values.packaging.s3.bucket }}" + - name: FLEET_PACKAGING_S3_PREFIX + value: "{{ .Values.packaging.s3.prefix }}" + {{- end }} + - name: FLEET_SERVER_ADDRESS + value: "0.0.0.0:{{ .Values.fleet.listenPort }}" + - name: FLEET_AUTH_BCRYPT_COST + value: "{{ .Values.fleet.auth.bcryptCost }}" + - name: FLEET_AUTH_SALT_KEY_SIZE + value: "{{ .Values.fleet.auth.saltKeySize }}" + - name: FLEET_APP_TOKEN_KEY_SIZE + value: "{{ .Values.fleet.app.tokenKeySize }}" + - name: FLEET_APP_TOKEN_VALIDITY_PERIOD + value: "{{ .Values.fleet.app.inviteTokenValidityPeriod }}" + - name: FLEET_SESSION_KEY_SIZE + value: "{{ .Values.fleet.session.keySize }}" + - name: FLEET_SESSION_DURATION + value: "{{ .Values.fleet.session.duration }}" + - name: FLEET_LOGGING_DEBUG + value: "{{ .Values.fleet.logging.debug }}" + - name: FLEET_LOGGING_JSON + value: "{{ .Values.fleet.logging.json }}" + - name: FLEET_LOGGING_DISABLE_BANNER + value: "{{ .Values.fleet.logging.disableBanner }}" + - name: FLEET_SERVER_TLS + value: "{{ .Values.fleet.tls.enabled }}" + {{- if .Values.fleet.tls.enabled }} + - name: FLEET_SERVER_TLS_COMPATIBILITY + value: "{{ .Values.fleet.tls.compatibility }}" + - name: FLEET_SERVER_CERT + value: "/secrets/tls/{{ .Values.fleet.tls.certSecretKey }}" + - name: FLEET_SERVER_KEY + value: "/secrets/tls/{{ .Values.fleet.tls.keySecretKey }}" + {{- end }} + {{- if ne .Values.fleet.carving.s3.bucketName "" }} + - name: FLEET_S3_BUCKET + value: "{{ .Values.fleet.carving.s3.bucketName }}" + - name: FLEET_S3_PREFIX + value: "{{ .Values.fleet.carving.s3.prefix }}" + {{- if ne .Values.fleet.carving.s3.accessKeyID "" }} + - name: FLEET_S3_ACCESS_KEY_ID + value: "{{ .Values.fleet.carving.s3.accessKeyID }}" + - name: FLEET_S3_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: "{{ .Values.fleet.secretName }}" + key: "{{ .Values.fleet.carving.s3.secretKey }}" + {{ else }} + - name: FLEET_S3_STS_ASSUME_ROLE_ARN + value: "{{ .Values.fleet.carving.s3.stsAssumeRoleARN }}" + {{- end }} + {{- end }} + ## END FLEET SECTION + ## BEGIN MYSQL SECTION + - name: FLEET_MYSQL_ADDRESS + value: "{{ .Values.mysql.address }}" + - name: FLEET_MYSQL_DATABASE + value: "{{ .Values.mysql.database }}" + - name: FLEET_MYSQL_USERNAME + value: "{{ .Values.mysql.username }}" + - name: FLEET_MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.mysql.secretName }} + key: {{ .Values.mysql.passwordKey }} + - name: FLEET_MYSQL_MAX_OPEN_CONNS + value: "{{ .Values.mysql.maxOpenConns }}" + - name: FLEET_MYSQL_MAX_IDLE_CONNS + value: "{{ .Values.mysql.maxIdleConns }}" + - name: FLEET_MYSQL_CONN_MAX_LIFETIME + value: "{{ .Values.mysql.connMaxLifetime }}" + {{- if .Values.mysql.tls.enabled }} + - name: FLEET_MYSQL_TLS_CA + value: "/secrets/mysql/{{ .Values.mysql.tls.caCertKey }}" + - name: FLEET_MYSQL_TLS_CERT + value: "/secrets/mysql/{{ .Values.mysql.tls.certKey }}" + - name: FLEET_MYSQL_TLS_KEY + value: "/secrets/mysql/{{ .Values.mysql.tls.keyKey }}" + - name: FLEET_MYSQL_TLS_CONFIG + value: "{{ .Values.mysql.tls.config }}" + - name: FLEET_MYSQL_TLS_SERVER_NAME + value: "{{ .Values.mysql.tls.serverName }}" + {{- end }} + ## END MYSQL SECTION + ## BEGIN REDIS SECTION + - name: FLEET_REDIS_ADDRESS + value: "{{ .Values.redis.address }}" + - name: FLEET_REDIS_DATABASE + value: "{{ .Values.redis.database }}" + {{- if .Values.redis.usePassword }} + - name: FLEET_REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: "{{ .Values.redis.secretName }}" + key: "{{ .Values.redis.passwordKey }}" + {{- end }} + ## END REDIS SECTION + ## BEGIN OSQUERY SECTION + - name: FLEET_OSQUERY_NODE_KEY_SIZE + value: "{{ .Values.osquery.nodeKeySize }}" + - name: FLEET_OSQUERY_LABEL_UPDATE_INTERVAL + value: "{{ .Values.osquery.labelUpdateInterval }}" + - name: FLEET_OSQUERY_DETAIL_UPDATE_INTERVAL + value: "{{ .Values.osquery.detailUpdateInterval }}" + - name: FLEET_OSQUERY_STATUS_LOG_PLUGIN + value: "{{ .Values.osquery.logging.statusPlugin }}" + - name: FLEET_OSQUERY_RESULT_LOG_PLUGIN + value: "{{ .Values.osquery.logging.resultPlugin }}" + {{- if eq .Values.osquery.logging.statusPlugin "filesystem" }} + - name: FLEET_FILESYSTEM_STATUS_LOG_FILE + value: "/logs/{{ .Values.osquery.logging.filesystem.statusLogFile }}" + {{- end }} + {{- if eq .Values.osquery.logging.resultPlugin "filesystem" }} + - name: FLEET_FILESYSTEM_RESULT_LOG_FILE + value: "/logs/{{ .Values.osquery.logging.filesystem.resultLogFile }}" + {{- end }} + {{- if or (eq .Values.osquery.logging.statusPlugin "filesystem") (eq .Values.osquery.logging.resultPlugin "filesystem") }} + - name: FLEET_FILESYSTEM_ENABLE_LOG_ROTATION + value: "{{ .Values.osquery.logging.filesystem.enableRotation }}" + - name: FLEET_FILESYSTEM_ENABLE_LOG_COMPRESSION + value: "{{ .Values.osquery.logging.filesystem.enableCompression }}" + {{- end }} + + {{- if or (eq .Values.osquery.logging.statusPlugin "firehose") (eq .Values.osquery.logging.resultPlugin "firehose") }} + - name: FLEET_FIREHOSE_REGION + value: "{{ .Values.osquery.logging.firehose.region }}" + {{- if eq .Values.osquery.logging.statusPlugin "firehose" }} + - name: FLEET_FIREHOSE_STATUS_STREAM + value: "{{ .Values.osquery.logging.firehose.statusStream }}" + {{- end }} + {{- if eq .Values.osquery.logging.resultPlugin "firehose" }} + - name: FLEET_FIREHOSE_RESULT_STREAM + value: "{{ .Values.osquery.logging.firehose.resultStream }}" + {{- end }} + {{- if ne .Values.osquery.logging.firehose.accessKeyID "" }} + - name: FLEET_FIREHOSE_ACCESS_KEY_ID + value: "{{ .Values.osquery.logging.firehose.accessKeyID }}" + - name: FLEET_FIREHOSE_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: "{{ .Values.osquery.secretName }}" + key: "{{ .Values.osquery.logging.firehose.secretKey }}" + {{ else }} + - name: FLEET_FIREHOSE_STS_ASSUME_ROLE_ARN + value: "{{ .Values.osquery.logging.firehose.stsAssumeRoleARN }}" + {{- end }} + {{- end }} + + {{- if or (eq .Values.osquery.logging.statusPlugin "kinesis") (eq .Values.osquery.logging.resultPlugin "kinesis") }} + - name: FLEET_KINESIS_REGION + value: "{{ .Values.osquery.logging.kinesis.region }}" + {{- if eq .Values.osquery.logging.statusPlugin "kinesis" }} + - name: FLEET_KINESIS_STATUS_STREAM + value: "{{ .Values.osquery.logging.kinesis.statusStream }}" + {{- end }} + {{- if eq .Values.osquery.logging.resultPlugin "kinesis" }} + - name: FLEET_KINESIS_RESULT_STREAM + value: "{{ .Values.osquery.logging.kinesis.resultStream }}" + {{- end }} + {{- if ne .Values.osquery.logging.kinesis.accessKeyID "" }} + - name: FLEET_KINESIS_ACCESS_KEY_ID + value: "{{ .Values.osquery.logging.kinesis.accessKeyID }}" + - name: FLEET_KINESIS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: "{{ .Values.osquery.secretName }}" + key: "{{ .Values.osquery.logging.kinesis.secretKey }}" + {{ else }} + - name: FLEET_KINESIS_STS_ASSUME_ROLE_ARN + value: "{{ .Values.osquery.logging.kinesis.stsAssumeRoleARN }}" + {{- end }} + {{- end }} + + {{- if or (eq .Values.osquery.logging.statusPlugin "lambda") (eq .Values.osquery.logging.resultPlugin "lambda") }} + - name: FLEET_LAMBDA_REGION + value: "{{ .Values.osquery.logging.lambda.region }}" + {{- if eq .Values.osquery.logging.statusPlugin "lambda" }} + - name: FLEET_LAMBDA_STATUS_FUNCTION + value: "{{ .Values.osquery.logging.lambda.statusFunction }}" + {{- end }} + {{- if eq .Values.osquery.logging.resultPlugin "lambda" }} + - name: FLEET_LAMBDA_RESULT_FUNCTION + value: "{{ .Values.osquery.logging.lambda.resultFunction }}" + {{- end }} + {{- if ne .Values.osquery.logging.lambda.accessKeyID "" }} + - name: FLEET_LAMBDA_ACCESS_KEY_ID + value: "{{ .Values.osquery.logging.lambda.accessKeyID }}" + - name: FLEET_LAMBDA_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: "{{ .Values.osquery.secretName }}" + key: "{{ .Values.osquery.logging.lambda.secretKey }}" + {{ else }} + - name: FLEET_LAMBDA_STS_ASSUME_ROLE_ARN + value: "{{ .Values.osquery.logging.lambda.stsAssumeRoleARN }}" + {{- end }} + {{- end }} + + + {{- if or (eq .Values.osquery.logging.statusPlugin "pubsub") (eq .Values.osquery.logging.resultPlugin "pubsub") }} + - name: FLEET_PUBSUB_PROJECT + value: "{{ .Values.osquery.logging.pubsub.project }}" + {{- end }} + {{- if eq .Values.osquery.logging.statusPlugin "pubsub" }} + - name: FLEET_PUBSUB_STATUS_TOPIC + value: "{{ .Values.osquery.logging.pubsub.statusTopic }}" + {{- end }} + {{- if eq .Values.osquery.logging.resultPlugin "pubsub" }} + - name: FLEET_PUBSUB_RESULT_TOPIC + value: "{{ .Values.osquery.logging.pubsub.resultTopic }}" + {{- end }} + ## END OSQUERY SECTION + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + privileged: false + readOnlyRootFilesystem: true + runAsGroup: 3333 + runAsUser: 3333 + runAsNonRoot: true + livenessProbe: + httpGet: + path: /healthz + port: {{ .Values.fleet.listenPort }} + timeoutSeconds: 10 + readinessProbe: + httpGet: + path: /healthz + port: {{ .Values.fleet.listenPort }} + timeoutSeconds: 10 + {{- if or (.Values.fleet.tls.enabled) (.Values.mysql.tls.enabled) (eq .Values.osquery.logging.statusPlugin "filesystem") (eq .Values.osquery.logging.resultPlugin "filesystem") }} + volumeMounts: + {{- if .Values.fleet.tls.enabled }} + - name: {{ .Values.fleetName }}-tls + readOnly: true + mountPath: /secrets/tls + {{- end }} + {{- if .Values.mysql.tls.enabled }} + - name: mysql-tls + readOnly: true + mountPath: /secrets/mysql + {{- end }} + {{- if or (eq .Values.osquery.logging.statusPlugin "filesystem") (eq .Values.osquery.logging.resultPlugin "filesystem") }} + - name: osquery-logs + mountPath: /logs + {{- end }} + - name: tmp + mountPath: /tmp + {{- end }} + {{- if .Values.gke.cloudSQL.enableProxy }} + - name: cloudsql-proxy + image: "gcr.io/cloudsql-docker/gce-proxy:{{ .Values.gke.cloudSQL.imageTag }}" + command: + - "/cloud_sql_proxy" + - "-verbose={{ .Values.gke.cloudSQL.verbose}}" + - "-instances={{ .Values.gke.cloudSQL.instanceName }}=tcp:3306" + resources: + limits: + cpu: 0.5 # 500Mhz + memory: 150Mi + requests: + cpu: 0.1 # 100Mhz + memory: 50Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + privileged: false + readOnlyRootFilesystem: true + runAsGroup: 3333 + runAsUser: 3333 + runAsNonRoot: true + {{- end }} + hostPID: false + hostNetwork: false + hostIPC: false + serviceAccountName: {{ .Values.fleetName }} + {{- if or (.Values.fleet.tls.enabled) (.Values.mysql.tls.enabled) (eq .Values.osquery.logging.statusPlugin "filesystem") (eq .Values.osquery.logging.resultPlugin "filesystem") }} + volumes: + {{- if .Values.fleet.tls.enabled }} + - name: {{ .Values.fleetName }}-tls + secret: + secretName: "{{ .Values.fleet.secretName }}" + {{- end }} + {{- if .Values.mysql.tls.enabled }} + - name: mysql-tls + secret: + secretName: "{{ .Values.mysql.secretName }}" + {{- end }} + {{- if or (eq .Values.osquery.logging.statusPlugin "filesystem") (eq .Values.osquery.logging.resultPlugin "filesystem") }} + - name: osquery-logs + emptyDir: + sizeLimit: "{{ .Values.osquery.logging.filesystem.volumeSize }}" + {{- end }} + - name: tmp + emptyDir: + {{- end }} diff --git a/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/templates/deployment.yaml b/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/templates/deployment.yaml index 39ef4e3b38..eebdd2bd36 100644 --- a/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/templates/deployment.yaml +++ b/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/templates/deployment.yaml @@ -50,10 +50,12 @@ spec: memory: {{ .Values.resources.requests.memory }} env: ## BEGIN FLEET SECTION + - name: FLEET_VULNERABILITIES_EXTERNAL_SCHEDULED + value: "true" + - name: FLEET_SESSION_DURATION + value: "1y" - name: FLEET_SERVER_SANDBOX_ENABLED value: "1" - - name: FLEET_VULNERABILITIES_PERIODICITY - value: "15m" - name: FLEET_LICENSE_ENFORCE_HOST_LIMIT value: "true" - name: FLEET_VULNERABILITIES_DATABASES_PATH diff --git a/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/values.yaml b/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/values.yaml index df65d39fb9..4a3277e9b5 100644 --- a/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/values.yaml +++ b/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/fleet/values.yaml @@ -187,3 +187,6 @@ gke: useManagedCertificate: false # Workload Identity allows the K8s service account to assume the IAM permissions of a GCP service account workloadIdentityEmail: "" + +crons: + vulnerabilities: "0,15,30,45 * * * *" diff --git a/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/main.tf b/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/main.tf index 06adb73da4..5056d3e3c5 100644 --- a/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/main.tf +++ b/infrastructure/sandbox/PreProvisioner/lambda/deploy_terraform/main.tf @@ -81,6 +81,11 @@ resource "random_password" "db" { length = 8 } +resource "random_integer" "cron_offset" { + min = 0 + max = 14 +} + resource "helm_release" "main" { name = terraform.workspace chart = "${path.module}/fleet" @@ -157,7 +162,7 @@ resource "helm_release" "main" { set { name = "imageTag" - value = "v4.27.1" + value = "v4.26.0-1" } set { @@ -184,6 +189,11 @@ resource "helm_release" "main" { name = "serviceAccountAnnotations.eks\\.amazonaws\\.com/role-arn" value = aws_iam_role.main.arn } + + set { + name = "crons.vulnerabilities" + value = "${random_integer.cron_offset.result}\\,${random_integer.cron_offset.result + 15}\\,${random_integer.cron_offset.result + 30}\\,${random_integer.cron_offset.result + 45} * * * *" + } } data "aws_iam_policy_document" "main" {