diff --git a/.github/workflows/SAST-Kubesec.yml b/.github/workflows/SAST-Kubesec.yml new file mode 100644 index 0000000..240f559 --- /dev/null +++ b/.github/workflows/SAST-Kubesec.yml @@ -0,0 +1,54 @@ +# https://kubesec.io/ +# https://github.com/controlplaneio/kubesec +# https://github.com/controlplaneio/kubesec-action + +name: SAST Kubesec Scanner + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '18 8 * * 6' + +permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + +jobs: + kubesec-scan: + name: Kubesec Scan + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run kubesec scanner (critical-double.yaml) + uses: controlplaneio/kubesec-action@43d0ddff5ffee89a6bb9f29b64cd865411137b14 + with: + input: manifests/critical-double.yaml # specify configuration file to scan here + format: template + template: manifests/sarif.tpl + output: kubesec-results.sarif + exit-code: "0" + + - name: Upload Kubesec scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: kubesec-results.sarif + + - name: Run kubesec scanner (score-5-pod-serviceaccount.yaml) + uses: controlplaneio/kubesec-action@43d0ddff5ffee89a6bb9f29b64cd865411137b14 + with: + input: manifests/score-5-pod-serviceaccount.yaml # specify configuration file to scan here + format: template + template: manifests/sarif.tpl + output: kubesec-results.sarif + exit-code: "0" + + - name: Upload Kubesec scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: kubesec-results.sarif \ No newline at end of file diff --git a/manifests/critical-double.yaml b/manifests/critical-double.yaml new file mode 100644 index 0000000..390aded --- /dev/null +++ b/manifests/critical-double.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Pod +metadata: + name: kubesec-test +spec: + containers: + - name: kubesec-demo + image: gcr.io/google-samples/node-hello:1.0 + securityContext: + allowPrivilegeEscalation: true + privileged: true diff --git a/manifests/sarif.tpl b/manifests/sarif.tpl new file mode 100644 index 0000000..62150c8 --- /dev/null +++ b/manifests/sarif.tpl @@ -0,0 +1,99 @@ +{ + "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json", + "version": "2.1.0", + "runs": [ + {{- $run_first := true }} + {{- range $report_index, $report := . }} + {{- if and $report.Valid (not (eq $report.Message "This resource kind is not supported by kubesec")) -}} + {{- if $run_first -}} + {{- $run_first = false -}} + {{ else -}} + , + {{- end }} + { + "tool": { + "driver": { + "name": "Kubesec", + "fullName": "Kubesec Kubernetes Resource Security Policy Validator", + "rules": [ + {{- $rule_first := true }} + {{- range .Rules }} + {{- if $rule_first -}} + {{- $rule_first = false -}} + {{ else -}} + , + {{- end }} + { + "id": "{{ .ID }}", + "shortDescription": { + "text": "{{ .Reason }}" + }, + "helpUri": "https://github.com/controlplaneio/kubesec", + "help": { + "text": "- Reason: {{ .Reason }}\n- Selector: {{ escapeString .Selector }}\n- Score: {{ .Points }}" + }, + "messageStrings": { + "selector": { + "text": {{ escapeString .Selector | printf "%q" }} + } + }, + "properties": { + "points": "{{ .Points }}", + {{- if lt .Points 0 -}} + "security-severity": "9.0" + {{ else -}} + "security-severity": "5.0" + {{- end }} + } + } + {{- end -}} + ] + } + }, + "results": [ + {{- $result_first := true }} + {{- range $result_index, $res := joinSlices .Scoring.Advise .Scoring.Critical -}} + {{- if $result_first -}} + {{- $result_first = false -}} + {{ else -}} + , + {{- end }} + { + "ruleId": "{{ $res.ID }}", + {{- if lt $res.Points 0 -}} + "level": "error", + {{ else -}} + "level": "warning", + {{- end }} + "message": { + "text": {{ endWithPeriod $res.Reason | printf "%q" }}, + "properties": { + "score": "{{ $res.Points }}", + "selector": {{ escapeString $res.Selector | printf "%q" }} + } + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "{{ $report.FileName }}" + }, + "region": { + "startLine": 1, + "endLine": 1 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "hash-{{ $report.FileName }}" + } + } + {{- end -}} + ], + "columnKind": "utf16CodeUnits" + } + {{- end -}} + {{- end }} + ] +} diff --git a/manifests/score-5-pod-serviceaccount.yaml b/manifests/score-5-pod-serviceaccount.yaml new file mode 100644 index 0000000..ed24e13 --- /dev/null +++ b/manifests/score-5-pod-serviceaccount.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: my-pod +spec: + serviceAccountName: build-robot + automountServiceAccountToken: false + containers: + - name: nginx + image: nginx + securityContext: + runAsNonRoot: true + readOnlyRootFilesystem: true + ports: + - containerPort: 80