Aller au contenu principal

Sigstore Self Hosted pour signer des artefacts

· 10 minutes de lecture
Stéphane ROBERT
Consultant DevOps

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