Skip to content

Token Authentication with JWT: "token signed by untrusted key with ID" #3941

@jaydrogers

Description

@jaydrogers

Hi all,

Thanks for putting together and maintaining this project. I'm working with my friend @danpastori and we're running into some issues with integrating authentication between the Docker registry and a Laravel PHP application.

Our goals

We want to have a Laravel application manage the authentication for a Docker regisry using the "token" authentication method.

Authenication

Error we're seeing

We have every thing connected but when we run docker login, this error appears in the logs for the registry container:

token signed by untrusted key with ID: "RBND:K6XB:4IRB:VEDH:NYDG:TO5G:J2PJ:5LGY:NXJO:ONXG:FNLG:QJ76"

Steps we're following

This is how we are configuring our services. Some things have been simplified to minimize confusion.

Step 1: Configure Laravel app & Docker registry via Docker Compose

Here is a simple version of our Docker configuration

docker-compose.yml:

version: '3.8'
services:

  traefik:
    image: traefik:v2.9
    networks:
      development:
        aliases:
          - laravel.dev.test
          - registry.dev.test
    ports:
      - 80:80
      - 443:443

  mariadb: 
    image: mariadb:10.11
    environment:
      MYSQL_ROOT_PASSWORD: "rootpassword"
      MYSQL_DATABASE: "mylaravelapp"
      MYSQL_USER: "mysqluser"
      MYSQL_PASSWORD: "mysqlpassword"

  php:
    image: serversideup/php:8.2-fpm-nginx
    networks:
      - development
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.laravel.rule=HostRegexp(`laravel.dev.test`)"
      - "traefik.http.routers.laravel.entrypoints=websecure"
      - "traefik.http.routers.laravel.tls=true"
      - "traefik.http.services.laravel.loadbalancer.server.port=443"
      - "traefik.http.services.laravel.loadbalancer.server.scheme=https"

  registry:
    image: registry:2
    networks:
      - development
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.registry.rule=HostRegexp(`registry.dev.test`)"
      - "traefik.http.routers.registry.entrypoints=websecure"
      - "traefik.http.routers.registry.tls=true"
      - "traefik.http.services.registry.loadbalancer.server.port=5000"
      - "traefik.http.services.registry.loadbalancer.server.scheme=http"
          

networks:
  development:

Step 2: Configure Registry to authenticate with Laravel PHP

This is what we are running for our registry configuration (some things have been removed to minimize confusion):
/etc/docker/registry/config.yml:

version: 0.1
log:
  level: debug
http:
  addr: :5000
  secret: mysecret
auth:
  token:
    realm: https://laravel.dev.test/registry-token
    service: registry.dev.test
    issuer: mylaravelapp
    rootcertbundle: /certs/rootcertbundle.crt

Step 3: Generate "rootcertbundle.crt"

We've seen a lot of discussion how to do this.

We've tried:

  1. Generating a certificate keypair, setting rootcertbundle to our public CA certificate.
  2. Generating a certificate keypair, setting rootcertbundle to the public certificate (not the CA)
  3. Generating a certificate keypair, creating a custom CA, signing the key with the CA, combining the public certificate of the CA and the generated certificate into a single bundle, then setting rootcertbundle to the CA Public Certificate + JWT Public Certificate

Related comments:

Step 4: Attempt to authenticate with Laravel

Here is the PHP code that we are using to generate a JWT token with a kid signature.

auth.php:

<?php

namespace App\Services\Registry;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Base32\Base32;

class CreateAccessTokenFirebaseJwt
{
    public function create()
    {
        $privateKey = file_get_contents( storage_path('keys/private.key') );

        $publicKey = file_get_contents( storage_path('keys/private.key') );

        $payload = [
            "iss" => "mylarvalapp",
            "aud" => "registry.dev.test",
            "iat" => time(),
            "nbf" => time(),
            "exp" => time() + 3600,
            "sub" => "myuser",
            "jti" => strtoupper(bin2hex(openssl_random_pseudo_bytes(16))),
            "access" => [
                [
                    "type" => "repository",
                    "name" => "serversideup/financial-freedom",
                    "actions" => [
                        "pull",
                        "push"
                    ]
                ]
            ]
        ];

        $jwt = JWT::encode($payload, $privateKey, 'ES256', $this->getKid( $publicKey ));
        return response()->json([
            'token' => $jwt
        ]);
    }

    private function getKid( $publicKey )
    {
        return implode(':', array_slice(
            str_split(
                rtrim(
                    Base32::encode( hash('sha256', $publicKey, true) ), '='
                ), 4
            ), 0, 12)
        );
    }
}

Docs we've been following

We've been using these docs trying to answer our questions as well but continue to see this error whenever we use docker login from a fresh docker client.

Questions

  1. Are we generating the JWT and kid correctly?
  2. What exactly is rootcertbundle expecting?

If anyone has any experience or areas they can point us in, that would be great! We've been working on this issue for days and are pretty stumped.

Thank you for your help! 🙌

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions