apiVersion: project.openshift.io/v1 kind: Project metadata: annotations: argocd.argoproj.io/sync-wave: "0" openshift.io/description: "" openshift.io/display-name: "" labels: kubernetes.io/metadata.name: rhacs-operator name: rhacs-operator spec: finalizers: - kubernetes --- apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: annotations: argocd.argoproj.io/sync-wave: "5" name: rhacs-operator namespace: rhacs-operator spec: upgradeStrategy: Default --- apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: annotations: argocd.argoproj.io/sync-wave: "10" name: rhacs-operator namespace: rhacs-operator spec: channel: rhacs-4.0 installPlanApproval: Automatic name: rhacs-operator source: redhat-operators sourceNamespace: openshift-marketplace startingCSV: rhacs-operator.v4.0.1 --- apiVersion: project.openshift.io/v1 kind: Project metadata: annotations: argocd.argoproj.io/sync-wave: "15" openshift.io/description: "" openshift.io/display-name: "" labels: kubernetes.io/metadata.name: stackrox name: stackrox spec: finalizers: - kubernetes --- apiVersion: v1 kind: Secret metadata: annotations: argocd.argoproj.io/sync-wave: "15" name: central-admin namespace: stackrox type: Opaque data: password: {{ include "acs-admin-password" . | b64enc | quote }} --- apiVersion: platform.stackrox.io/v1alpha1 kind: Central metadata: annotations: argocd.argoproj.io/sync-wave: "15" argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true name: stackrox-central-services namespace: stackrox spec: central: exposure: loadBalancer: enabled: false port: 443 nodePort: enabled: false route: enabled: true adminPasswordSecret: name: central-admin db: isEnabled: Default persistence: persistentVolumeClaim: claimName: central-db persistence: persistentVolumeClaim: claimName: stackrox-db egress: connectivityPolicy: Online scanner: analyzer: scaling: autoScaling: Enabled maxReplicas: 5 minReplicas: 2 replicas: 3 scannerComponent: Enabled --- apiVersion: v1 kind: ServiceAccount metadata: annotations: argocd.argoproj.io/sync-wave: "20" name: stackrox-hook namespace: stackrox --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: annotations: argocd.argoproj.io/sync-wave: "20" name: stackrox-hook namespace: stackrox roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: edit subjects: - kind: ServiceAccount name: stackrox-hook namespace: stackrox --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: annotations: argocd.argoproj.io/sync-wave: "20" name: stackrox-hook-scc namespace: stackrox rules: - apiGroups: - security.openshift.io resourceNames: - anyuid resources: - securitycontextconstraints verbs: - use --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: annotations: argocd.argoproj.io/sync-wave: "20" name: stackrox-hook-scc namespace: stackrox roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: stackrox-hook-scc subjects: - kind: ServiceAccount name: stackrox-hook namespace: stackrox --- apiVersion: v1 kind: ConfigMap metadata: annotations: argocd.argoproj.io/sync-wave: "20" name: stackrox-init-hook namespace: stackrox data: configure-acs.sh: | #!/bin/bash set -Eeuo pipefail mkdir -p /tmp/bin curl -sfLo /tmp/bin/roxctl https://mirror.openshift.com/pub/rhacs/assets/4.0.0/bin/Linux/roxctl chmod 755 /tmp/bin/roxctl curl -sLo /tmp/bin/jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 chmod 755 /tmp/bin/jq export PATH="/tmp/bin:$PATH" echo "========================================================================" echo " Connecting to Red Hat ACS" echo "========================================================================" echo export ROX_CENTRAL_ADDRESS="$(oc get route central -n stackrox -o go-template='{{ "{{" }}.spec.host}}'):443" export ROX_CENTRAL_HOSTNAME="$ROX_CENTRAL_ADDRESS" while ! curl -sfko /dev/null "https://$ROX_CENTRAL_ADDRESS/"; do echo "Red Hat ACS not ready..." sleep 5 done echo "========================================================================" echo " Retrieving an API Token for Red Hat ACS" echo "========================================================================" echo if ! oc get secret stackrox-api-token -n stackrox &>/dev/null; then POLICY_JSON='{ "name": "init-token", "role":"Admin"}' APIURL="https://$ROX_CENTRAL_ADDRESS/v1/apitokens/generate" export ROX_API_TOKEN=$(curl -s -k -u admin:$ROX_ADMIN_PASSWORD -H 'Content-Type: application/json' -X POST -d "$POLICY_JSON" "$APIURL" | jq -r '.token') oc create secret generic stackrox-api-token -n stackrox --from-literal=token="$ROX_API_TOKEN" else export ROX_API_TOKEN="$(oc get secret stackrox-api-token -n stackrox -o go-template --template='{{ "{{" }}.data.token|base64decode}}')" fi echo "========================================================================" echo " Generating the Cluster Init Bundle" echo "========================================================================" echo if ! oc get secret admission-control-tls -n stackrox &>/dev/null; then roxctl -e "$ROX_CENTRAL_ADDRESS" central init-bundles generate local-cluster --output-secrets /tmp/cluster_init_bundle.yaml oc apply -f /tmp/cluster_init_bundle.yaml -n stackrox fi exit 0 --- apiVersion: v1 kind: ConfigMap metadata: annotations: argocd.argoproj.io/sync-wave: "20" name: stackrox-configure-hook namespace: stackrox data: entrypoint.sh: | #!/bin/bash set -Eeuo pipefail ansible-galaxy collection install community.general ansible-playbook configure.yaml exit 0 configure.yaml: | - name: Configure RHACS hosts: localhost gather_facts: no vars: ansible_connection: local acs_api: https://{{ central_hostname }}/v1 validate_certs: no tasks: - name: Get Stackrox central's Route kubernetes.core.k8s_info: api_version: route.openshift.io/v1 kind: Route name: central namespace: stackrox register: central_route failed_when: central_route.resources|length == 0 until: central_route is succeeded retries: 60 delay: 5 - set_fact: central_hostname: '{{ central_route.resources[0].spec.host }}:443' - name: Get Stackrox central's admin password kubernetes.core.k8s_info: api_version: v1 kind: Secret name: central-admin namespace: stackrox register: admin_secret failed_when: admin_secret.resources|length == 0 until: admin_secret is succeeded retries: 60 delay: 5 - set_fact: central_admin_password: '{{ admin_secret.resources[0].data.password | b64decode }}' - name: Get Cosign public key kubernetes.core.k8s_info: api_version: v1 kind: Secret name: code-signature namespace: fruits-dev register: cosign_secret failed_when: cosign_secret.resources|length == 0 until: cosign_secret is succeeded retries: 60 delay: 5 - set_fact: cosign_public_key: '{{ cosign_secret.resources[0].data["cosign.pub"] | b64decode }}' - name: Check if jmespath is available locally debug: msg={{ dummy|json_query('@') }} register: check_jmespath ignore_errors: yes vars: dummy: Hello World - name: Ensure JMESPath is installed assert: that: - 'check_jmespath is success' msg: > The JMESPath library is required by this playbook. Please install the JMESPath library with 'pip install jmespath'. - name: Find signature integrations uri: url: '{{ acs_api }}/signatureintegrations' validate_certs: '{{ validate_certs }}' url_username: admin url_password: '{{ central_admin_password }}' force_basic_auth: yes register: find_signature_integrations_response changed_when: false - set_fact: signature_integration_id: '{{ (find_signature_integrations_response.json | json_query(query) | first).id }}' when: find_signature_integrations_response.json | json_query(query) | count > 0 vars: query: integrations[?name == `Sigstore`] - name: Create the Cosign signature integration uri: url: '{{ acs_api }}/signatureintegrations' method: POST status_code: "200" validate_certs: '{{ validate_certs }}' url_username: admin url_password: '{{ central_admin_password }}' body: '{{ integration }}' body_format: json force_basic_auth: yes register: create_signature_integration_response changed_when: create_signature_integration_response.status == 200 when: signature_integration_id is not defined vars: integration: name: Sigstore cosign: publicKeys: - name: cosign.pub publicKeyPemEnc: '{{ cosign_public_key }}' - set_fact: signature_integration_id: '{{ create_signature_integration_response.json.id }}' when: signature_integration_id is not defined - debug: var: signature_integration_id - name: Find policies uri: url: '{{ acs_api }}/policies?query=Policy%3AImage%20is%20not%20signed' validate_certs: '{{ validate_certs }}' url_username: admin url_password: '{{ central_admin_password }}' force_basic_auth: yes register: find_policies_response changed_when: false - set_fact: policy_id: '{{ (find_policies_response.json.policies | first).id }}' when: find_policies_response.json.policies | count > 0 - name: Create the policy uri: url: '{{ acs_api }}/policies' method: POST status_code: "200" validate_certs: '{{ validate_certs }}' url_username: admin url_password: '{{ central_admin_password }}' body: '{{ policy }}' body_format: json force_basic_auth: yes register: create_policy_response changed_when: create_policy_response.status == 200 when: policy_id is not defined vars: policy: SORTEnforcement: no SORTLifecycleStage: '' SORTName: '' categories: - Security Best Practices criteriaLocked: no description: The container image has not been digitally signed. disabled: no enforcementActions: - SCALE_TO_ZERO_ENFORCEMENT - UNSATISFIABLE_NODE_CONSTRAINT_ENFORCEMENT eventSource: NOT_APPLICABLE exclusions: [] isDefault: no lifecycleStages: - DEPLOY mitreAttackVectors: [] mitreVectorsLocked: no name: Image is not signed notifiers: [] policySections: - policyGroups: - booleanOperator: OR fieldName: Image Signature Verified By negate: no values: - value: "{{ signature_integration_id }}" sectionName: Policy Section 1 policyVersion: '1.1' rationale: The container image MUST be digitally signed in order to prevent tampering. remediation: Use cosign to sign this image. See https://docs.sigstore.dev/cosign/signing_with_containers/ scope: - cluster: label: namespace: dev severity: CRITICAL_SEVERITY - set_fact: policy_id: '{{ create_policy_response.json.id }}' when: policy_id is not defined - debug: var: policy_id --- apiVersion: batch/v1 kind: Job metadata: annotations: argocd.argoproj.io/sync-wave: "20" name: stackrox-init-hook namespace: stackrox spec: backoffLimit: 30 template: spec: containers: - name: hook command: - /entrypoint/configure-acs.sh args: [] image: registry.redhat.io/openshift4/ose-cli:v4.13 imagePullPolicy: IfNotPresent env: - name: ROX_ADMIN_PASSWORD valueFrom: secretKeyRef: name: central-admin key: password - name: USER value: openshift - name: HOME value: /tmp volumeMounts: - mountPath: /entrypoint name: stackrox-hook readOnly: true serviceAccountName: stackrox-hook serviceAccount: stackrox-hook restartPolicy: OnFailure terminationGracePeriodSeconds: 30 volumes: - name: stackrox-hook configMap: name: stackrox-init-hook defaultMode: 0755 --- apiVersion: platform.stackrox.io/v1alpha1 kind: SecuredCluster metadata: annotations: argocd.argoproj.io/sync-wave: "30" argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true name: stackrox-secured-cluster-services namespace: stackrox spec: auditLogs: collection: Auto admissionControl: listenOnUpdates: true bypass: BreakGlassAnnotation contactImageScanners: ScanIfMissing listenOnCreates: true timeoutSeconds: 20 listenOnEvents: true scanner: analyzer: scaling: autoScaling: Enabled maxReplicas: 5 minReplicas: 2 replicas: 3 scannerComponent: AutoSense perNode: collector: collection: EBPF imageFlavor: Regular taintToleration: TolerateTaints clusterName: local-cluster --- apiVersion: batch/v1 kind: Job metadata: annotations: argocd.argoproj.io/sync-wave: "20" name: stackrox-configure-hook namespace: stackrox spec: backoffLimit: 30 template: spec: containers: - name: hook command: - /playbooks/entrypoint.sh args: [] image: registry.redhat.io/ansible-automation-platform-21/ee-supported-rhel8:1.0 imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /playbooks name: stackrox-hook readOnly: true workingDir: /playbooks serviceAccountName: stackrox-hook serviceAccount: stackrox-hook restartPolicy: OnFailure terminationGracePeriodSeconds: 30 volumes: - name: stackrox-hook configMap: name: stackrox-configure-hook defaultMode: 0755