Aller au contenu principal

Sigstore Self Hosted pour signer des artefacts

· 10 minutes de lecture
Stéphane ROBERT

logo wolfi

Hier, je vous ai présenté comment utiliser cosign pour signer et vérifier vos images Docker. Par contre, j'avais désactivé le stockage des preuves de signature dans le serveur de transparence rekor publique. Aujourd'hui, je vous propose de voir comment avoir un serveur Rekor self-hosted. Attention au moment de l'écriture de ce billet cette fonctionnalité est au status Experimental.

Installation de kind

Pour tester rekor, j'ai choisi de le faire sur une instance de cluster kubernetes créé avec kind. Pour installer kind, rien de plus facile avec asdf.

asdf plugin add kind                                                                                                                               13:49:33
asdf install kind latest
asdf global kind latest

Configuration kind pour ajouter un serveur d'ingress

Pour exposer mon service, je vais utiliser le controleur d'ingress nginx qui peut être déployé dans kind. Pour l'installer sur notre cluster kind, il faut créer un fichier de configuration kind.config:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP

On peut créer le cluster :

kind create cluster --config kind.config
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

Au bout de quelques instants, vous devriez avoir un pod ingress-nginx-controller-xxxxxxxxx-xxxx dans le namespace ingress-nginx.

> kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-5mbz8 0/1 Completed 0 5h5m
ingress-nginx-admission-patch-gsj58 0/1 Completed 1 5h5m
ingress-nginx-controller-6b7f45576b-qxfxc 1/1 Running 0 5h5m

Déploiement de la suite sigstore

Bon reste à déployer toute la suite Sigstore. Et bonne nouvelle, on a un chart Helm à notre disposition.

helm repo add sigstore https://sigstore.github.io/helm-charts
helm repo update

Avant de l'utiliser, nous allons créer le fichier de values.

helm show values sigstore/scaffold > values.yaml

Editer le fichier et activer les composants : fulcio, ctlog, tuf et CopySecretjob : enabled: true.

Un exemple de fichier de values:

---
# Fulcio
fulcio:
enabled: true
namespace:
name: fulcio-system
create: true
forceNamespace: fulcio-system
createcerts:
fullnameOverride: fulcio-createcerts
ctlog:
enabled: false
createctconfig:
logPrefix: sigstorescaffolding
server:
fullnameOverride: fulcio-server
ingress:
http:
annotations:
cert-manager.io/cluster-issuer: "vault-issuer"
className: nginx
hosts:
- host: fulcio.robert.local
path: /
tls:
- secretName: fulcio-tls
hosts:
- fulcio.robert.local
config:
contents:
OIDCIssuers:
https://oauth2.sigstore.dev/auth:
IssuerURL: https://oauth2.sigstore.dev/auth
ClientID: sigstore
Type: email
IssuerClaim: $.federated_claims.connector_id

# CTLog
ctlog:
enabled: true
namespace:
name: ctlog-system
create: true
forceNamespace: ctlog-system
fullnameOverride: ctlog
createcerts:
fullnameOverride: ctlog-createcerts
createtree:
fullnameOverride: ctlog-createtree
displayName: ctlog-tree
# Rekor
rekor:
enabled: true
namespace:
name: rekor-system
create: true
forceNamespace: rekor-system
fullnameOverride: rekor
server:
fullnameOverride: rekor-server
ingress:
className: nginx
annotations:
cert-manager.io/cluster-issuer: "vault-issuer"
hosts:
- host: rekor.robert.local
path: /
tls:
- secretName: rekor-tls
hosts:
- rekor.robert.local
redis:
fullnameOverride: rekor-redis
trillian:
enabled: false


# Trillian
trillian:
enabled: true
namespace:
name: trillian-system
create: true
forceNamespace: trillian-system
fullnameOverride: trillian
logServer:
name: trillian-logserver
fullnameOverride: trillian-logserver
portHTTP: 8090
portRPC: 8091
logSigner:
name: trillian-logsigner
fullnameOverride: trillian-logsigner
mysql:
fullnameOverride: trillian-mysql

tuf:
enabled: true
namespace:
name: tuf-system
create: true
forceNamespace: tuf-system
fullnameOverride: tuf
ingress:
className: nginx
annotations:
cert-manager.io/cluster-issuer: "vault-issuer"
http:
hosts:
- host: tuf.robert.local
path: /
tls:
- secretName: tuf-tls
hosts:
- tuf.robert.local

secrets:
rekor:
name: rekor-public-key
path: rekor-pubkey
fulcio:
name: fulcio-server-secret
path: fulcio-cert
ctlog:
name: ctlog-public-key
path: ctlog-pubkey

copySecretJob:
enabled: true
name: copy-secrets-job
registry: docker.io
repository: alpine/k8s
version: sha256:fb0d2db81fb0f98abb1adf5246d6f0f4d19f34031afe4759cb7ad8e2eb8d2c01
imagePullPolicy: IfNotPresent
serviceaccount: tuf-secret-copy-job
backoffLimit: 6

tsa:
enabled: false
namespace:
name: tsa-system
create: true
forceNamespace: tsa-system
server:
fullnameOverride: tsa-server

Nous allons générer les certificats de rekor, fulcio et tuf avec mkcert:

for service_name in rekor fulcio tuf; do
mkcert ${service_name}.robert.local localhost 127.0.0.1 ::1
done

On déploie la stack :

helm upgrade -i scaffold sigstore/scaffold -n sigstore --create-namespace --values scaffold.values.yaml

Release "scaffold" does not exist. Installing it now.
NAME: scaffold
LAST DEPLOYED: Thu Aug 31 08:38:28 2023
NAMESPACE: sigstore
STATUS: deployed
REVISION: 1
TEST SUITE: None

Au bout de quelques minutes, vous trouverez en fait plusieurs composants :

  • Le serveur rekor.
  • Un serveur redis.
  • Un serveur de bdd mysql.
  • Un serveur de log trillian, produit sigstore également.
  • Un serveur ctflog
  • Un serveur fulcio
  • Et quelques jobs dont certains en erreur mais c'est normal !

Attendez que tout soit démarré !

kubectcl get pod -A --watch
NAMESPACE NAME READY STATUS RESTARTS AGE
ctlog-system ctlog-56fbc5595f-8trzt 1/1 Running 0 4m45s
ctlog-system ctlog-createtree-7vdgs 0/1 Completed 0 4m45s
ctlog-system scaffold-ctlog-createctconfig-chhmb 0/1 Completed 0 4m45s
fulcio-system fulcio-createcerts-mrpkd 0/1 Completed 0 4m45s
fulcio-system fulcio-server-7cd549dc9b-2hw4f 1/1 Running 0 4m45s
ingress-nginx ingress-nginx-admission-create-n64qz 0/1 Completed 0 9m56s
ingress-nginx ingress-nginx-admission-patch-k8tcv 0/1 Completed 0 9m56s
ingress-nginx ingress-nginx-controller-6b7f45576b-pbxzb 1/1 Running 0 9m56s
kube-system coredns-5d78c9869d-659ns 1/1 Running 0 11m
kube-system coredns-5d78c9869d-pgh42 1/1 Running 0 11m
kube-system etcd-kind-control-plane 1/1 Running 0 12m
kube-system kindnet-v4x9k 1/1 Running 0 11m
kube-system kube-apiserver-kind-control-plane 1/1 Running 0 12m
kube-system kube-controller-manager-kind-control-plane 1/1 Running 0 12m
kube-system kube-proxy-875cf 1/1 Running 0 11m
kube-system kube-scheduler-kind-control-plane 1/1 Running 0 12m
local-path-storage local-path-provisioner-6bc4bddd6b-4tvd7 1/1 Running 0 11m
rekor-system rekor-redis-5d665cddb8-mrsh9 1/1 Running 0 4m45s
rekor-system rekor-server-75c967c64-p4jc2 1/1 Running 0 4m45s
rekor-system scaffold-rekor-createtree-vt8wb 0/1 Completed 0 4m45s
trillian-system scaffold-trillian-createdb-22t9s 0/1 Error 0 4m45s
trillian-system scaffold-trillian-createdb-9jwgm 0/1 Completed 0 3m55s
trillian-system trillian-logserver-68bf4cbbc8-jd52q 1/1 Running 0 4m45s
trillian-system trillian-logsigner-754cd6894-bk6hr 1/1 Running 0 4m45s
trillian-system trillian-mysql-64dbf8864-cngn2 1/1 Running 0 4m45s
tuf-system copy-secrets-job-fzv89 0/3 Completed 0 4m45s
tuf-system scaffold-tuf-tuf-7fd95b87cd-gzrwv 1/1 Running 0 4m45s

On crée les secrets stockant les certificats :

for service_name in rekor fulcio tuf; do
kubectl create secret -n ${service_name}-system tls ${service_name}-tls --key=${service_name}.robert.local+3-key.pem --cert=${service_name}.robert.local+3.pem
done

secret/rekor-tls created
secret/fulcio-tls created
secret/tuf-tls created

On récupère l'adresse du container :

docker container inspect kind-control-plane \
--format '{{ .NetworkSettings.Networks.kind.IPAddress }}'

172.19.0.2

On édite le fichier /etc/hosts :

vi /etc/hosts

Ajoutez cette ligne :

172.19.0.2 rekor.robert.local
172.19.0.2 fulcio.robert.local
172.19.0.2 tuf.robert.local

On teste que le serveur rekor est accessible et que le certificat est accepté:

curl https://rekor.robert.local
<!DOCTYPE html>
<html lang="en-us">

<head>
<meta property="og:title" content="sigstore" />
<meta property="og:description" content="A non-profit, public good software signing &amp; transparency service" />
<meta property="og:type" content="website" />
<meta property="og:url" content="/" />
<meta name="description" content="A non-profit, public good software signing &amp; transparency service" />
<meta charset="utf-8">
<title>sigstore</title>

<link href="https://fonts.googleapis.com/css?family=Catamaran:400,600" rel="stylesheet">
</head>

<body>
<h1>
Rekor Server
</h1>
<h2>
A non-profit, public good software signing &amp; transparency service.
<p>To learn more visit <a href="https://sigstore.dev">Sigstore project page</a></p>
</h2>

<p>Currently storing <span id="count">some</span> items.</p>

<footer>
<p>Copyright © sigstore a Series of LF Projects, LLC For web site terms of use, trademark policy and general project
policies please see <a href="https://lfprojects.org">https://lfprojects.org</a>.</p>
</footer>

<script type="text/javascript">
const url = '/api/v1/log/';
function update() {
fetch(url, {headers: {'Accept': 'application/json'}}).then((resp) => {
resp.json().then((j) => {
let count = j.treeSize;
document.getElementById('count').innerText = count;
}).catch(console.error);
}).catch(console.error);
}
update(); // Update immediately on page load.
setInterval(update, 10000); // Update the counter every 10 seconds.
</script>
</body>
</html>

Il faut maintenant initialisé le serveur tuf :

kubectl -n tuf-system get secrets tuf-root -ojsonpath='{.data.root}' | base64 -d > root.json


Vous devriez obtenir le fichier root.json avec ce contenu :

cat root.json
{
"signed": {
"_type": "root",
"spec_version": "1.0",
"version": 1,
"expires": "2024-03-02T06:42:45Z",
"keys": {
"1ddd1e0f11401c5b981bd67edaf3c2779a963cc9739cfb7136a4be48b0ae7a6e": {
"keytype": "ed25519",
"scheme": "ed25519",
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keyval": {
"public": "507e72d4024fa5f12289bbe9ddb24259939eeece2f43100be3deaab8e7fed859"
}
},
"1f28940a9d054ddcd3d34603b6b0db2bad0b5c79ce822bb206682fa5f3595f01": {
"keytype": "ed25519",
"scheme": "ed25519",
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keyval": {
"public": "4a50d6d166fdc9648e7ffc3db6eae7f09fadc6d47c2012a3382d159daab42870"
}
},
"777ba66bf28f946c2182ec68260230b1aded26ff5b80888a6777354f674a92fb": {
"keytype": "ed25519",
"scheme": "ed25519",
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keyval": {
"public": "5b1940fe025d8e4cf4ea4b04b8e3260b20b6e460eef3e31e98ff9d5021dfdefe"
}
},
"acbf0880ad51c944a937b1a5b869bbe423279fa803d04fcc13d7ecb49e50b456": {
"keytype": "ed25519",
"scheme": "ed25519",
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keyval": {
"public": "6cdfd0edf6f1e99db99a3a3bad5f66f2bf7228c4108d6c1774986235daf1d152"
}
}
},
"roles": {
"root": {
"keyids": [
"777ba66bf28f946c2182ec68260230b1aded26ff5b80888a6777354f674a92fb"
],
"threshold": 1
},
"snapshot": {
"keyids": [
"1ddd1e0f11401c5b981bd67edaf3c2779a963cc9739cfb7136a4be48b0ae7a6e"
],
"threshold": 1
},
"targets": {
"keyids": [
"1f28940a9d054ddcd3d34603b6b0db2bad0b5c79ce822bb206682fa5f3595f01"
],
"threshold": 1
},
"timestamp": {
"keyids": [
"acbf0880ad51c944a937b1a5b869bbe423279fa803d04fcc13d7ecb49e50b456"
],
"threshold": 1
}
},
"consistent_snapshot": false
},
"signatures": [
{
"keyid": "777ba66bf28f946c2182ec68260230b1aded26ff5b80888a6777354f674a92fb",
"sig": "188589b269dd79f899718aac806375a0a437c23e7691434fd28b83ca719ee2b309f70f82e7a497245f19dfeacc265ab53e74aa68a8de9d151c38268fb2864c04"
}
]
}

Initialisons le serveur tuf :

cosign -d initialize --root root.json --mirror https://tuf.robert.local
Root status:
{
"local": "/home/bob/.sigstore/root",
"remote": "https://tuf.robert.local",
"metadata": {
"root.json": {
"version": 1,
"len": 2178,
"expiration": "02 Mar 24 06:42 UTC",
"error": ""
},
"snapshot.json": {
"version": 1,
"len": 618,
"expiration": "02 Mar 24 06:42 UTC",
"error": ""
},
"targets.json": {
"version": 1,
"len": 1028,
"expiration": "02 Mar 24 06:42 UTC",
"error": ""
},
"timestamp.json": {
"version": 1,
"len": 619,
"expiration": "02 Mar 24 06:42 UTC",
"error": ""
}
},
"targets": [
"ctfe.pub",
"fulcio_v1.crt.pem",
"rekor.pub"
]
}

Cool ! Passons à la signature :)

Installation de la CLI Rekor

Avant de signer notre image de conteneur, installons la cli Rekor :

wget https://github.com/sigstore/rekor/releases/download/v1.2.2/rekor-cli-linux-amd64 -O rekor-cli
chmod +x rekor-cli
sudo install rekor-cli /usr/local/bin

On teste l'accès à notre serveur local :

rekor-cli loginfo --rekor_server https://rekor.robert.local

No previous log state stored, unable to prove consistency
Unable to store previous state: do not persist state for empty logs
Unable to store previous state: do not persist state for empty logs
Verification Successful!
Active Tree Size: 0
Total Tree Size: 0
Root Hash: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Timestamp: 2023-08-31T06:54:22Z
TreeID: 2639852854654503233

Super !

Signature de l'image avec Cosign avec notre serveur Rekor

On reprend les commandes du billet d'hier pour signer notre image sauf qu'on indique l'adresse du serveur rekor :

docker pull registry.gitlab.com/dockerfiles6/images/demo-cosign:latest
export IMAGE=`docker inspect --format='{{index .RepoDigests 0}}' registry.gitlab.com/dockerfiles6/images/demo-cosign:latest`
export REKOR_URL=https://rekor.robert.local
export FULCIO_URL=https://fulcio.robert.local

Generating ephemeral keys...
Retrieving signed certificate...

The sigstore service, hosted by sigstore a Series of LF Projects, LLC, is provided pursuant to the Hosted Project Tools Terms of Use, available at https://lfprojects.org/policies/hosted-project-tools-terms-of-use/.
Note that if your submission includes personal data associated with this signed artifact, it will be part of an immutable record.
This may include the email address associated with the account with which you authenticate your contractual Agreement.
This information will be used for signing this artifact and will be stored in public transparency logs and cannot be removed later, and is subject to the Immutable Record notice at https://lfprojects.org/policies/hosted-project-tools-immutable-records/.

By typing 'y', you attest that (1) you are not submitting the personal data of any other person; and (2) you understand and agree to the statement and the Agreement terms at the URLs listed above.
Are you sure you would like to continue? [y/N] y
Your browser will now be opened to:
https://oauth2.sigstore.dev/auth/auth?access_type=online&client_id=sigstore&code_challenge=C_QL_huOKcxcHhVSnehyy5RhfHmA3TznIZqAdNAPCvg&code_challenge_method=S256&nonce=2UjtssQFjscrEvjyvBxOETXwuF1&redirect_uri=http%3A%2F%2Flocalhost%3A33809%2Fauth%2Fcallback&response_type=code&scope=openid+email&state=2Ujtslba54Tl5MLEmqLSBMBWVpX
Successfully verified SCT...
tlog entry created with index: 0
Pushing signature to: registry.gitlab.com/dockerfiles6/images/demo-cosign

Normalement une fenêtre s'ouvre avec une demande d'authentification. Pour le moment, on utilise la configuration d'OIDC de sigstore mais on peut imaginer utiliser celui de -- (self-hosté) par exemple. Ça fera l'objet d'un projet billet.

Vérification de la signature

Et là cela fonctionne-t-il ? :

cosign verify --rekor-url=$REKOR_URL --certificate-identity-regexp=.+ --certificate-oidc-issuer-regexp=.+ $IMAGE | jq -r .
**Warning** Missing fallback target fulcio.crt.pem, skipping
**Warning** Missing fallback target fulcio_intermediate_v1.crt.pem, skipping

Verification for registry.gitlab.com/dockerfiles6/images/demo-cosign@sha256:c3b002f3a90054d0f0db12f43f608291610dd61af7f5b06f2a05fdeac1096b40 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- Existence of the claims in the transparency log was verified offline
- The code-signing certificate was verified using trusted certificate authority certificates
[
{
"critical": {
"identity": {
"docker-reference": "registry.gitlab.com/dockerfiles6/images/demo-cosign"
},
"image": {
"docker-manifest-digest": "sha256:c3b002f3a90054d0f0db12f43f608291610dd61af7f5b06f2a05fdeac1096b40"
},
"type": "cosign container image signature"
},
"optional": {
"1.3.6.1.4.1.57264.1.1": "https://github.com/login/oauth",
"Bundle": {
"SignedEntryTimestamp": "MEQCIFA1nuEu4X0/HaGUs/Pi2wURTRTNa/2aLYV+9rSUBny2AiAdsANTHFDFedNu5JMkIM3ItLrqJLIZ9V1PboGZeLuQXA==",
"Payload": {
"body": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MjVhNmIyOTcxOWI1YzRhOWMyZDNlZTIzZGU0YmY4OGExNGM3NDE0NjRlN2YwNDdmOTE2NjAyMTg1ZTdmNGE1In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJSGV2aTc3Y3gzcnIrYWNoU053WEpYWHdNL0JFcGVudVM2RThuQzA3M0hQZUFpRUFnYUlOS1FMb3RmSkp5eWtyOHN1WkFlZjhXOU85a3c0OXZTRUppT2JZYU5ZPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVVjNla05EUVhGMVowRjNTVUpCWjBsVlRrcG1lamhyVTNOVmNraEVVVVU0SzFGUldFb3phbVp1Tm5jMGQwUlJXVXBMYjFwSmFIWmpUa0ZSUlV3S1FsRkJkMlpxUlUxTlFXOUhRVEZWUlVKb1RVUldWazVDVFZKTmQwVlJXVVJXVVZGSlJYZHdSRmxYZUhCYWJUbDVZbTFzYUUxU1dYZEdRVmxFVmxGUlNBcEZkekZVV1ZjMFoxSnVTbWhpYlU1d1l6Sk9kazFTV1hkR1FWbEVWbEZSU2tWM01ERk9SR2RuVkZkR2VXRXlWakJKUms0d1RWRTBkMFJCV1VSV1VWRlNDa1YzVlRGT2Vra3pUa1JGV2sxQ1kwZEJNVlZGUTJoTlVWUkhiSFZrV0dkblVtMDVNV0p0VW1oa1IyeDJZbXBCWlVaM01IbE5la0UwVFhwRmQwNXFWVE1LVFZSQ1lVWjNNSGxOZWtFMFRYcEZkMDU2UVROTlZFSmhUVUZCZDFkVVFWUkNaMk54YUd0cVQxQlJTVUpDWjJkeGFHdHFUMUJSVFVKQ2QwNURRVUZUWlFvM2FFcERUelJqUTJaclJXNWhXVkZTU210TFowWlBUR0ZqVGxaNVRXSlFTVzgyZEhORlRIVnBNalE1UTFsVWNIaDNiemh0WkcxdVVuQkpOVlZ4WldGa0NuazJlVWcwV1VSeVRrTjNRV0pNY1hZemVtSllielJKUW1kRVEwTkJXSGQzUkdkWlJGWlNNRkJCVVVndlFrRlJSRUZuWlVGTlFrMUhRVEZWWkVwUlVVMEtUVUZ2UjBORGMwZEJVVlZHUW5kTlJFMUNNRWRCTVZWa1JHZFJWMEpDVW5GdFRUSnNaMWx3Y0RoNlIwUm9XRkJxVVZvcmJXTjFUM0JuZWtGbVFtZE9WZ3BJVTAxRlIwUkJWMmRDVTFablFYZFhhMU0zUmxob01sUmhkV2xOV0d0S1kxaENMMGMzUkVGeFFtZE9Wa2hTUlVKQlpqaEZTVVJCWldkU2VIbGlNa3BzQ21OdVVYVmpNMUpzWTBkb2FHSnRWWFZOYW1oQldqSXhhR0ZYZDNWWk1qbDBUVU4zUjBOcGMwZEJVVkZDWnpjNGQwRlJSVVZJYldnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJJeVpIQmlhVGwyV1ZoV01HRkVRWFZDWjI5eVFtZEZSVUZaVHk5TlFVVkpRa05CVFVodGFEQmtTRUo2VDJrNGRncGFNbXd3WVVoV2FVeHRUblppVXpsellqSmtjR0pwT1haWldGWXdZVVJEUW1sbldVdExkMWxDUWtGSVYyVlJTVVZCWjFJNFFraHZRV1ZCUWpKQlNWbEJDbk5DWm1rMVUyMDFlbWg0U2xGM1RIRkpla2RZWmxkaVpsQmlaa2x4VGtkR2FHTlVSblVyWld0QlFVRkNhV3R3YUhKeVFVRkJRVkZFUVVWamQxSlJTV2dLUVVrek1ISkllRGhVTVhaVmVVaGxRM2Q1V0M4MWJsQjZNbFU0YTBWcE9WRkNaV1l6WTNKWEszTnhNbkZCYVVKWmFrVjZUMk5YTVRjck9WRlRNVU5KVEFwb2MwTkljblJoYlZoV05tNXRNbGhYZFdOU2VuRlVaRzk0ZWtGT1FtZHJjV2hyYVVjNWR6QkNRVkZ6UmtGQlQwTkJaMFZCY0ROS2VGcFNXbTFoWTBaRENuUmtiV2RaWm5SQlFXTkJabXRSVVVsT2NERlJLM3BWYWxVMWVtMWllbmhtVFRVd2NGaEJWWFpTVlVRNFptSlBkRGxOZWxJeGFrcHVTblp1Y0RsVmNXZ0tlbFptWnpSMGRIQkhXVXNyWjB4MmVHZzNXbU5IYVcwNVFYUmtORXBpYTJNMVkyOTFMMWg0UkdkM1NXTjVTbWxhWkc1bGQzbFBNR0YxVm1WMWNGTmxNQXBLYkZsUmJWSkNRMkpFZUVNeVRHWkJLM1k1WW1sR1dsQklUSFJRUm1abWQzVTVhMVF6UVRZM09VTnFaRkpsVURscWNIWndPRFIyYkVWeVVVYzFaRlp0Q25JdllWUkxNMHBJYURKaGEyVXJlazVTTnprNFQyUlFhVkZSUjNsQlMycFJiV3hMTVhkWlNGUkpZbTFCVEhGRlFpOTJjbUlyYm0xSVQzZDJhSE4xTkZnS2JXWkZhMlpOT0daNVJGWk9SMW96T0U5M1EwMTNaRlIwV201Q05GWjNOMHQxV21wNk5YSmxORTEzY1ZsVU5XODVTMng1YjAxUlNub3JkV1V2YVRKbVVBcEhRWGRNYUNzcmRHRnNRa0owWkVaMmVITnphRlJFZEdwdFlsWm1TQ3N6UVVSbWMycE9iMmcwVEhSdWN6WjBiMVV6VlZOTFFqTnFOV0oxVjFGeWIzQjNDa0ZYTW1SRlUxRjRkV1ExYjJSMGFFOWxPVUZPZW5wNGNGVk1jeXRsYTFkdGVraEdNak52TmtoV2VYTmtPRUo1TnpsRk9XSTVabTF2T0ZoR1ZXNTVOWG9LTURaT1MwZ3dhakoyYlVOa2MxcElkblZ1Vm14VlVYWXhSbUpvZGxadVJrMTRNV0o2ZGxwbmNtUnhUV3BxU1RoQ1ExSmpTMDFITTJabE5FVnlkRlJLVmdvMU5GaENlVE5JVlZBdmEwOVJUbTV4T1V4TGFtSTRWVzVCWWtKeFJXZzFUSEp1WVdjMU1YSnRWREZUUVRCaVptMHdaVlkwYkZOeGExVk5Oek5VWVVOU0NucGtaVTEwT1ZCd1JtUkNaWE5aTkhOdmNXWktZVU5KT1RWR1VXTkplRk5pTXpKWWFrWnliMjA1V0M5WU1FSXJNR0o1YmpBeWMwbHZlSHBCWTJsRUszWUtiMFF3Vm5JeVZFbzVRMDVzTmxkV2VqUnlaRkV4Ykd0ck1sVXZlbk51ZHowS0xTMHRMUzFGVGtRZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvPSJ9fX19",
"integratedTime": 1693465031,
"logIndex": 0,
"logID": "a558e7d223efd979dace1b9323ce425f376f9e9c971900e2e1a0a6fdb6af312e"
}
},
"Issuer": "https://github.com/login/oauth",
"Subject": "robert.stephane.28@gmail.com"
}
}
]

Yes !!!!

rekor-cli get --log-index 0 --rekor_server https://rekor.robert.local  --format json | jq -r .
{
"Attestation": "",
"AttestationType": "",
"Body": {
"HashedRekordObj": {
"data": {
"hash": {
"algorithm": "sha256",
"value": "625a6b29719b5c4a9c2d3ee23de4bf88a14c741464e7f047f916602185e7f4a5"
}
},
"signature": {
"content": "MEUCIHevi77cx3rr+achSNwXJXXwM/BEpenuS6E8nC073HPeAiEAgaINKQLotfJJyykr8suZAef8W9O9kw49vSEJiObYaNY=",
"publicKey": {
"content": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUV3ekNDQXF1Z0F3SUJBZ0lVTkpmejhrU3NVckhEUUU4K1FRWEozamZuNnc0d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2ZqRU1NQW9HQTFVRUJoTURWVk5CTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SWXdGQVlEVlFRSApFdzFUWVc0Z1JuSmhibU5wYzJOdk1SWXdGQVlEVlFRSkV3MDFORGdnVFdGeWEyVjBJRk4wTVE0d0RBWURWUVFSCkV3VTFOekkzTkRFWk1CY0dBMVVFQ2hNUVRHbHVkWGdnUm05MWJtUmhkR2x2YmpBZUZ3MHlNekE0TXpFd05qVTMKTVRCYUZ3MHlNekE0TXpFd056QTNNVEJhTUFBd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTZQo3aEpDTzRjQ2ZrRW5hWVFSSmtLZ0ZPTGFjTlZ5TWJQSW82dHNFTHVpMjQ5Q1lUcHh3bzhtZG1uUnBJNVVxZWFkCnk2eUg0WURyTkN3QWJMcXYzemJYbzRJQmdEQ0NBWHd3RGdZRFZSMFBBUUgvQkFRREFnZUFNQk1HQTFVZEpRUU0KTUFvR0NDc0dBUVVGQndNRE1CMEdBMVVkRGdRV0JCUnFtTTJsZ1lwcDh6R0RoWFBqUVorbWN1T3BnekFmQmdOVgpIU01FR0RBV2dCU1ZnQXdXa1M3RlhoMlRhdWlNWGtKY1hCL0c3REFxQmdOVkhSRUJBZjhFSURBZWdSeHliMkpsCmNuUXVjM1JsY0doaGJtVXVNamhBWjIxaGFXd3VZMjl0TUN3R0Npc0dBUVFCZzc4d0FRRUVIbWgwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2IyZHBiaTl2WVhWMGFEQXVCZ29yQmdFRUFZTy9NQUVJQkNBTUhtaDBkSEJ6T2k4dgpaMmwwYUhWaUxtTnZiUzlzYjJkcGJpOXZZWFYwYURDQmlnWUtLd1lCQkFIV2VRSUVBZ1I4QkhvQWVBQjJBSVlBCnNCZmk1U201emh4SlF3THFJekdYZldiZlBiZklxTkdGaGNURnUrZWtBQUFCaWtwaHJyQUFBQVFEQUVjd1JRSWgKQUkzMHJIeDhUMXZVeUhlQ3d5WC81blB6MlU4a0VpOVFCZWYzY3JXK3NxMnFBaUJZakV6T2NXMTcrOVFTMUNJTApoc0NIcnRhbVhWNm5tMlhXdWNSenFUZG94ekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBZ0VBcDNKeFpSWm1hY0ZDCnRkbWdZZnRBQWNBZmtRUUlOcDFRK3pValU1em1ienhmTTUwcFhBVXZSVUQ4ZmJPdDlNelIxakpuSnZucDlVcWgKelZmZzR0dHBHWUsrZ0x2eGg3WmNHaW05QXRkNEpia2M1Y291L1h4RGd3SWN5SmlaZG5ld3lPMGF1VmV1cFNlMApKbFlRbVJCQ2JEeEMyTGZBK3Y5YmlGWlBITHRQRmZmd3U5a1QzQTY3OUNqZFJlUDlqcHZwODR2bEVyUUc1ZFZtCnIvYVRLM0pIaDJha2Urek5SNzk4T2RQaVFRR3lBS2pRbWxLMXdZSFRJYm1BTHFFQi92cmIrbm1IT3d2aHN1NFgKbWZFa2ZNOGZ5RFZOR1ozOE93Q013ZFR0Wm5CNFZ3N0t1Wmp6NXJlNE13cVlUNW85S2x5b01RSnordWUvaTJmUApHQXdMaCsrdGFsQkJ0ZEZ2eHNzaFREdGptYlZmSCszQURmc2pOb2g0THRuczZ0b1UzVVNLQjNqNWJ1V1Fyb3B3CkFXMmRFU1F4dWQ1b2R0aE9lOUFOenp4cFVMcytla1dtekhGMjNvNkhWeXNkOEJ5NzlFOWI5Zm1vOFhGVW55NXoKMDZOS0gwajJ2bUNkc1pIdnVuVmxVUXYxRmJodlZuRk14MWJ6dlpncmRxTWpqSThCQ1JjS01HM2ZlNEVydFRKVgo1NFhCeTNIVVAva09RTm5xOUxLamI4VW5BYkJxRWg1THJuYWc1MXJtVDFTQTBiZm0wZVY0bFNxa1VNNzNUYUNSCnpkZU10OVBwRmRCZXNZNHNvcWZKYUNJOTVGUWNJeFNiMzJYakZyb205WC9YMEIrMGJ5bjAyc0lveHpBY2lEK3YKb0QwVnIyVEo5Q05sNldWejRyZFExbGtrMlUvenNudz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
}
}
}
},
"LogIndex": 0,
"IntegratedTime": 1693465031,
"UUID": "24a2a42f4cfb8941bfe184574ae20e7444b406693ec70484cd661f92fb079a9996c9a1af6ba384a4",
"LogID": "a558e7d223efd979dace1b9323ce425f376f9e9c971900e2e1a0a6fdb6af312e"
}

Excellent non ?

Conclusion

Bon maintenant que la signature fonctionne, il faut passer à l'attachement des attestations dont le certificat d'analyse de vulnérabilités et des attestions de validation d'étapes de build avec in-toto

Source