Skip to content

Commit 75ece05

Browse files
authored
Add security checks (#4)
* Cosmetic changes * Add container build arguments * Add mongodb-atlas/add-runner-ip/action.yml * Remove logs * Add cosign, fossa and syft * use explicit version of cosign-installer * Improve cosign * Move sbom generation * Update yamllint config * Remove trailing spaces * Add comment
1 parent d8ea587 commit 75ece05

7 files changed

Lines changed: 170 additions & 39 deletions

File tree

.editorconfig

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
root = true
2-
3-
[*]
4-
end_of_line = lf
5-
insert_final_newline = true
6-
indent_style = space
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
indent_size = 2
6+
indent_style = space
7+
insert_final_newline = true
8+
trim_trailing_whitespace = true
9+
10+
[md]
11+
trim_trailing_whitespace = false

.markdownlint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# ref. https://github.com/DavidAnson/markdownlint
22
default: true
3-
MD013: # Line length
3+
MD013:
44
line_length: 240

.yamllint.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
# ref. https://yamllint.readthedocs.io/en/stable/configuration.html
2-
32
extends: default
4-
53
rules:
64
document-start: disable
75
line-length:
86
level: warning
9-
max: 120
7+
max: 180
108
truthy: disable

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ Repository of workflow parts to be used in GitHub Actions.
44

55
## Actions
66

7-
Name | Detail
8-
-------------------------------------------------------------- | ---------------------------------------------------------------------------
9-
[Docker > Build & Push](docker/build-push/action.yml) | Build a new container image with Docker and push it to a container registry
10-
[Docker > Build & Scan](docker/build-scan/action.yml) | Build a new container image with Docker and scan it
11-
[.NET > Build, lint & test](dotnet/build-lint-test/action.yml) | Build .NET code, lint it and run tests
12-
[MongoDB > Start](mongodb/start/action.yml) | Start a local MongoDB database
7+
Technology | Role | Action | Detail
8+
-----------|----------|---------------------------------------------------------|----------------------------------------------------------------------------
9+
Docker | CD | [Build & Push](docker/build-push/action.yml) | Build a new container image with Docker, and push it to a container registry
10+
Docker | CI | [Build & Scan](docker/build-scan/action.yml) | Build a new container image with Docker, and scan it
11+
.NET | CI | [Build, lint & test](dotnet/build-lint-test/action.yml) | Build .NET code, check the code with linter and Sonar, and run tests
12+
MongoDB | Services | [Start](mongodb/start/action.yml) | Start a local MongoDB database

docker/build-push/action.yml

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
name: Build and push a container image
2-
description: Builds a new container image with Docker and pushes it to a registry
2+
description: |
3+
Builds a new container image with Docker and pushes it to a registry
4+
Make sure to add (needed by cosign):
5+
```
6+
permissions:
7+
id-token: write
8+
contents: read
9+
```
310
411
inputs:
512
container_registry:
613
description: Container registry
714
required: true
8-
container_username:
9-
description: Container username
15+
container_registry_username:
16+
description: Container registry username
1017
required: true
11-
container_password:
12-
description: Container password
18+
container_registry_password:
19+
description: Container registry password
1320
required: true
1421
docker_file:
1522
description: Path to the Dockerfile
@@ -27,6 +34,10 @@ inputs:
2734
description: Create latest tag?
2835
required: false
2936
default: 'false'
37+
build_arguments:
38+
description: Container build arguments
39+
required: false
40+
default: ''
3041

3142
runs:
3243
using: "composite"
@@ -35,17 +46,48 @@ runs:
3546
uses: docker/login-action@v3
3647
with:
3748
registry: ${{ inputs.container_registry }}
38-
username: ${{ inputs.container_username }}
39-
password: ${{ inputs.container_password }}
49+
username: ${{ inputs.container_registry_username }}
50+
password: ${{ inputs.container_registry_password }}
51+
4052
- name: Build container image
41-
run: docker build . --file ${{inputs.docker_file}} --tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}}
53+
run: docker build . --file ${{ inputs.docker_file }} --tag ${{ inputs.image_path }}/${{ inputs.image_name }}:${{ inputs.image_tag }}${{ inputs.build_arguments }}
4254
shell: bash
55+
56+
- name: Generate SBOM with Syft
57+
uses: anchore/sbom-action@v0
58+
continue-on-error: true
59+
with:
60+
image: ${{ inputs.image_path }}/${{ inputs.image_name }}:${{ inputs.image_tag }}
61+
# format: spdx-json # Or cyclonedx-json
62+
# output-file: sbom.json
63+
# upload-artifact: true # Auto-upload to workflow artifacts
64+
4365
- name: Push image to container registry
44-
run: docker push ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}}
66+
run: docker push ${{ inputs.image_path }}/${{ inputs.image_name }}:${{ inputs.image_tag }}
4567
shell: bash
68+
4669
- name: Push latest tag to container registry
4770
if: ${{ inputs.create_latest == 'true' }}
4871
run: |
49-
docker tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} ${{inputs.image_path}}/${{inputs.image_name}}:latest
50-
docker push ${{inputs.image_path}}/${{inputs.image_name}}:latest
72+
docker tag ${{ inputs.image_path }}/${{ inputs.image_name }}:${{ inputs.image_tag }} ${{ inputs.image_path }}/${{ inputs.image_name }}:latest
73+
docker push ${{ inputs.image_path }}/${{ inputs.image_name }}:latest
74+
shell: bash
75+
76+
- name: Install Cosign
77+
uses: sigstore/cosign-installer@v4.0.0
78+
with:
79+
cosign-release: 'v3.0.3'
80+
81+
- name: Get image digest
82+
id: digest
83+
run: |
84+
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ${{ inputs.image_path }}/${{ inputs.image_name }}:${{ inputs.image_tag }} | cut -d'@' -f2)
85+
echo "DIGEST=$DIGEST" >> $GITHUB_OUTPUT
86+
shell: bash
87+
88+
- name: Sign image with Cosign
89+
env:
90+
COSIGN_EXPERIMENTAL: 1
91+
run: |
92+
cosign sign --yes ${{ inputs.image_path }}/${{ inputs.image_name }}@${{ steps.digest.outputs.DIGEST }}
5193
shell: bash

dotnet/build-lint-test/action.yml

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,57 @@ inputs:
55
dotnet_version:
66
description: .NET SDK version to be used
77
required: false
8-
default: '10.0.x'
8+
default: "10.0.x"
99
sonar_enabled:
1010
description: Enable code scan by Sonar
1111
required: false
12-
default: 'false'
12+
default: "false"
1313
sonar_organization:
1414
description: Sonar organization
1515
required: false
16-
default: ''
16+
default: ""
1717
sonar_host_url:
1818
description: Sonar host URL
1919
required: false
20-
default: ''
20+
default: ""
2121
sonar_project_name:
2222
description: Sonar project name
2323
required: false
24-
default: ''
24+
default: ""
2525
sonar_project_key:
2626
description: Sonar project key
2727
required: false
28-
default: ''
28+
default: ""
2929
sonar_token:
3030
description: Sonar token for login
3131
required: false
32-
default: ''
32+
default: ""
3333
report_folder:
3434
description: Folder where report files will be generated
3535
required: false
3636
default: report
37+
fossa_enabled:
38+
description: Enable license compliance with FOSSA
39+
required: false
40+
default: "false"
41+
fossa_api_key:
42+
description: FOSSA API KEY
43+
required: false
44+
default: ""
3745

3846
runs:
3947
using: "composite"
4048
steps:
4149
- name: Install .NET
4250
uses: actions/setup-dotnet@v4
4351
with:
44-
dotnet-version: ${{inputs.dotnet_version}}
52+
dotnet-version: ${{ inputs.dotnet_version }}
4553
- name: Set up JDK for Sonar
4654
if: ${{ inputs.sonar_enabled == 'true' }}
4755
uses: actions/setup-java@v4
4856
with:
4957
java-version: 21
50-
distribution: 'zulu'
58+
distribution: "zulu"
5159
- name: Install .NET linters
5260
if: ${{ inputs.dotnet_version == '7.0.x' }}
5361
run: dotnet tool install -g dotnet-format --version "7.*" --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json
@@ -86,24 +94,50 @@ runs:
8694
shell: bash
8795
- name: Start code analysis
8896
if: ${{ inputs.sonar_enabled == 'true' }}
89-
run: ./.sonar/scanner/dotnet-sonarscanner begin /k:"${{inputs.sonar_project_key}}" /o:"${{inputs.sonar_organization}}" /n:"${{inputs.sonar_project_name}}" /d:sonar.token="${{inputs.sonar_token}}" /d:sonar.host.url="${{inputs.sonar_host_url}}" /d:sonar.cpd.exclusions="**/*Generated*.cs,${{inputs.report_folder}}/**" /d:sonar.exclusions="${{inputs.report_folder}}/**/*" /d:sonar.coverageReportPaths="${{inputs.report_folder}}/SonarQube.xml"
97+
run: |
98+
./.sonar/scanner/dotnet-sonarscanner begin /k:"${{ inputs.sonar_project_key }}" /o:"${{ inputs.sonar_organization }}" \
99+
/n:"${{ inputs.sonar_project_name }}" /d:sonar.token="${{ inputs.sonar_token}}" /d:sonar.host.url="${{inputs.sonar_host_url}}" \
100+
/d:sonar.cpd.exclusions="**/*Generated*.cs,${{ inputs.report_folder }}/**" /d:sonar.exclusions="${{ inputs.report_folder }}/**/*" \
101+
/d:sonar.coverageReportPaths="${{ inputs.report_folder }}/SonarQube.xml"
90102
shell: bash
91103
- name: Build .NET solution
92104
run: dotnet build --no-restore
93105
shell: bash
94106
- name: Run tests
95-
run: dotnet test --no-build --verbosity normal --configuration Debug --logger:"junit;LogFilePath=..\..\artifacts\{assembly}-test-result.xml;MethodFormat=Class;FailureBodyFormat=Verbose" --collect:"XPlat Code Coverage"
107+
run: |
108+
dotnet test --no-build --verbosity normal --configuration Debug \
109+
--logger:"junit;LogFilePath=..\..\artifacts\{assembly}-test-result.xml;MethodFormat=Class;FailureBodyFormat=Verbose" \
110+
--collect:"XPlat Code Coverage"
96111
shell: bash
97112
env:
98113
ASPNETCORE_ENVIRONMENT: Development
99114
Application__IsHttpsRedirectionEnabled: "false"
100115
- name: Generate test report
101-
run: reportgenerator "-reports:./test/*/TestResults/*/coverage.cobertura.xml" "-targetdir:${{inputs.report_folder}}" "-reporttypes:Cobertura;Html;TextSummary;SonarQube"
116+
run: |
117+
reportgenerator "-reports:./test/*/TestResults/*/coverage.cobertura.xml" \
118+
"-targetdir:${{inputs.report_folder}}" \
119+
"-reporttypes:Cobertura;Html;TextSummary;SonarQube"
102120
shell: bash
103121
- name: Complete code analysis
104122
if: ${{ inputs.sonar_enabled == 'true' }}
105123
run: ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{inputs.sonar_token}}"
106124
shell: bash
125+
126+
- name: License Compliance with FOSSA
127+
if: ${{ inputs.fossa_enabled == 'true' }}
128+
uses: fossas/fossa-action@v1
129+
with:
130+
api-key: "${{ inputs.fossa_api_key }}"
131+
run-tests: false
132+
133+
- name: Generate SBOM with Syft
134+
uses: anchore/sbom-action@v0
135+
# with:
136+
# path: . # Or Dockerfile path
137+
# format: spdx-json # Or cyclonedx-json
138+
# output-file: sbom.json
139+
# upload-artifact: true # Auto-upload to workflow artifacts
140+
107141
- name: Archive test results
108142
uses: actions/upload-artifact@v4
109143
with:
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Add GitHub Actions runner public IP to MongoDB Atlas
2+
description: Update project IP access list (temporary)
3+
4+
inputs:
5+
atlas_publickey:
6+
description: MongoDB public key
7+
required: true
8+
atlas_privatekey:
9+
description: MongoDB private key
10+
required: true
11+
atlas_groupid:
12+
description: MongoDB group ID
13+
required: true
14+
github_runid:
15+
description: GitHub run ID
16+
required: true
17+
18+
runs:
19+
using: "composite"
20+
steps:
21+
- name: Add runner IP to MongoDB Atlas
22+
shell: bash
23+
run: |
24+
# gets current outbound IP of this runner
25+
RUNNER_IP=$(curl -s https://api.ipify.org || curl -s https://ifconfig.me)
26+
if [ -z "$RUNNER_IP" ]; then
27+
echo "Failed to detect runner IP"
28+
exit 1
29+
fi
30+
31+
echo "Detected runner IP: $RUNNER_IP"
32+
33+
# prepares JSON payload (single /32 entry, temporary delete after 1 hour)
34+
# uses ISO 8601 UTC for deleteAfterDate (current time + 3600s)
35+
DELETE_AFTER=$(date -u -d '+3600 seconds' +"%Y-%m-%dT%H:%M:%SZ")
36+
PAYLOAD="[{\"ipAddress\": \"$RUNNER_IP\", \"comment\": \"GH Actions temp - run ${{ github.run_id }}\", \"deleteAfterDate\": \"$DELETE_AFTER\"}]"
37+
38+
# calls Atlas API v2 to add the entry (uses digest Auth via curl)
39+
RESPONSE=$(curl -s -w "%{http_code}" --user "${{inputs.atlas_publickey}}:${{inputs.atlas_privatekey}}" --digest \
40+
-H "Accept: application/vnd.atlas.2023-01-01+json" \
41+
-H "Content-Type: application/json" \
42+
--data "$PAYLOAD" \
43+
"https://cloud.mongodb.com/api/atlas/v2/groups/${{inputs.atlas_groupid}}/accessList")
44+
45+
HTTP_CODE=${RESPONSE: -3}
46+
47+
if [[ "$HTTP_CODE" != "201" ]]; then
48+
echo "Failed to add IP to Atlas access list (code $HTTP_CODE)"
49+
exit 1
50+
fi
51+
52+
echo "Successfully added temporary IP $RUNNER_IP to Atlas access list (auto-deletes ~1h)"

0 commit comments

Comments
 (0)