Closed mettke closed 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.
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 :
docker login
, you're also logged in the notary
docker trust
commands to load you private key, add the associated public SSL/TLS Certifcate as identity of a new signer, and sign your images, before you docker push
them.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 :
Keycloak Gatekeeper
to drop the notary
token into the response to your docker login 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?
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.
note :
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.
hi @mettke Thank you so much for this quick feedback ! (all tests are run against portus 2.4.3
)
Excellent :
DOCKER_CONTENT_TRUST_SERVER
, which I was looking for. I have just modified my script above, to add this env. var. for docker notary client conf. notary
(it's like awesome), a few days ago, along my work on portus
. And that especially interested in the capability of notary
to act for different, independent registry services. Actually I am especially interested in generalizing notary for trust management on any binary, could be java jar
, go executables, or linux packages. Even non executable binaries, like pure internationalization packages. Any dependency, of a given infrastructure, to sum it up.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
logs below is notary
requesting an auth token, to be able to afterwards query the registry
? portus_1 | Parameters: {"account"=>"mettke", "scope"=>"repository:registry.itmettke.de/test/alpine:push,pull", "service"=>"registry.itmettke.de"}
portus
for an auth token to authenticate to registry ? After reading https://docs.docker.com/notary/service_architecture/#example-client-server-signer-interaction, I don't think so, I rather think (do you agree ?) :
notary
server, and then successfully load your private key, and where you had the error, is when you tried and register your new signer, to your private notary
with your signer's public certificate :
# --
# I think [docker trust keyload] does not send a request to notary (will test that, what
# happened for you?), so does not trigger docker login
docker trust key load $OCI_SIGNER_PRIVATE_KEY --name $OCI_IMAGE_SIGNER_NOTARY_NAME
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)
Also, I wanna ask :
portus
, and use another solution instead, like harbor
? (Did it take you to switch to harbor, to get proper trust management ?)all tests are run against portus 2.4.3
I create the first super admin user with username jbladmin
, using the portus webui
DOCKER_CONTENT_TRUST_SERVER
env varPortus
Logs I get, when I docker login
, using a wrong password and the first super admin username jbladmin
(test login failure) : 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
Portus
Logs I get, when I docker login
, using the right password and the first super admin username jbladmin
(test login success) : 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
DOCKER_CONTENT_TRUST_SERVER
env varfirst, I docker logout of course, to wipe out base64-encoded credentials from ~/.docker/config.json
Portus
Logs I get, when I docker login
, using a wrong password and the first super admin username jbladmin
(test login failure) : 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
Portus
Logs I get, when I docker login
, using the right password and the first super admin username jbladmin
(test login success) : 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
@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.
:)
registry
(Docker Distribution, alright) , and the notary
auth. servicesI want to point out one thing here :
registry
and notary
use the same auth token issuer/authentication/authorization service.notary
: so let's use another ? notary
? Well, when you dockerlogin, as confirmed in official documentation here, providing the example below : # -- #
$ 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
Jeff Anderson
, from the docker official team, April 2016
, says indeed those two auth services can be the same, like on DTR. They can be the same, so they can also not (and be different) : 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..?) .
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
What happens is that the following token is requested:
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:
This leads to the hostname being used as namespace name failing the authorization process.
This is my first try to solve this problem:
It works, however it migth not be the best way to solve this
The hostname might contain a port reference like:
This leads to another problem in this part:
Instead the code should be able to handle four parts as well
https://github.com/theupdateframework/notary/issues/1023