Browse Source

wip

pull/1/head
Nicolas Massé 3 years ago
parent
commit
c2e02b56dc
  1. 24
      infrastructure/files/cosign-hook/cosign.sh
  2. 186
      infrastructure/files/stackrox-configure-hook/configure.yaml
  3. 8
      infrastructure/files/stackrox-configure-hook/entrypoint.sh
  4. 47
      infrastructure/files/stackrox-init-hook/configure-acs.sh
  5. 246
      infrastructure/templates/acs.yaml
  6. 26
      infrastructure/templates/fruits-dev.yaml

24
infrastructure/files/cosign-hook/cosign.sh

@ -0,0 +1,24 @@
#!/bin/bash
set -Eeuo pipefail
mkdir -p /tmp/bin
curl -sfLo /tmp/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.0.2/cosign-linux-amd64
chmod 755 /tmp/bin/cosign
export PATH="/tmp/bin:$PATH"
if ! oc get secret code-signature -n fruits-dev &>/dev/null; then
echo "========================================================================"
echo " Generating a keypair"
echo "========================================================================"
echo
## Move to /tmp before creating the keypair because of:
# Error: open cosign.pub: permission denied
# main.go:74: error during command execution: open cosign.pub: permission denied
cd /tmp
COSIGN_PASSWORD=dummy cosign generate-key-pair k8s://fruits-dev/code-signature
fi
exit 0

186
infrastructure/files/stackrox-configure-hook/configure.yaml

@ -0,0 +1,186 @@
- 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

8
infrastructure/files/stackrox-configure-hook/entrypoint.sh

@ -0,0 +1,8 @@
#!/bin/bash
set -Eeuo pipefail
ansible-galaxy collection install community.general
ansible-playbook configure.yaml
exit 0

47
infrastructure/files/stackrox-init-hook/configure-acs.sh

@ -0,0 +1,47 @@
#!/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

246
infrastructure/templates/acs.yaml

@ -166,54 +166,7 @@ metadata:
name: stackrox-init-hook name: stackrox-init-hook
namespace: stackrox namespace: stackrox
data: data:
configure-acs.sh: | {{ (.Files.Glob "files/stackrox-init-hook/*").AsConfig | indent 2 }}
#!/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 apiVersion: v1
kind: ConfigMap kind: ConfigMap
@ -223,202 +176,7 @@ metadata:
name: stackrox-configure-hook name: stackrox-configure-hook
namespace: stackrox namespace: stackrox
data: data:
entrypoint.sh: | {{ (.Files.Glob "files/stackrox-configure-hook/*").AsConfig | indent 2 }}
#!/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

26
infrastructure/templates/fruits-dev.yaml

@ -73,31 +73,7 @@ metadata:
name: cosign-hook name: cosign-hook
namespace: fruits-dev namespace: fruits-dev
data: data:
cosign.sh: | {{ (.Files.Glob "files/cosign-hook/*").AsConfig | indent 2 }}
#!/bin/bash
set -Eeuo pipefail
mkdir -p /tmp/bin
curl -sfLo /tmp/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.0.2/cosign-linux-amd64
chmod 755 /tmp/bin/cosign
export PATH="/tmp/bin:$PATH"
if ! oc get secret code-signature -n fruits-dev &>/dev/null; then
echo "========================================================================"
echo " Generating a keypair"
echo "========================================================================"
echo
## Move to /tmp before creating the keypair because of:
# Error: open cosign.pub: permission denied
# main.go:74: error during command execution: open cosign.pub: permission denied
cd /tmp
COSIGN_PASSWORD=dummy cosign generate-key-pair k8s://fruits-dev/code-signature
fi
exit 0
--- ---
apiVersion: batch/v1 apiVersion: batch/v1
kind: Job kind: Job

Loading…
Cancel
Save