This tutorial will guide you through:
- Storing verification key as a Kubernetes Secret
- Configuring ManifestIntegrityProfile to define which resource(s) should be protected
- Creating a sample resource without signature
- Generating the signature for the sample resource
- Creating a sample resource with signature
- [Detect mode] Checking the resource integrity status from the ManifestIntegrityState generated by observer
- [Enforce mode] Checking the denied requests as Kubernetes Event
Integrity Shield provides a phased approach. It allows users to use signature-based protection first in inform mode and then switch to enforcement mode.
Integrity Shield requires a secret that includes a pubkey for verifying signatures of resources that need to be protected. Integrity Shield supports X509, PGP or Sigstore key for signing resources. In this document, we use pgp signing.
Create a verification key as a Kubernetes Secret with the following command. Please check here to generate your own keypair.
gpg --export [email protected] > /tmp/pubring.gpg
kubectl create secret generic my-pubkey --from-file=key=/tmp/pubring.gpg -n integrity-shield-operator-system
If you do not have any PGP key or you want to use new key, generate new one and export it to a file. See this GitHub document.
A custom resource ManifestIntegrityConstraint
(MIC) includes the definition of which resources should be protected with signature in a cluster by Integrity Shield.
This constraint enables us to monitor three resources ConfigMap, Deployment, and Service in a namespace secure-ns
.
Set the appropriate signer to "signers" field and create the following ManifestIntegrityConstraint resource.
See Define Protected Resources for detailed specs.
cat <<EOF | kubectl apply -f -
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: ManifestIntegrityConstraint
metadata:
name: sample-constraint
spec:
match:
kinds:
- apiGroups:
- ""
kinds:
- ConfigMap
- Service
- apiGroups:
- "apps"
kinds:
- Deployment
namespaces:
- "secure-ns"
parameters:
action:
mode: inform
constraintName: sample-constraint
signers:
- [email protected]
keyConfigs:
- keySecretName: my-pubkey
keySecretNamespace: integrity-shield-operator-system
EOF
manifestintegrityconstraint.constraints.gatekeeper.sh/sample-constraint created
Run the following command to create a Yaml manifest for a sample configmap.
cat << EOF > /tmp/sample-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-cm
data:
key1: val1
key2: val2
comment: comment1
EOF
Create a sample configmap by the command below, and the configmap will be created because of inform mode.
$ kubectl apply -f /tmp/sample-cm.yaml -n secure-ns
configmap/sample-cm created
Check the audit result for all constraints on the cluster with this command.
you can see that some resources defined in sample-constraint are in invalid state because integrityshield.io/verifyResourceViolation
label is true.
$ kubectl get mis --show-labels -n integrity-shield-operator-system
NAME AGE LABELS
sample-constraint 2m1s integrityshield.io/verifyResourceIgnored=false,integrityshield.io/verifyResourceViolation=true
By checking the verification result on per constraint, you can see which resource is violated from ManifestIntegrityState.
$ kubectl get mis sample-constraint -n integrity-shield-operator-system -o yaml
apiVersion: apis.integrityshield.io/v1
kind: ManifestIntegrityState
metadata:
labels:
integrityshield.io/verifyResourceIgnored: "false"
integrityshield.io/verifyResourceViolation: "true"
name: sample-constraint
namespace: integrity-shield-operator-system
spec:
constraintName: sample-constraint
nonViolations: null
observationTime: "2021-10-14 13:04:55"
totalViolations: 1
violation: true
violations:
- apiGroup: ""
apiVersion: v1
kind: ConfigMap
name: sample-cm
namespace: secure-ns
result: 'failed to verify signature: failed to get signature: `cosign.sigstore.dev/message`
is not found in the annotations'
status: {}
Generate siganture with the following command. Please see this document.
$ ./scripts/gpg-annotation-sign.sh [email protected] /tmp/sample-cm.yaml
Check the signature is attached to the sample resource.
$ less /tmp/sample-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-cm
annotations:
integrityshield.io/message: H4sIAMmiZmEAAzXLMQqAMAyF4T...
integrityshield.io/signature: LS0tLS1CRUdJTiBQR1AgU...
data:
key1: val1
key2: val2
comment: comment1
Apply a sample configmap by the command below.
$ kubectl apply -f /tmp/sample-cm.yaml -n secure-ns
configmap/sample-cm created
Check the resource integrity status again. You will see that the label of integrityshield.io/verifyResourceViolation
has become false.
$ kubectl get mis --show-labels -n integrity-shield-operator-system
NAME AGE LABELS
sample-constraint 22m integrityshield.io/verifyResourceIgnored=false,integrityshield.io/verifyResourceViolation=false
Also, you can see sample-cm has no violation because it has a valid signature now.
$ kubectl get mis sample-constraint -n integrity-shield-operator-system -o yaml
apiVersion: apis.integrityshield.io/v1
kind: ManifestIntegrityState
metadata:
labels:
integrityshield.io/verifyResourceIgnored: "false"
integrityshield.io/verifyResourceViolation: "false"
name: sample-constraint
namespace: integrity-shield-operator-system
spec:
constraintName: sample-constraint
nonViolations:
- apiGroup: ""
apiVersion: ""
kind: ConfigMap
name: sample-cm
namespace: secure-ns
result: 'singed by a valid signer: [email protected]'
sigRef: __embedded_in_annotation__
signer: [email protected]
observationTime: "2021-10-14 13:24:54"
totalViolations: 0
violation: false
violations: null
status: {}
Now, let's switch to Enforce mode
.
To enable enforce mode, change mode: inform
to mode: enforce
, then create the ManifestIntegrityConstraint.
cat <<EOF | kubectl apply -f -
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: ManifestIntegrityConstraint
metadata:
name: sample-constraint
spec:
match:
kinds:
- apiGroups:
- ""
kinds:
- ConfigMap
- Service
- apiGroups:
- "apps"
kinds:
- Deployment
namespaces:
- "secure-ns"
parameters:
action:
mode: enforce
constraintName: sample-constraint
signers:
- [email protected]
keyConfigs:
- keySecretName: my-pubkey
keySecretNamespace: integrity-shield-operator-system
EOF
Run the following command to create a Yaml manifeset for a sample service.
cat << EOF > /tmp/sample-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: sample-service
spec:
selector:
app: SampleApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
EOF
Create a sample service by the command below, and the service will be blocked because the signature verification fails.
$ kubectl create -f /tmp/sample-svc.yaml -n secure-ns
Error from server ([sample-constraint] denied; {"allow": false, "message": "failed to verify signature: failed to get signature: `cosign.sigstore.dev/message` is not found in the annotations"}): error when creating "/tmp/sample-svc.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [sample-constraint] denied; {"allow": false, "message": "failed to verify signature: failed to get signature: `cosign.sigstore.dev/message` is not found in the annotations"}
Integrity Shield generates an Event when it does not allow the admission request. You can see denied requests as Kubernetes Event like below.
$ kubectl get event -n secure-ns --field-selector type=IntegrityShield
LAST SEEN TYPE REASON OBJECT MESSAGE
65s IntegrityShield Deny service/sample-service [sample-constraint]failed to verify signature: failed to get signature: `cosign.sigstore.dev/message` is not found in the annotations
Generate a signature for a resource. Run the following script to generate a signature.
$ ./scripts/gpg-annotation-sign.sh [email protected] /tmp/sample-svc.yaml
Then, run the same command again to create Service. It should be successful this time because the resource has valid siganture.
$ kubectl create -f /tmp/sample-svc.yaml -n secure-ns
service/sample-service created
You can confirm that all protected resources are valid.
kind: ManifestIntegrityState
metadata:
creationTimestamp: "2021-10-14T13:04:55Z"
generation: 18
labels:
integrityshield.io/verifyResourceIgnored: "false"
integrityshield.io/verifyResourceViolation: "false"
name: sample-constraint
namespace: integrity-shield-operator-system
spec:
constraintName: sample-constraint
nonViolations:
- apiGroup: ""
apiVersion: ""
kind: Service
name: sample-service
namespace: secure-ns
result: 'singed by a valid signer: [email protected]'
sigRef: __embedded_in_annotation__
signer: [email protected]
- apiGroup: ""
apiVersion: v1
kind: ConfigMap
name: sample-cm
namespace: secure-ns
result: 'singed by a valid signer: [email protected]'
sigRef: __embedded_in_annotation__
signer: [email protected]
observationTime: "2021-10-14 14:29:49"
totalViolations: 0
violation: false
violations: null
status: {}