DAST (ZAP) + SLSA provenance attestation
This commit is contained in:
parent
cc2549863d
commit
b8196fb18d
72
vars/cosignAttest.groovy
Normal file
72
vars/cosignAttest.groovy
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Sirf signature ye batata hai ki "image hamne banayi". Provenance attestation
|
||||||
|
// ye batata hai ki "kaunse commit se, kis machine par, kab, kis pipeline se".
|
||||||
|
// Audit me yahi sabse pehle poochha jaata hai.
|
||||||
|
def call(Map info) {
|
||||||
|
withCredentials([string(credentialsId: 'vault-deployer-token', variable: 'VAULT_TOKEN')]) {
|
||||||
|
withEnv([
|
||||||
|
"P_REPO=${info.repo}", "P_COMMIT=${info.commit}",
|
||||||
|
"P_BUILD=${info.build}", "P_JOB=${info.job}", "P_BUILDER=${info.builder}"
|
||||||
|
]) {
|
||||||
|
sh '''
|
||||||
|
set +x
|
||||||
|
umask 077
|
||||||
|
WORK=$(mktemp -d); trap "rm -rf $WORK" EXIT
|
||||||
|
|
||||||
|
curl -sf -H "X-Vault-Token: $VAULT_TOKEN" "$VAULT_ADDR/v1/platform/data/cosign" > $WORK/c.json
|
||||||
|
python3 - "$WORK" <<'PY'
|
||||||
|
import json, sys, os
|
||||||
|
w = sys.argv[1]
|
||||||
|
d = json.load(open(w + '/c.json'))['data']['data']
|
||||||
|
for k, f in (('private_key', 'cosign.key'), ('password', 'pw')):
|
||||||
|
open(os.path.join(w, f), 'w').write(d[k])
|
||||||
|
PY
|
||||||
|
curl -sf -H "X-Vault-Token: $VAULT_TOKEN" "$VAULT_ADDR/v1/$VAULT_MOUNT/data/registry" > $WORK/r.json
|
||||||
|
python3 - "$WORK" "$REGISTRY" <<'PY'
|
||||||
|
import base64, json, os, sys
|
||||||
|
w, reg = sys.argv[1], sys.argv[2]
|
||||||
|
d = json.load(open(w + '/r.json'))['data']['data']
|
||||||
|
auth = base64.b64encode(('%s:%s' % (d['username'], d['password'])).encode()).decode()
|
||||||
|
os.makedirs(os.path.expanduser('~/.docker'), exist_ok=True)
|
||||||
|
json.dump({'auths': {reg: {'auth': auth}}}, open(os.path.expanduser('~/.docker/config.json'), 'w'))
|
||||||
|
PY
|
||||||
|
|
||||||
|
# SLSA v0.2 predicate - kis source se kaise bani
|
||||||
|
python3 - "$WORK" <<'PY'
|
||||||
|
import json, os, sys, datetime
|
||||||
|
w = sys.argv[1]
|
||||||
|
pred = {
|
||||||
|
"builder": {"id": os.environ["P_BUILDER"]},
|
||||||
|
"buildType": "https://ns31240276.ip-51-195-4.eu/devops-platform/jenkins@v1",
|
||||||
|
"invocation": {
|
||||||
|
"configSource": {
|
||||||
|
"uri": os.environ["P_REPO"],
|
||||||
|
"digest": {"sha1": os.environ["P_COMMIT"]},
|
||||||
|
"entryPoint": os.environ["P_JOB"],
|
||||||
|
},
|
||||||
|
"environment": {"buildNumber": os.environ["P_BUILD"]},
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"buildInvocationId": os.environ["P_BUILD"],
|
||||||
|
"buildFinishedOn": datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||||
|
"completeness": {"parameters": True, "environment": False, "materials": True},
|
||||||
|
"reproducible": False,
|
||||||
|
},
|
||||||
|
"materials": [{
|
||||||
|
"uri": os.environ["P_REPO"],
|
||||||
|
"digest": {"sha1": os.environ["P_COMMIT"]},
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
open(w + '/predicate.json', 'w').write(json.dumps(pred, indent=2))
|
||||||
|
PY
|
||||||
|
|
||||||
|
COSIGN_PASSWORD=$(cat $WORK/pw) cosign attest \
|
||||||
|
--key $WORK/cosign.key --yes --tlog-upload=false \
|
||||||
|
--type slsaprovenance --predicate $WORK/predicate.json \
|
||||||
|
"$FULL_IMAGE"
|
||||||
|
rm -f ~/.docker/config.json
|
||||||
|
echo " provenance attach ho gaya: $FULL_IMAGE"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
vars/dastScan.groovy
Normal file
28
vars/dastScan.groovy
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Chalti hui app par hamla karke dekhta hai (dynamic testing).
|
||||||
|
// SAST code padhta hai, Trivy packages dekhta hai - ye dono wo nahi pakadte
|
||||||
|
// jo sirf running app me dikhta hai: missing security headers, khuli
|
||||||
|
// directory listing, cookie flags, etc.
|
||||||
|
def call() {
|
||||||
|
sh '''
|
||||||
|
set +e
|
||||||
|
CID=$(docker run -d --name "dast-$BUILD_NUMBER" "$FULL_IMAGE")
|
||||||
|
trap "docker rm -f $CID >/dev/null 2>&1" EXIT
|
||||||
|
sleep 6
|
||||||
|
|
||||||
|
mkdir -p zap-report && chmod 777 zap-report
|
||||||
|
|
||||||
|
# ZAP app ke hi network namespace me chalta hai - to 127.0.0.1 par
|
||||||
|
# app seedha mil jaata hai, koi extra network banane ki zarurat nahi.
|
||||||
|
docker run --rm \
|
||||||
|
--network "container:$CID" \
|
||||||
|
-v "$PWD/zap-report":/zap/wrk:rw \
|
||||||
|
"$IMG_ZAP" zap-baseline.py \
|
||||||
|
-t "http://127.0.0.1:${APP_PORT}" \
|
||||||
|
-r zap-report.html -J zap-report.json \
|
||||||
|
-I -m 2
|
||||||
|
RC=$?
|
||||||
|
echo " zap exit: $RC (0=clean, 1=FAIL mila, 2=warning only)"
|
||||||
|
exit $RC
|
||||||
|
'''
|
||||||
|
archiveArtifacts artifacts: 'zap-report/zap-report.*', allowEmptyArchive: true
|
||||||
|
}
|
||||||
@ -25,6 +25,7 @@ def call(Map cfg = [:]) {
|
|||||||
cfg.inventory = cfg.inventory ?: 'deploy/inventory.ini'
|
cfg.inventory = cfg.inventory ?: 'deploy/inventory.ini'
|
||||||
cfg.playbook = cfg.playbook ?: 'deploy/deploy.yml'
|
cfg.playbook = cfg.playbook ?: 'deploy/deploy.yml'
|
||||||
cfg.runSonar = cfg.containsKey('runSonar') ? cfg.runSonar : true
|
cfg.runSonar = cfg.containsKey('runSonar') ? cfg.runSonar : true
|
||||||
|
cfg.runDast = cfg.containsKey('runDast') ? cfg.runDast : true
|
||||||
cfg.runDeploy = cfg.containsKey('runDeploy') ? cfg.runDeploy : true
|
cfg.runDeploy = cfg.containsKey('runDeploy') ? cfg.runDeploy : true
|
||||||
cfg.approval = cfg.containsKey('approval') ? cfg.approval : true
|
cfg.approval = cfg.containsKey('approval') ? cfg.approval : true
|
||||||
cfg.gitops = cfg.containsKey('gitops') ? cfg.gitops : false
|
cfg.gitops = cfg.containsKey('gitops') ? cfg.gitops : false
|
||||||
@ -80,7 +81,8 @@ def call(Map cfg = [:]) {
|
|||||||
"HEALTH_PATH=${cfg.healthPath}", "INVENTORY=${cfg.inventory}",
|
"HEALTH_PATH=${cfg.healthPath}", "INVENTORY=${cfg.inventory}",
|
||||||
"PLAYBOOK=${cfg.playbook}", "IMG_GITLEAKS=${P.gitleaks}",
|
"PLAYBOOK=${cfg.playbook}", "IMG_GITLEAKS=${P.gitleaks}",
|
||||||
"IMG_TRIVY=${P.trivy}", "IMG_SYFT=${P.syft}",
|
"IMG_TRIVY=${P.trivy}", "IMG_SYFT=${P.syft}",
|
||||||
"IMG_SCANNER=${P.sonarScanner}", "TEST_IMAGE=${cfg.testImage}"
|
"IMG_SCANNER=${P.sonarScanner}", "TEST_IMAGE=${cfg.testImage}",
|
||||||
|
"IMG_ZAP=${P.zap}"
|
||||||
]
|
]
|
||||||
currentBuild.displayName = "#${env.BUILD_NUMBER} ${imageTag}"
|
currentBuild.displayName = "#${env.BUILD_NUMBER} ${imageTag}"
|
||||||
echo "module=${cfg.module} node=${env.NODE_NAME} branch=${branch} " +
|
echo "module=${cfg.module} node=${env.NODE_NAME} branch=${branch} " +
|
||||||
@ -172,6 +174,10 @@ assert r['version'] == '${IMAGE_TAG}', 'version mismatch'
|
|||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cfg.runDast) {
|
||||||
|
stage('DAST (ZAP)') { dastScan() }
|
||||||
|
}
|
||||||
|
|
||||||
stage('Push') {
|
stage('Push') {
|
||||||
withCredentials([string(credentialsId: 'vault-builder-token', variable: 'VAULT_TOKEN')]) {
|
withCredentials([string(credentialsId: 'vault-builder-token', variable: 'VAULT_TOKEN')]) {
|
||||||
sh '''
|
sh '''
|
||||||
@ -199,7 +205,18 @@ assert r['version'] == '${IMAGE_TAG}', 'version mismatch'
|
|||||||
// ============ Sign: controller par (key builder ko nahi milti) ============
|
// ============ Sign: controller par (key builder ko nahi milti) ============
|
||||||
if (!isRollback) {
|
if (!isRollback) {
|
||||||
node(P.deployLabel) {
|
node(P.deployLabel) {
|
||||||
withEnv(commonEnv) { stage('Sign image') { cosignSign() } }
|
withEnv(commonEnv) {
|
||||||
|
stage('Sign image') { cosignSign() }
|
||||||
|
stage('Provenance attest') {
|
||||||
|
cosignAttest([
|
||||||
|
repo : scm.userRemoteConfigs ? scm.userRemoteConfigs[0].url : 'unknown',
|
||||||
|
commit : env.GIT_COMMIT ?: 'unknown',
|
||||||
|
build : env.BUILD_NUMBER,
|
||||||
|
job : env.JOB_NAME,
|
||||||
|
builder: "https://ns31240276.ip-51-195-4.eu:8443/${env.JOB_NAME}"
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ def call() {
|
|||||||
gitleaks : 'zricethezav/gitleaks:v8.21.2',
|
gitleaks : 'zricethezav/gitleaks:v8.21.2',
|
||||||
trivy : 'aquasec/trivy:0.58.1',
|
trivy : 'aquasec/trivy:0.58.1',
|
||||||
syft : 'anchore/syft:v1.18.1',
|
syft : 'anchore/syft:v1.18.1',
|
||||||
sonarScanner: 'sonarsource/sonar-scanner-cli:11'
|
sonarScanner: 'sonarsource/sonar-scanner-cli:11',
|
||||||
|
zap : 'ghcr.io/zaproxy/zaproxy:2.16.1'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user