Skip to content

Commit da0dbb4

Browse files
authored
[SECURESIGN-2287] add artifact verification (#29)
* Add artifact verification * fix golangci-lint * updates * fix golangci-lint * qodo review updates * Add tlogEntries to signature verification output, add attestation verification support * fix golangci-lint * fix CI builds * update image deps * review updates
1 parent 58f7bc5 commit da0dbb4

File tree

15 files changed

+3325
-457
lines changed

15 files changed

+3325
-457
lines changed

.github/workflows/linter.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
with:
1919
go-version-file: 'go.mod'
2020
- name: golangci-lint
21-
uses: golangci/golangci-lint-action@v6
21+
uses: golangci/golangci-lint-action@v7
2222
with:
23-
version: v1.63
23+
version: v2.2.2
2424
args: --verbose --timeout=15m

.tekton/rhtas-console-pull-request.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ spec:
4242
value: [{"path": ".", "type": "gomod"}]
4343
- name: go_unit_test
4444
value: true
45-
- name: go_base_image
46-
value: registry.redhat.io/ubi9/go-toolset:1.24@sha256:6fd64cd7f38a9b87440f963b6c04953d04de65c35b9672dbd7f1805b0ae20d09
4745
pipelineRef:
4846
resolver: git
4947
params:

.tekton/rhtas-console-push.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ spec:
3939
value: [{"path": ".", "type": "gomod"}]
4040
- name: go_unit_test
4141
value: true
42-
- name: go_base_image
43-
value: registry.redhat.io/ubi9/go-toolset:1.24@sha256:6fd64cd7f38a9b87440f963b6c04953d04de65c35b9672dbd7f1805b0ae20d09
4442
pipelineRef:
4543
resolver: git
4644
params:

Dockerfile.rh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build stage
2-
FROM registry.redhat.io/ubi9/go-toolset:1.24@sha256:6fd64cd7f38a9b87440f963b6c04953d04de65c35b9672dbd7f1805b0ae20d09 AS builder
2+
FROM registry.redhat.io/ubi9/go-toolset:9.6@sha256:84286c7555df503df0bd3acb86fe2ad50af82a07f35707918bb0fad312fdc193 AS builder
33

44
WORKDIR /app
55

@@ -8,7 +8,7 @@ COPY . .
88
RUN go build -buildvcs=false -o rhtas_console ./cmd/rhtas_console
99

1010
# Final stage
11-
FROM registry.access.redhat.com/ubi9/ubi-minimal@sha256:67fee1a132e8e326434214b3c7ce90b2500b2ad02c9790cc61581feb58d281d5
11+
FROM registry.access.redhat.com/ubi9/ubi-minimal@sha256:7c5495d5fad59aaee12abc3cbbd2b283818ee1e814b00dbc7f25bf2d14fa4f0c
1212

1313
# Set a writable working directory
1414
WORKDIR /tmp

README.md

Lines changed: 159 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# RHTAS Console
22

3-
The RHTAS Console is a Go-based RESTful API server, providing functionality for signing and verifying software artifacts using Cosign, interacting with Sigstore's Rekor transparency log, and managing trust configurations with TUF and Fulcio. This repository serves as the backend for the RHTAS Console application, with plans to potentially add a frontend in the future.
3+
The RHTAS Console is a Go-based RESTful API server, providing functionality for verifying software artifacts, interacting with Rekor transparency log, and managing trust configurations with TUF and Fulcio. This repository serves as the backend for the RHTAS Console application, which now includes a [frontend interface](https://github.com/securesign/rhtas-console-ui).
44

55
## Features
66

7-
- **Artifact management**: Sign and verify artifacts (e.g., container images, files, SBOMs) using Cosign.
7+
- **Artifact management**: Verify artifacts (e.g., container images, files, SBOMs).
88
- **Rekor integration**: Retrieve transparency log entries and public keys from Rekor.
99
- **Trust configuration**: Get TUF targets and Fulcio certificate authorities for trust policies.
1010
- Built with [Chi](https://github.com/go-chi/chi), a lightweight Go router.
@@ -20,7 +20,6 @@ The RHTAS Console is a Go-based RESTful API server, providing functionality for
2020
```bash
2121
oapi-codegen -generate types,chi-server -package models openapi/rhtas-console.yaml > internal/models/models.go
2222
```
23-
- Optional: [rekor-cli](https://docs.sigstore.dev/rekor/installation/) and [cosign](https://docs.sigstore.dev/cosign/installation/) for testing Rekor and Cosign interactions
2423

2524
### Steps
2625

@@ -82,8 +81,7 @@ The backend exposes the following RESTful endpoints, as defined in the OpenAPI s
8281
| GET | `/healthz` | Retrieves the current health status of the server. |
8382
| GET | `/swagger-ui` | Serves the Swagger User Interface. |
8483
| GET | `/rhtas-console.yaml` | Returns the project OpenAPI spec file. |
85-
| POST | `/api/v1/artifacts/sign` | Signs an artifact using Cosign. |
86-
| POST | `/api/v1/artifacts/verify` | Verifies an artifact using Cosign. |
84+
| POST | `/api/v1/artifacts/verify` | Verifies an artifact. |
8785
| GET | `/api/v1/artifacts/{artifact}/policies` | Retrieves policies and attestations for an artifact. |
8886
| GET | `/api/v1/artifacts/image` | Retrieves metadata for a container image by full reference URI. |
8987
| GET | `/api/v1/rekor/entries/{uuid}` | Retrieves a Rekor transparency log entry by UUID. |
@@ -94,35 +92,175 @@ The backend exposes the following RESTful endpoints, as defined in the OpenAPI s
9492
| GET | `/api/v1/trust/target` | Retrieves a specific TUF target. |
9593
| GET | `/api/v1/trust/targets/certificates` | Retrieves certificates for TUF targets. |
9694

97-
#### Example: Sign an artifact
95+
#### Example: Verify an artifact
9896

99-
To sign a container image using Cosign (keyless signing with OIDC token):
97+
To verify an OCI image:
10098

99+
100+
- Using `ociImage`:
101101
```bash
102-
curl -X POST http://localhost:8080/api/v1/artifacts/sign \
102+
curl -X POST http://localhost:8080/api/v1/artifacts/verify \
103103
-H "Content-Type: application/json" \
104104
-d '{
105-
"artifact": "quay.io/example/app:latest",
106-
"artifactType": "container-image",
107-
"identityToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
108-
"annotations": {"env": "prod"}
105+
"ociImage": "ttl.sh/rhtas/test-image:1h",
106+
"expectedOIDIssuer": "https://accounts.google.com",
107+
"expectedSAN": "[email protected]",
108+
"tufRootURL": "https://tuf-repo-cdn.sigstore.dev"
109+
}'
110+
```
111+
- Using `bundle`:
112+
```bash
113+
# bundle.json: the file which contains the bundle
114+
bundle_json=$(jq -c '.' bundle.json)
115+
curl -X POST http://localhost:8080/api/v1/artifacts/verify \
116+
-H "Content-Type: application/json" \
117+
-d '{
118+
"artifactDigest": "e128e0a064433c8d46f0467b149c70052fedbfa1f9e96ac22e3deefdc943e965",
119+
"expectedOIDIssuer": "https://accounts.google.com",
120+
"expectedSAN": "[email protected]",
121+
"tufRootURL": "https://tuf-repo-cdn.sigstore.dev",
122+
"bundle": '"$bundle_json"'
109123
}'
110124
```
111125

112126
Response:
113127
```json
114128
{
115-
"success": true,
116-
"signature": "MEUCIQC...",
117-
"certificate": "-----BEGIN CERTIFICATE-----\nMIIBIjANBgkq...\n-----END CERTIFICATE-----",
118-
"logEntry": {
119-
"uuid": "108e9186e8c5677a249f2ad46ab96976656298b3feb5e031777b9e1fa5c55aaf7e0115bee955ccaa",
120-
"integratedTime": 1747816420,
121-
"logIndex": 216249784
122-
}
129+
"details": {
130+
"mediaType": "application/vnd.dev.sigstore.verificationresult+json;version=0.1",
131+
"signature": {
132+
"certificate": {
133+
"certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev",
134+
"issuer": "https://accounts.google.com",
135+
"subjectAlternativeName": "[email protected]"
136+
}
137+
},
138+
"statement": {},
139+
"tlogEntries": [
140+
{
141+
"canonicalized_body": "***",
142+
"inclusion_promise": {
143+
"signed_entry_timestamp": "MEQCICBVaTJ7x0hcWCJToFGwyRuTvWL/Tx2ZCe/7C8J1odmAAiATWLgtclMY4TmrzKvdf2Nj9a7SQp9oZSvQauK1I/nqqg=="
144+
},
145+
"integrated_time": 1761133633,
146+
"kind_version": {
147+
"kind": "hashedrekord",
148+
"version": "0.0.1"
149+
},
150+
"log_id": {
151+
"key_id": "wNI9atQGlz+VWfO6LRygH4WSfY/8W4RFwiT5i5WRgB0="
152+
},
153+
"log_index": 630106028
154+
}
155+
],
156+
"verifiedIdentity": {
157+
"issuer": {
158+
"issuer": "https://accounts.google.com"
159+
},
160+
"subjectAlternativeName": {
161+
"subjectAlternativeName": "[email protected]"
162+
}
163+
},
164+
"verifiedTimestamps": [
165+
{
166+
"timestamp": "2025-10-22T13:47:13+02:00",
167+
"type": "Tlog",
168+
"uri": "https://rekor.sigstore.dev"
169+
}
170+
]
171+
},
172+
"verified": true
173+
}
174+
```
175+
176+
To verify an attestation:
177+
178+
- Using `predicateType`:
179+
```bash
180+
curl -X POST http://localhost:8080/api/v1/artifacts/verify \
181+
-H "Content-Type: application/json" \
182+
-d '{
183+
"ociImage": "ttl.sh/rhtas/test-image-1@sha256:1fd54200e48e100883366b0180add3400a74e8b912e7c87a98215d1d25a888f8",
184+
"expectedOIDIssuer": "https://accounts.google.com",
185+
"expectedSAN": "[email protected]",
186+
"tufRootURL": "https://tuf-repo-cdn.sigstore.dev",
187+
"predicateType": "https://example.com/attestations/build"
188+
}'
189+
```
190+
191+
Response:
192+
```json
193+
{
194+
"details": {
195+
"mediaType": "application/vnd.dev.sigstore.verificationresult+json;version=0.1",
196+
"signature": {
197+
"certificate": {
198+
"certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev",
199+
"issuer": "https://accounts.google.com",
200+
"subjectAlternativeName": "[email protected]"
201+
}
202+
},
203+
"statement": {
204+
"_type": "https://in-toto.io/Statement/v0.1",
205+
"predicate": {
206+
"buildType": "manual-test",
207+
"builder": {
208+
"id": "example-builder"
209+
},
210+
"metadata": {
211+
"buildFinishedOn": "2025-10-20T14:50:39+02:00",
212+
"buildStartedOn": "2025-10-20T14:50:39+02:00"
213+
},
214+
"predicateType": "https://example.com/attestations/build"
215+
},
216+
"predicateType": "https://example.com/attestations/build",
217+
"subject": [
218+
{
219+
"digest": {
220+
"sha256": "b8e61023eb8a764eba2ebaea3a80049f046b213af3ca8729322e7ac33ba02bff"
221+
},
222+
"name": "ttl.sh/rhtas/test-image"
223+
}
224+
]
225+
},
226+
"tlogEntries": [
227+
{
228+
"canonicalized_body": "***",
229+
"inclusion_promise": {
230+
"signed_entry_timestamp": "MEQCIE5HREiJyWlMW2cpn389w4pZz1uKNTuN/IkTvw4ARQytAiByIqNxle9Vi2JO8FCywTyFZzsetyht9bslNfAibsug0A=="
231+
},
232+
"integrated_time": 1761133726,
233+
"kind_version": {
234+
"kind": "dsse",
235+
"version": "0.0.1"
236+
},
237+
"log_id": {
238+
"key_id": "wNI9atQGlz+VWfO6LRygH4QUfY/8W5RFwiT5i5WRgB0="
239+
},
240+
"log_index": 630107152
241+
}
242+
],
243+
"verifiedIdentity": {
244+
"issuer": {
245+
"issuer": "https://accounts.google.com"
246+
},
247+
"subjectAlternativeName": {
248+
"subjectAlternativeName": "[email protected]"
249+
}
250+
},
251+
"verifiedTimestamps": [
252+
{
253+
"timestamp": "2025-10-22T13:48:46+02:00",
254+
"type": "Tlog",
255+
"uri": "https://rekor.sigstore.dev"
256+
}
257+
]
258+
},
259+
"verified": true
123260
}
124261
```
125262

263+
126264
#### Example: Retrieve a Rekor entry
127265

128266
To fetch a Rekor entry by UUID:
@@ -191,4 +329,4 @@ The `models` package is generated from the OpenAPI specification:
191329
make generate-openapi
192330
```
193331

194-
This generates Go types such as `RekorEntry`, `SignArtifactRequest`, `VerifyArtifactResponse`, and others.
332+
This generates Go types such as `RekorEntry`, `VerifyArtifactResponse`, and others.

0 commit comments

Comments
 (0)