|
|
@ -125,12 +125,45 @@ subjects: |
|
|
name: stackrox-hook |
|
|
name: stackrox-hook |
|
|
namespace: stackrox |
|
|
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 |
|
|
apiVersion: v1 |
|
|
kind: ConfigMap |
|
|
kind: ConfigMap |
|
|
metadata: |
|
|
metadata: |
|
|
annotations: |
|
|
annotations: |
|
|
argocd.argoproj.io/sync-wave: "20" |
|
|
argocd.argoproj.io/sync-wave: "20" |
|
|
name: stackrox-hook |
|
|
name: stackrox-init-hook |
|
|
namespace: stackrox |
|
|
namespace: stackrox |
|
|
data: |
|
|
data: |
|
|
configure-acs.sh: | |
|
|
configure-acs.sh: | |
|
|
@ -182,6 +215,211 @@ data: |
|
|
|
|
|
|
|
|
exit 0 |
|
|
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 |
|
|
apiVersion: batch/v1 |
|
|
kind: Job |
|
|
kind: Job |
|
|
metadata: |
|
|
metadata: |
|
|
@ -221,7 +459,7 @@ spec: |
|
|
volumes: |
|
|
volumes: |
|
|
- name: stackrox-hook |
|
|
- name: stackrox-hook |
|
|
configMap: |
|
|
configMap: |
|
|
name: stackrox-hook |
|
|
name: stackrox-init-hook |
|
|
defaultMode: 0755 |
|
|
defaultMode: 0755 |
|
|
--- |
|
|
--- |
|
|
apiVersion: platform.stackrox.io/v1alpha1 |
|
|
apiVersion: platform.stackrox.io/v1alpha1 |
|
|
@ -256,3 +494,36 @@ spec: |
|
|
imageFlavor: Regular |
|
|
imageFlavor: Regular |
|
|
taintToleration: TolerateTaints |
|
|
taintToleration: TolerateTaints |
|
|
clusterName: local-cluster |
|
|
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 |
|
|
|