SUSE / Portus

Authorization service and frontend for Docker registry (v2)
http://port.us.org/
Apache License 2.0
3k stars 471 forks source link

Authorization token including hostname #2204

Closed mettke closed 5 years ago

mettke commented 5 years ago

Description

I'm trying to use the docker registry, portus, and notary together. The registry and notary both are using portus as an authentication provider.

Steps to reproduce

  1. Setup portus
  2. Setup docker registry and notary both with portus as auth token provider
  3. Try to push images

What happens is that the following token is requested:

portus_1            |   Parameters: {"account"=>"mettke", "scope"=>"repository:registry.itmettke.de/test/alpine:push,pull", "service"=>"registry.itmettke.de"}

Portus should be able to handle the hostname inside the scope using test and alpine as reference for the repository

Portus is parsing the string using this very simple command:

@namespace_name = @resource_name.split("/").first

This leads to the hostname being used as namespace name failing the authorization process.

This is my first try to solve this problem:

if @resource_name.count("/") == 2
  @tmp, @namespace_name, @tmp2 = @resource_name.split("/", 3)
else
  @namespace_name = @resource_name.split("/").first
end

It works, however it migth not be the best way to solve this

The hostname might contain a port reference like:

portus_1            |   Parameters: {"account"=>"mettke", "scope"=>"repository:registry.itmettke.de:5000/test/alpine:push,pull", "service"=>"registry.itmettke.de"}

This leads to another problem in this part:

str = scope_string.split(":", 3)
raise ScopeNotHandled, "Wrong format for scope string" if str.length != 3

Instead the code should be able to handle four parts as well

https://github.com/theupdateframework/notary/issues/1023

stale[bot] commented 5 years ago

Thanks for all your contributions! This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

Jean-Baptiste-Lasselle commented 4 years ago

hi @mettke Ihaveseen other people in the community, complaining about that stale bot, so regardless of that bee :

I will address the issue in the next weeks, have a question I want to hear your answer to (will help me a lot to listen to your answer, before doing anything and building my opinion, which I will share and backup with running examples) :

What If you used a Keycloak Open ID Provider, independently of the running portus, to provide authentication to your notary service : Do you _have_to make portus Auth Service, the notary auth service?

Ok, what you want, is :

Okay, So in the end, what you want is to be logged in notary, when you docker login. I have seen a similar pattern, and :

I 'm definitely on that issue.

Alright, I kind of all in all already answered my own question, but do you confirm I haven't missed any aspect of your idea / need?

Jean-Baptiste-Lasselle commented 4 years ago

hi back @mettke simple question, is this your use case:

export OCI_REGISTRY_SERVICE_NETNAME=oci-registry.pegasusio.io
export OCI_NOTARY_SERVICE_NETNAME=notary.pegasusio.io

export OCI_IMAGE_SIGNER_NOTARY_NAME=jeanbl
export OCI_IMAGE_SIGNER_NOTARY_PASSPHRASE="that1smys3cretpa22phraseyeah"

# That's what portus calls "repository namespace", and
# that's what I propose we also call that a "relative namespace", because 
# that namespace does not mention to which registry it belongs. So : 

export OCI_IMAGE_REPOSITORY_RELATIVE_NAMESPACE=myapp

# - About [OCI_IMAGE_REPOSITORY_RELATIVE_NAMESPACE]  
# => it could be considered as kind of belonging to the local registry actually... (not a local registry 
# server you launch on the same machine where you docker push, listening on 5000. No, I mean 
# here the local registry, where the images you docker pull are stored on your file system. Like a 
# local  git repo.
# => But that namespace could be considered as belonging to the default configured registry : 
# not 'docker.io', but the private registry you configured, which is where you push images without 
# specifying a domain / network name of the (private) registry service you want to push to. 
# => And that's why I refuse to call it local, because it is not local, it implicitly belongs to the default registry configured for the docker clients. The default here considered a bit as the path back to the root of filesystem....
# => And what I would call a namespace actually belonging to the local registry, is a namespace of the form localhost:5000/mynamespace/ even if you would think of the notion So all in all, we can think of the notion of a namespace belonging to here

# --- 
# That's what I propose we call "a fully qualified namespace"
export OCI_IMAGE_REPOSITORY_FQ_NAMESPACE=${OCI_REGISTRY_SERVICE_NETNAME}/myapp
# and I propose we consider "repository namespace" to be either "a relative namespace", or 
# a fully qualified one as defined above.
# 
# ---
# that's what we could call a "repository name",
export OCI_IMAGE_REPOSITORY_NAME=myapp-componentone
# that's what we could calI  the "Fully Qualified Name", of an OCI image repository
export OCI_IMAGE_REPOSITORY_FQN=$OCI_IMAGE_REPOSITORY_FQ_NAMESPACE/$OCI_IMAGE_REPOSITORY_NAME
# ---
# that's what we could calI  the "relative name", of an OCI image repository, which is 
# relative, not absolute, because it does not mention from which registry, it is 
# coming from, or will go to for the first time, cause it has never ever been in a 
# registry before, it's just born (built). And
# that's why we must use at some steps, relative naming of images to work locally 
# with them, without the image having to belong to any registry yet. 
# --- 
export OCI_IMAGE_REPOSITORY_RN=$OCI_IMAGE_REPOSITORY_RELATIVE_NAMESPACE/$OCI_IMAGE_REPOSITORY_NAME

# I assume here you build, or built on this
# containerization host, an OCI container image named  
#  $OCI_IMAGE_REPOSITORY_RN:12.0.2-alpine 
# 

export OCI_IMAGE_PUBLISHING_NAME=$OCI_REGISTRY_SERVICE_NETNAME/$OCI_IMAGE_REPOSITORY_RN:12.0.2-alpine

docker tag $OCI_IMAGE_REPOSITORY_RN:12.0.2-alpine $NOM_DE_PUBLICATION_OCI_IMAGE

# ---
# generate private/public key pair
# You can use the below command (will load the
# key to the local trust store at same time)
# to run it silently,set the passphrase with
# export DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE="kwhatever"
# ---
# docker trust key generate $OCI_IMAGE_REPOSITORY_NAME
# ---
# Or you can use : 
# https://ruimarinho.gitbooks.io/yubikey-handbook/content/docker-content-trust/delegation-roles/generating-a-delegation-key.html
# 
# pre-existing private key : 
export OCI_SIGNER_PRIVATE_KEY=./$OCI_IMAGE_SIGNER_NOTARY_NAME-$OCI_IMAGE_REPOSITORY_NAME-oci-signinkey.pem
# pre-existing public key : 
export OCI_SIGNER_PUBLIC_KEY=./$OCI_IMAGE_SIGNER_NOTARY_NAME-$OCI_IMAGE_REPOSITORY_NAME-oci-signincert.pem

# 
# --- # [docker trust key load]
# Add the  private key, called the _delegation_ private key, to
# the local Docker trust repository. 
# (By default this local trust repository is in  '~/.docker/trust/').
# 

# --- 
# To run [docker trust key load ...] silently (non-interactively), we can use the
#  DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE
# environment variable to set the passphrase
# --- 

export DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$OCI_IMAGE_SIGNER_NOTARY_PASSPHRASE

# --- 
# Credits for adding this configuration options to @mettke , see 
# https://github.com/SUSE/Portus/issues/2204#issuecomment-590701916
# 
# setting [DOCKER_CONTENT_TRUST_SERVER] allows
# us to tell the docker client where is the notary service, how to reach it for every 
# [docker trust *] command, and when DOCKER_CONTENT_TRUST is additonally set 
# to 1, then for almost every [docker *] command, like checking signature at 
# [docker pull], and reject the layer downloads if signature checks fail, or successful 
# checks of signatures are insufficient due to TRUST TRESHOLD configuration on of 
# the notary service.
# ---

# --- 
# assumes the notary service is
# reachable at 'notary.pegasusio.io' network name
export DOCKER_CONTENT_TRUST_SERVER=$OCI_NOTARY_SERVICE_NETNAME

# -- 
# ok so now let's do this silently
docker trust key load $OCI_SIGNER_PRIVATE_KEY --name $OCI_IMAGE_SIGNER_NOTARY_NAME

# --- #
# register a new signer to the notary server
# the signer is given a name, identified by its
# public key (pubic Certificate), and will be a 
# signer only for the specified given
# repo, $OCI_IMAGE_PUBLISHING_NAME, not 
# any other.
# --- # 
# DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE is 
# set, so the signer add command will not 
# interactively  ask for the passphrase, while registering its signer but pick
# up the env variable value in the stead.
# --- 

docker trust signer add --key cert.pem $OCI_IMAGE_SIGNER_NOTARY_NAME $OCI_IMAGE_PUBLISHING_NAME

# --- # 
# DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE is 
# set, so the signer add command will not 
# interactively  askfor the passphrase, but pick
# up the env variable value in the stead.
# --- 

docker trust sign $NOM_DE_PUBLICATION_OCI_IMAGE

# --- #
# setting DOCKER_CONTENT_TRUST to 1 will cause
# the docker client, every time you [docker push] to
# go and ask the notary service :
# Hey, Do you trust me ? Of course if answer is no...
# Can that option be forced from the regsitry ? 
# Can we Configure registry, so that no image is
# allowed in, unless it is signed by a notary-trusted ? 
# --- 
# https://dev.to/idevkamboj/care-about-security-enable-docker-content-trust-4djh
# 

# Now, additionally to 
# the [DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE]
# env.var., we set DOCKER_CONTENT_TRUST
#
# setting DOCKER_CONTENT_TRUST  to 1 will cause the
# docker client to : 
# => check signature of image, every time you docker pull
# => check signature of image, every time you docker run
# => sign the image with $OCI_SIGNER_PRIVATE_KEY, every time you docker push
# 
# So, content trust is like CRITICAL, to setting up any app's 
# production grade dockerized environment : 
# 
# Of course, your boss wants you to run **only** trusted image in 
# production
# 
# And last, but not least, Justin Cormack, and The Update Framework, want us to
# run only trusted binaries. See also coreos automatic updates
# --- 

export DOCKER_CONTENT_TRUST=1

docker push $NOM_DE_PUBLICATION_OCI_IMAGE

?

If any difference with what you want to do, please don't be shy about correcting my assumptions.

Waiting for you to confirm this is your exact use case.

Jean-Baptiste-Lasselle commented 4 years ago

note :

mettke commented 4 years ago

Hey @Jean-Baptiste-Lasselle,

its been a while by now but I will try to remember as much as possible.

What you said so far looks like what I was trying to do except that you are missing the DOCKER_CONTENT_TRUST_SERVER variable which is set to domain of the notary server. Otherwise you would by default use the official docker notary service.

When I remember correctly the problematic token from above is not requested from docker login but from notary. Notary itself can be configured for multiple domains and thus appends the domain to the request.

Jean-Baptiste-Lasselle commented 4 years ago

hi @mettke Thank you so much for this quick feedback ! (all tests are run against portus 2.4.3) Excellent :

So yeah, I am l aware this is why notary uses what I call fully qualified namespaces, as defined in my updated script above.

But I am now investigating the whole request / response flow involved in the scenario I precisely describe with that script above :

portus_1            |   Parameters: {"account"=>"mettke", "scope"=>"repository:registry.itmettke.de/test/alpine:push,pull", "service"=>"registry.itmettke.de"}

---

But this one for sure, does send a request to

notary server configured with [DOCKER_CONTENT_TRUST_SERVER]

so it does trigger docker login, like a docker push ot private regsistry would, doesn't it ?

docker trust signer add --key cert.pem $OCI_IMAGE_SIGNER_NOTARY_NAME $OCI_IMAGE_PUBLISHING_NAME

  * the docker client sends a first request to upload data to `notary` : the notary client, included in, or called by, the docker client, does that.
  * the `notary` service answers with a **401 Unauthorized** HTTP error code _buddy, you don't have a token, please ask for a token to https://portus.pegasusio.io/v2/token , and come back here_
  * and there you get your log line in `portus`, loggin the following request from the docker client, to ask for a `Bearer token` 
  * **But wait, no** what you specified is that **you tried and `docker push` first** without caring about any signer key pair : Is it what you actually did ? 
  * All in all, I was really wondering what the following log line actually means, in the mind of `portus`  : 

portus_1 | Parameters: {"account"=>"docker", "client_id"=>"docker", "offline_token"=>"true", "service"=>"oci-registry.pegasusio.io"}

  * Ok, I was puzzled, So I ran a few tests, to determine how the hell you could have got that log, with the scenario you describe to reproduce. 
  * Here are my detailed tests results : https://github.com/SUSE/Portus/issues/2204#issuecomment-591111720
  * and what I think, is that what fails in your particular case, has nothing to do with your notary private server.
  * I tell you why :  you will check for yourself, that `docker login` failing or not, with or without `DOCKER_CONTENT_TRUST_SERVER`, well there is **never** any log line mentioning for example , the string `push,pull`
  * But the exact same line appears for me, at home, but absolutely not when I docker login, but when I docker push an image, without having successfully logged in with `docker login`, look what I get then, in my logs (can reproduce it any time) : 

<pre>
portuscontainer                     | Started GET "/v2/token?scope=repository%3Ajbladmin%2Fnode%3Apush%2Cpull&service=oci-registry.pegasusio.io" for 192.168.1.22 at 2020-02-25 21:55:36 +0000
portuscontainer                     | Processing by Api::V2::TokensController#show as JSON
portuscontainer                     |   Parameters: {"scope"=>"repository:jbladmin/node:push,pull", "service"=>"oci-registry.pegasusio.io"}
portuscontainer                     |   Registry Load (0.3ms)  SELECT  `registries`.* FROM `registries` WHERE `registries`.`hostname` = 'oci-registry.pegasusio.io' LIMIT 1
portuscontainer                     | No hostname matching `oci-registry.pegasusio.io', testing external_hostname
portuscontainer                     |   Registry Load (0.5ms)  SELECT  `registries`.* FROM `registries` WHERE `registries`.`external_hostname` = 'oci-registry.pegasusio.io' LIMIT 1
portuscontainer                     |   Namespace Load (0.4ms)  SELECT  `namespaces`.* FROM `namespaces` WHERE `namespaces`.`registry_id` = 1 AND `namespaces`.`name` = 'jbladmin' LIMIT 1
portuscontainer                     | action push not handled/authorized, removing from actions
portuscontainer                     |   CACHE (0.0ms)  SELECT  `namespaces`.* FROM `namespaces` WHERE `namespaces`.`registry_id` = 1 AND `namespaces`.`name` = 'jbladmin' LIMIT 1  [["registry_id", 1], ["name", "jbladmin"]]
portuscontainer                     | action pull not handled/authorized, removing from actions
portuscontainer                     | [jwt_token] [claim] {:iss=>"portus.pegasusio.io", :sub=>nil, :aud=>"oci-registry.pegasusio.io", :iat=>1582667736, :nbf=>1582667731, :exp=>1582668636, :jti=>"BX4juX3XGDT8db6iNg6PWdbqviX9yWBLqGX1Rto2XL", :access=>[]}
portuscontainer                     | Completed 200 OK in 52ms (Views: 0.7ms | ActiveRecord: 3.2ms)

</pre>

  * So , if all this was just a problem with notary not being able to authenticate, then we would look at `notary` logs, and most of all _why would `portus` (or `registry`) reject a push?_ 
  * Only reason I can think of, is that `portus` would check validity of signatures, and refuses the push if any trust problem : Ok, given the history between portus and notary (type `notary` in the issues search...), I think portus is far from being able to do that, or I'm gonna learn another great thing about portus ! 
  * eg here an official suse team in 2018 stating portus has zero support (meaning they won't talk about it) for content trust : 
    * in this issue comment, a community apparently expert in `portus`, states there's zero support for content trust  : https://github.com/SUSE/Portus/issues/1882#issuecomment-405265819
    * and in this issue, @mssola himself confirms : https://github.com/SUSE/Portus/issues/1882#issuecomment-408040926
    * last but not least Google results first page with string `portus notary` (self-explanatory) : 

![self](https://github.com/Jean-Baptiste-Lasselle/for-fellow-developers/raw/master/docuementation/impr.ecrans/portus/PORTUS_AND_CONTENT_TRUST_Firefox_Screenshot_2020-02-26T00-50-25.164Z.png) 

  * the log line you gave us : 

portus_1 | Parameters: {"account"=>"mettke", "scope"=>"repository:registry.itmettke.de/test/alpine:push,pull", "service"=>"registry.itmettke.de"}

  * And below is the exact same log, but on my infra at home, failing to docker push because I did not successfully logged in first : 

portuscontainer | Parameters: {"scope"=>"repository:jbladmin/node:push,pull", "service"=>"oci-registry.pegasusio.io"}

  * So all in all, the line you gave us is just a part of portus telling (someone) :  _hey man, you have to docker login, before I can tell you if you're allowed to push_
  * what bothers me though, is how could the `registry.itmettke.de`  domain name for your private registry, be inserted in ... Is this the `notary` trying to push to registry ...??? **Is  `notary` supposed to push anything to `registry` ...? I'm really asking but that would surprise me a lot**
  * even further, that line rang may bells inmy mind, and suddenly I remembered : the docker official docs, [here](https://github.com/docker/distribution/blob/master/docs/spec/auth/token.md#how-to-authenticate) , look at the example _**HTTP Response Header indicating the auth challenge:**_ given : 

Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push"


  * oh, `scope="repository:samalba/my-app:pull,push"` that what was ringing a bell! I note here they don't include the registry's domain / network name there.
  * And I confirm that when i test a `docker push`, without successfully `docker login` first, whether `DOCKER_CONTENT_TRUST_SERVER` is set or unset, regardless to its value, and whether `DOCKER_CONTENT_TRUST` is set to 1or not, it does not change a character in my logs. 
  * Will those same logs change if I additionnaly sign my `node` freshly pulled image , before trying to push it to my private registry ? Are we considering that the docker client might send a different ID to identify the exact same docker repository, just because it takes account of trust management, setting `DOCKER_CONTENT_TRUST*` env. vars ...?  
  * now : Did you successfully run `docker login registry.insideyourcompany.io` , before trying to `docker push` ?  
  * I assume yeah, so now we need a bit more of infos to reproduce your case : 
    * docker-compose.yml, 
    * env. files 
    * files mapped into volumes (except secrets)
    * larger, larger logs, if you don't want to spend time identifying which lines are related to which action in your scenario.
    * waiting for all that ! Whatever that is, I definitely am on the subject of making portus work with `notary`, thanks to you!

Also, I can't help asking  , did you configure your `notary` with  : 
* something like the `ROOT_CERT_BUNDLE` for `notary` , with value the path to the portus public certificate e.g. `portus.crt` ? That, for notary to accept portus token as valid auth token . Here we talk about the docker client's  auth to `notary` (update the day after i wrote this : yes there exist such a config, https://docs.docker.com/notary/reference/server-config/#auth-section-optional )
* something like the secret, or Bearer Token :  and here we are talking about `notary`, authenticating to portus, to then get a registry auth token.  Like when a human user has to log  into portus webui, using his portus credentials, whatever they are, and then create an _application token_, which can be used to authenticate to the registry, using `docker login`, or `curl` if you are dan walsh. _Update the day after i wrote this : I did not find any notiffication webhook, for the notary server, no exist such a config, is referenced in https://docs.docker.com/notary/reference/server-config/#auth-section-optional . And also, I today think I don't even know why a notary server should use a webhook to send notification of an event (to notify who and about what event?)_

Thank you so much for your help there, because for the time being, it is you, helping me, and I commit to give back :

* I will solve this issue, whatever it takes. 
* Goal : 
  * a proof of concept prototype working out of the box, that fulfills the trust management policy scenario, defined in my script, 
  * and with seamless integration to automated updates solutions, like https://github.com/IBM/portieris or `grafeas/kritis`. (So I'll have to get one of those up and running, for the proof of concept)
Jean-Baptiste-Lasselle commented 4 years ago

How did your team managed since may 2019 ?

Also, I wanna ask :

Jean-Baptiste-Lasselle commented 4 years ago

Testing the login process portus logs against DOCKER_CONTENT_TRUST_SERVER

all tests are run against portus 2.4.3

I create the first super admin user with username jbladmin, using the portus webui

Without the DOCKER_CONTENT_TRUST_SERVER env var

background_1                        |   RegistryEvent Load (0.2ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.2ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
registry_1                          | time="2020-02-25T22:05:35Z" level=warning msg="error authorizing context: authorization token required" go.version=go1.7.6 http.request.host=oci-registry.pegasusio.io http.request.id=f43ca8b2-5ca1-491e-acb5-2c4adc7d66f4 http.request.method=GET http.request.remoteaddr=192.168.1.22 http.request.uri="/v2/" http.request.useragent="docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))" instance.id=b87dc65a-4fdf-43ae-8975-c276ec1eaaf0 version=v2.6.2-14-ga66a4c3 
registry_1                          | 172.18.0.1 - - [25/Feb/2020:22:05:35 +0000] "GET /v2/ HTTP/1.0" 401 87 "" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))"
nginx_1                             | 192.168.1.22 - - [25/Feb/2020:22:05:35 +0000] "GET /v2/ HTTP/1.1" 401 87 "-" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \x5C(linux\x5C))"
portuscontainer                     | Started GET "/v2/token?account=jbladmin&client_id=docker&offline_token=true&service=oci-registry.pegasusio.io" for 192.168.1.22 at 2020-02-25 22:05:35 +0000
portuscontainer                     | Processing by Api::V2::TokensController#show as JSON
portuscontainer                     |   Parameters: {"account"=>"jbladmin", "client_id"=>"docker", "offline_token"=>"true", "service"=>"oci-registry.pegasusio.io"}
portuscontainer                     |   User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'jbladmin' LIMIT 1
portuscontainer                     |   ApplicationToken Load (0.5ms)  SELECT `application_tokens`.* FROM `application_tokens` WHERE `application_tokens`.`user_id` = 2
portuscontainer                     |   User Load (0.5ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'jbladmin'  ORDER BY `users`.`id` ASC LIMIT 1
portuscontainer                     |    (2.0ms)  BEGIN
portuscontainer                     |   SQL (0.5ms)  UPDATE `users` SET `failed_attempts` = 1, `updated_at` = '2020-02-25 22:05:35' WHERE `users`.`id` = 2
portuscontainer                     |    (0.3ms)  COMMIT
portuscontainer                     | Completed 401 Unauthorized in 106ms (ActiveRecord: 5.5ms)
background_1                        |   RegistryEvent Load (0.3ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.2ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   Tag Exists (0.1ms)  SELECT  1 AS one FROM `tags` WHERE `tags`.`scanned` = 0 LIMIT 1
background_1                        |   RegistryEvent Load (0.2ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.2ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.2ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.2ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
registry_1                          | 172.18.0.1 - - [25/Feb/2020:22:16:07 +0000] "GET /v2/ HTTP/1.0" 401 87 "" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))"
nginx_1                             | 192.168.1.22 - - [25/Feb/2020:22:16:07 +0000] "GET /v2/ HTTP/1.1" 401 87 "-" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \x5C(linux\x5C))"
portuscontainer                     | Started GET "/v2/token?account=jbladmin&client_id=docker&offline_token=true&service=oci-registry.pegasusio.io" for 192.168.1.22 at 2020-02-25 22:16:07 +0000
portuscontainer                     | Processing by Api::V2::TokensController#show as JSON
portuscontainer                     |   Parameters: {"account"=>"jbladmin", "client_id"=>"docker", "offline_token"=>"true", "service"=>"oci-registry.pegasusio.io"}
portuscontainer                     |   User Load (0.8ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'jbladmin' LIMIT 1
portuscontainer                     |   ApplicationToken Load (0.4ms)  SELECT `application_tokens`.* FROM `application_tokens` WHERE `application_tokens`.`user_id` = 2
portuscontainer                     |   User Load (0.7ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'jbladmin'  ORDER BY `users`.`id` ASC LIMIT 1
portuscontainer                     |    (0.5ms)  BEGIN
portuscontainer                     |   SQL (0.5ms)  UPDATE `users` SET `failed_attempts` = 0, `updated_at` = '2020-02-25 22:16:07' WHERE `users`.`id` = 2
portuscontainer                     |    (0.3ms)  COMMIT
portuscontainer                     |    (0.2ms)  BEGIN
portuscontainer                     |   User Exists (0.5ms)  SELECT  1 AS one FROM `users` WHERE (`users`.`username` = BINARY 'jbladmin' AND `users`.`id` != 2) LIMIT 1
portuscontainer                     |   SQL (0.4ms)  UPDATE `users` SET `current_sign_in_at` = '2020-02-25 22:16:07', `current_sign_in_ip` = '192.168.1.22', `sign_in_count` = 2, `updated_at` = '2020-02-25 22:16:07' WHERE `users`.`id` = 2
portuscontainer                     |    (0.3ms)  COMMIT
portuscontainer                     |   Registry Load (0.3ms)  SELECT  `registries`.* FROM `registries` WHERE `registries`.`hostname` = 'oci-registry.pegasusio.io' LIMIT 1
portuscontainer                     | No hostname matching `oci-registry.pegasusio.io', testing external_hostname
portuscontainer                     |   Registry Load (0.3ms)  SELECT  `registries`.* FROM `registries` WHERE `registries`.`external_hostname` = 'oci-registry.pegasusio.io' LIMIT 1
portuscontainer                     | [jwt_token] [claim] {:iss=>"portus.pegasusio.io", :sub=>"jbladmin", :aud=>"oci-registry.pegasusio.io", :iat=>1582668967, :nbf=>1582668962, :exp=>1582669867, :jti=>"DtiHcBHNzXdynXZkC47cL11ReGddfrnDRFYBw9D8mR"}
portuscontainer                     | Completed 200 OK in 113ms (Views: 0.2ms | ActiveRecord: 7.5ms)
registry_1                          | time="2020-02-25T22:16:07Z" level=info msg="response completed" go.version=go1.7.6 http.request.host=oci-registry.pegasusio.io http.request.id=f519d60f-d072-4fe2-b7c9-2613878f0da3 http.request.method=GET http.request.remoteaddr=192.168.1.22 http.request.uri="/v2/" http.request.useragent="docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))" http.response.contenttype="application/json; charset=utf-8" http.response.duration=1.313059ms http.response.status=200 http.response.written=2 instance.id=b87dc65a-4fdf-43ae-8975-c276ec1eaaf0 version=v2.6.2-14-ga66a4c3 
registry_1                          | 172.18.0.1 - - [25/Feb/2020:22:16:07 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))"
nginx_1                             | 192.168.1.22 - - [25/Feb/2020:22:16:07 +0000] "GET /v2/ HTTP/1.1" 200 2 "-" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \x5C(linux\x5C))"
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   Tag Exists (0.2ms)  SELECT  1 AS one FROM `tags` WHERE `tags`.`scanned` = 0 LIMIT 1
background_1                        |   RegistryEvent Load (0.3ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000

Now with, the DOCKER_CONTENT_TRUST_SERVER env var

first, I docker logout of course, to wipe out base64-encoded credentials from ~/.docker/config.json

background_1                        |   Tag Exists (0.2ms)  SELECT  1 AS one FROM `tags` WHERE `tags`.`scanned` = 0 LIMIT 1
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.3ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
nginx_1                             | 192.168.1.22 - - [25/Feb/2020:22:32:36 +0000] "GET /v2/ HTTP/1.1" 401 87 "-" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \x5C(linux\x5C))"
registry_1                          | time="2020-02-25T22:32:36Z" level=warning msg="error authorizing context: authorization token required" go.version=go1.7.6 http.request.host=oci-registry.pegasusio.io http.request.id=847821c9-69ca-4892-9369-330d0986e68e http.request.method=GET http.request.remoteaddr=192.168.1.22 http.request.uri="/v2/" http.request.useragent="docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))" instance.id=b87dc65a-4fdf-43ae-8975-c276ec1eaaf0 version=v2.6.2-14-ga66a4c3 
registry_1                          | 172.18.0.1 - - [25/Feb/2020:22:32:36 +0000] "GET /v2/ HTTP/1.0" 401 87 "" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))"
portuscontainer                     | Started GET "/v2/token?account=jbladmin&client_id=docker&offline_token=true&service=oci-registry.pegasusio.io" for 192.168.1.22 at 2020-02-25 22:32:36 +0000
portuscontainer                     | Processing by Api::V2::TokensController#show as JSON
portuscontainer                     |   Parameters: {"account"=>"jbladmin", "client_id"=>"docker", "offline_token"=>"true", "service"=>"oci-registry.pegasusio.io"}
portuscontainer                     |   User Load (1.0ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'jbladmin' LIMIT 1
portuscontainer                     |   ApplicationToken Load (0.3ms)  SELECT `application_tokens`.* FROM `application_tokens` WHERE `application_tokens`.`user_id` = 2
portuscontainer                     |   User Load (0.6ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'jbladmin'  ORDER BY `users`.`id` ASC LIMIT 1
portuscontainer                     |    (0.4ms)  BEGIN
portuscontainer                     |   SQL (0.5ms)  UPDATE `users` SET `failed_attempts` = 1, `updated_at` = '2020-02-25 22:32:36' WHERE `users`.`id` = 2
portuscontainer                     |    (0.4ms)  COMMIT
portuscontainer                     | Completed 401 Unauthorized in 112ms (ActiveRecord: 4.5ms)
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.3ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   Tag Exists (0.3ms)  SELECT  1 AS one FROM `tags` WHERE `tags`.`scanned` = 0 LIMIT 1

background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   Tag Exists (0.2ms)  SELECT  1 AS one FROM `tags` WHERE `tags`.`scanned` = 0 LIMIT 1
registry_1                          | time="2020-02-25T22:33:51Z" level=warning msg="error authorizing context: authorization token required" go.version=go1.7.6 http.request.host=oci-registry.pegasusio.io http.request.id=6edb93ea-ccef-4e1b-9dfc-4fbeaf157004 http.request.method=GET http.request.remoteaddr=192.168.1.22 http.request.uri="/v2/" http.request.useragent="docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))" instance.id=b87dc65a-4fdf-43ae-8975-c276ec1eaaf0 version=v2.6.2-14-ga66a4c3 
registry_1                          | 172.18.0.1 - - [25/Feb/2020:22:33:51 +0000] "GET /v2/ HTTP/1.0" 401 87 "" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))"
nginx_1                             | 192.168.1.22 - - [25/Feb/2020:22:33:51 +0000] "GET /v2/ HTTP/1.1" 401 87 "-" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \x5C(linux\x5C))"
portuscontainer                     | Started GET "/v2/token?account=jbladmin&client_id=docker&offline_token=true&service=oci-registry.pegasusio.io" for 192.168.1.22 at 2020-02-25 22:33:51 +0000
portuscontainer                     | Processing by Api::V2::TokensController#show as JSON
portuscontainer                     |   Parameters: {"account"=>"jbladmin", "client_id"=>"docker", "offline_token"=>"true", "service"=>"oci-registry.pegasusio.io"}
portuscontainer                     |   User Load (0.7ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'jbladmin' LIMIT 1
portuscontainer                     |   ApplicationToken Load (1.2ms)  SELECT `application_tokens`.* FROM `application_tokens` WHERE `application_tokens`.`user_id` = 2
portuscontainer                     |   User Load (0.7ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'jbladmin'  ORDER BY `users`.`id` ASC LIMIT 1
portuscontainer                     |    (0.4ms)  BEGIN
portuscontainer                     |   SQL (0.5ms)  UPDATE `users` SET `failed_attempts` = 0, `updated_at` = '2020-02-25 22:33:52' WHERE `users`.`id` = 2
portuscontainer                     |    (0.3ms)  COMMIT
portuscontainer                     |    (0.3ms)  BEGIN
portuscontainer                     |   User Exists (0.6ms)  SELECT  1 AS one FROM `users` WHERE (`users`.`username` = BINARY 'jbladmin' AND `users`.`id` != 2) LIMIT 1
portuscontainer                     |   SQL (0.7ms)  UPDATE `users` SET `last_sign_in_at` = '2020-02-25 22:16:07', `current_sign_in_at` = '2020-02-25 22:33:52', `last_sign_in_ip` = '192.168.1.22', `sign_in_count` = 3, `updated_at` = '2020-02-25 22:33:52' WHERE `users`.`id` = 2
portuscontainer                     |    (0.2ms)  COMMIT
portuscontainer                     |   Registry Load (0.2ms)  SELECT  `registries`.* FROM `registries` WHERE `registries`.`hostname` = 'oci-registry.pegasusio.io' LIMIT 1
portuscontainer                     | No hostname matching `oci-registry.pegasusio.io', testing external_hostname
portuscontainer                     |   Registry Load (0.6ms)  SELECT  `registries`.* FROM `registries` WHERE `registries`.`external_hostname` = 'oci-registry.pegasusio.io' LIMIT 1
portuscontainer                     | [jwt_token] [claim] {:iss=>"portus.pegasusio.io", :sub=>"jbladmin", :aud=>"oci-registry.pegasusio.io", :iat=>1582670032, :nbf=>1582670027, :exp=>1582670932, :jti=>"EQeJNCEC2fgySoTdFpVDPcJvwGT5wX3Jqa2PBGXzow"}
portuscontainer                     | Completed 200 OK in 118ms (Views: 0.2ms | ActiveRecord: 6.3ms)
registry_1                          | time="2020-02-25T22:33:52Z" level=info msg="response completed" go.version=go1.7.6 http.request.host=oci-registry.pegasusio.io http.request.id=4f2071ef-d684-4637-bf9d-2103b7c1e8d7 http.request.method=GET http.request.remoteaddr=192.168.1.22 http.request.uri="/v2/" http.request.useragent="docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))" http.response.contenttype="application/json; charset=utf-8" http.response.duration=1.377583ms http.response.status=200 http.response.written=2 instance.id=b87dc65a-4fdf-43ae-8975-c276ec1eaaf0 version=v2.6.2-14-ga66a4c3 
registry_1                          | 172.18.0.1 - - [25/Feb/2020:22:33:52 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \\(linux\\))"
nginx_1                             | 192.168.1.22 - - [25/Feb/2020:22:33:52 +0000] "GET /v2/ HTTP/1.1" 200 2 "-" "docker/19.03.5 go/go1.12.12 git-commit/633a0ea838 kernel/4.9.0-12-amd64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.5 \x5C(linux\x5C))"
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.4ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000
background_1                        |   RegistryEvent Load (0.3ms)  SELECT  `registry_events`.* FROM `registry_events` WHERE `registry_events`.`status` = 2  ORDER BY `registry_events`.`id` ASC LIMIT 1000

Jean-Baptiste-Lasselle commented 4 years ago

@mettke If @mssola reopens this issue, I'll provide open suse team all of my portus / TUF proof of concept, and i'll additionally agree with any task the suse team ask me to complete, as long as it can be done in less than 5 working days.

:)

Jean-Baptiste-Lasselle commented 4 years ago

About the registry (Docker Distribution, alright) , and the notary auth. services

I want to point out one thing here :

# -- # 
$ docker login dtr.example.com/user/repo
Username: admin
Password:

Login Succeeded

$ docker trust signer add --key cert.pem jeff dtr.example.com/user/repo
Adding signer "jeff" to dtr.example.com/user/repo...
Initializing signed repository for dtr.example.com/user/repo...
Successfully initialized "dtr.example.com/user/repo"
Successfully added signer: jeff to dtr.example.com/user/repo

Notary can be configured to talk to the same token authentication service that the Docker Registry V2 spec uses. In the case of DTR, DTR provides that component. If my DTR is running at https://dtr.example.com/ 5, then my auth service is located at https://dtr.example.com/auth/token > 3.

Check out the “auth” section in this example config file: https://github.com/docker/notary/blob/master/docs/reference/server-config.md#overview 25

That should give you at least a starting point.

/Jeff

I definitely think we can federate 2 distincts JWT-token based auth services, using Keycloak, and maybe Keycloak Gatekeeper (A Keycloak Gatekeeper in front of both..?) .