SUSE / Portus

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

setting up the first user in docker-compose file #2244

Closed shqear93 closed 4 years ago

shqear93 commented 4 years ago

I just implemented my private registry in my automation server stack, I'm trying to create first user automatically without using UI because I'm already building CI/CD system and it doesn't make sense to set it using UI ! Is there any way to create first user and first registry within the initialisation using docker-compose file?

I'm using the latest portus version 2.4.3

salzig commented 4 years ago

the following should work.

docker-compose exec portus bundle exec rake portus:create_user[username email password admin]
Jean-Baptiste-Lasselle commented 4 years ago

the following should work.

docker-compose exec portus bundle exec rake portus:create_user[username email password admin]

hi @salzig , I am currently working on that issue, and I have a short question, beforeI give full feddback here :

Did you mean executing that inside the portus , or the portus background container ?

I have executed that inside the portus contianers, and apparently bunble not present in the container, so I guess you build your own portus:development image, while I used opensuse/portus:2.5 container distributions. Am I right ?

(also did the test based on `opensuse/portus:2.4.3)

Jean-Baptiste-Lasselle commented 4 years ago

Hi @shqear93 I have a solution here that I just sucessfully tested today, with portus version 2.4.3, and here you go :

# The name of the portus container, but you could also use 'docker-compose exec'
export PORTUS_CONTAINER_NAME=${PORTUS_CONTAINER_NAME:-'portuscontainer'}
export FIRST_SUPER_ADMIN_USERNAME=superjbl
export FIRST_SUPER_ADMIN_PASSWORD=pokusportus
export FIRST_SUPER_ADMIN_EMAIL=jbl@pegasusio.io
export FIRST_SUPER_ADMIN_ROLE=admin

echo "Current working directory is [$(pwd)]. Creating Portus' first super admin [$FIRST_SUPER_ADMIN_USERNAME]... "
# docker-compose config && docker-compose exec portus bundle exec rake portus:create_user[$FIRST_SUPER_ADMIN_USERNAME $FIRST_SUPER_ADMIN_EMAIL $FIRST_SUPER_ADMIN_PASSWORD $FIRST_SUPER_ADMIN_ROLE]
# docker-compose config && docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && bundle exec rake portus:create_user[$FIRST_SUPER_ADMIN_USERNAME $FIRST_SUPER_ADMIN_EMAIL $FIRST_SUPER_ADMIN_PASSWORD $FIRST_SUPER_ADMIN_ROLE]"

docker-compose config || exit 1

docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && gem i bundler -v 1.17.3"
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && ./bin/bundle install"
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && ./bin/bundle update --bundler"

# installation de rake
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && gem i annotate"
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && gem i rake -v 12.3.2"

# Et creation du FIRST SUPER USER
# Now creating first super admin user, like we would create any other user, only 
# we do want 'admin' role for this particular user.
# 
# Extremely important down there : 
# NO SPACES BETWEEN RAKE ARGS, AND COMMA SEPARATED ARGS
# 
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && export RAILS_ENV=production && ./bin/bundle exec rake portus:create_user[$FIRST_SUPER_ADMIN_USERNAME,$FIRST_SUPER_ADMIN_EMAIL,$FIRST_SUPER_ADMIN_PASSWORD,$FIRST_SUPER_ADMIN_ROLE]"

# Finally, note that the portus role for the newly created user, above 
# named 'FIRST_SUPER_ADMIN_ROLE' ,can have either of the following values : 
# 
# - "admin" (that's the value used above)
# - "owner"
# - "contributor"
# - "viewer"
# 
# An example of how to create a regular user with lowest permissions (viewer role) : 
# username is "omersimpson", "omer@pegasusio.io" his email address, and "viewer"is its portus role.
# docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && export RAILS_ENV=production && ./bin/bundle exec rake portus:create_user[omersimpson,omer@pegasusio.io,$FIRST_SUPER_ADMIN_PASSWORD,viewer]"
jbl@poste-devops-typique:/tmp/portus.autopilot.provision.HhOb2D/portus/official/compose$ docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && export RAILS_ENV=production && ./bin/bundle exec rake portus:create_user[$FIRST_SUPER_ADMIN_USERNAME,$FIRST_SUPER_ADMIN_EMAIL,$FIRST_SUPER_ADMIN_PASSWORD,FIRST_SUPER_ADMIN_ROLE]"
[Mailer config] Host:     portus.test.lan
[Mailer config] Protocol: https://
  User Exists (0.2ms)  SELECT  1 AS one FROM `users` WHERE `users`.`username` = 'portus' LIMIT 1
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`username` = 'portus' LIMIT 1
   (0.3ms)  BEGIN
  SQL (0.4ms)  UPDATE `users` SET `encrypted_password` = '$2a$10$HROpzRDH.ujgCVNRC2.4J.oN1TAjlHFURF.tbxwWhJ1ahWpYp0K0y', `updated_at` = '2020-02-12 01:24:34' WHERE `users`.`id` = 1
   (0.4ms)  COMMIT
  User Exists (0.4ms)  SELECT  1 AS one FROM `users` WHERE `users`.`username` = 'portus' LIMIT 1
   (0.2ms)  SELECT COUNT(*) FROM `registries`
   (0.3ms)  SELECT COUNT(*) FROM `registries`
   (0.3ms)  BEGIN
  User Exists (25.5ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` = BINARY 'jbl@pegasusio.io' LIMIT 1
  User Exists (0.5ms)  SELECT  1 AS one FROM `users` WHERE `users`.`username` = BINARY 'superjbl' LIMIT 1
  Namespace Exists (0.4ms)  SELECT  1 AS one FROM `namespaces` WHERE `namespaces`.`name` = 'superjbl' LIMIT 1
  SQL (30.0ms)  INSERT INTO `users` (`username`, `encrypted_password`, `email`, `created_at`, `updated_at`) VALUES ('superjbl', '$2a$10$jxHUDJv2t2N5zyMzue2GzeitACxOm/1mpNEr/wbRCk2NfXKsXgJ3C', 'jbl@pegasusio.io', '2020-02-12 01:24:34', '2020-02-12 01:24:34')
   (0.4ms)  SELECT COUNT(*) FROM `registries`
   (0.2ms)  SELECT COUNT(*) FROM `registries`
  Namespace Exists (0.2ms)  SELECT  1 AS one FROM `namespaces` WHERE `namespaces`.`name` = 'superjbl' LIMIT 1
  Team Exists (0.6ms)  SELECT  1 AS one FROM `teams` WHERE `teams`.`name` = 'superjbl' LIMIT 1
  Team Exists (0.5ms)  SELECT  1 AS one FROM `teams` WHERE `teams`.`name` = BINARY 'superjbl' LIMIT 1
  TeamUser Exists (0.5ms)  SELECT  1 AS one FROM `team_users` WHERE (`team_users`.`user_id` = BINARY 3 AND `team_users`.`team_id` IS NULL) LIMIT 1
  User Exists (0.5ms)  SELECT  1 AS one FROM `users` WHERE (`users`.`email` = BINARY 'jbl@pegasusio.io' AND `users`.`id` != 3) LIMIT 1
  User Exists (0.5ms)  SELECT  1 AS one FROM `users` WHERE (`users`.`username` = BINARY 'superjbl' AND `users`.`id` != 3) LIMIT 1
  SQL (0.3ms)  INSERT INTO `teams` (`name`, `hidden`, `created_at`, `updated_at`) VALUES ('superjbl', 1, '2020-02-12 01:24:34', '2020-02-12 01:24:34')
  TeamUser Exists (0.4ms)  SELECT  1 AS one FROM `team_users` WHERE (`team_users`.`user_id` = BINARY 3 AND `team_users`.`team_id` = 4) LIMIT 1
  SQL (0.3ms)  INSERT INTO `team_users` (`role`, `user_id`, `team_id`, `created_at`, `updated_at`) VALUES (2, 3, 4, '2020-02-12 01:24:34', '2020-02-12 01:24:34')
  TeamUser Exists (0.4ms)  SELECT  1 AS one FROM `team_users` WHERE (`team_users`.`user_id` = BINARY 3 AND `team_users`.`id` != 5 AND `team_users`.`team_id` = 4) LIMIT 1
  Registry Load (0.3ms)  SELECT  `registries`.* FROM `registries`  ORDER BY `registries`.`id` ASC LIMIT 1
  Namespace Load (0.4ms)  SELECT  `namespaces`.* FROM `namespaces` WHERE `namespaces`.`name` = 'superjbl' AND `namespaces`.`visibility` = 0 AND `namespaces`.`description` = 'This personal namespace belongs to superjbl.' AND `namespaces`.`team_id` = 4 AND `namespaces`.`registry_id` = 1 LIMIT 1
  Namespace Exists (0.5ms)  SELECT  1 AS one FROM `namespaces` WHERE (`namespaces`.`name` = BINARY 'superjbl' AND `namespaces`.`registry_id` = 1) LIMIT 1
  SQL (0.3ms)  INSERT INTO `namespaces` (`team_id`, `name`, `visibility`, `description`, `registry_id`, `created_at`, `updated_at`) VALUES (4, 'superjbl', 0, 'This personal namespace belongs to superjbl.', 1, '2020-02-12 01:24:34', '2020-02-12 01:24:34')
  SQL (0.4ms)  UPDATE `users` SET `username` = 'superjbl', `encrypted_password` = '$2a$10$jxHUDJv2t2N5zyMzue2GzeitACxOm/1mpNEr/wbRCk2NfXKsXgJ3C', `email` = 'jbl@pegasusio.io', `created_at` = '2020-02-12 01:24:34', `updated_at` = '2020-02-12 01:24:34', `id` = 3, `namespace_id` = 4 WHERE `users`.`id` = 3
   (0.3ms)  COMMIT
jbl@poste-devops-typique:/tmp/portus.autopilot.provision.HhOb2D/portus/official/compose$ 

Honestly, I think distributed containers are either a little bit trashy, or they are automated flows used by the project team, not documented for the public audience yet. Update: It actually is totally normal that rake and bundle are not distributed with app in container, and we shouldnot use rake or any "inside container" operation, to silently create first admin user, see my final note

For the curious reader

Examining the commands I above execute in the docker container, you might askyouself a few questions. Here are a few answers.

To investigate "what's wrong with portus", I very early found that you can set portus log level using one environment variable for the portus run, PORTUS_LOG_LEVEL=debug. So I set that env., and what did i find in the portus container logs ? see for yourself :

jbl@poste-devops-typique:/tmp/portus.autopilot.provision.S7Ngyh/portus/official/compose$ docker-compose logs -f |more
Attaching to compose_registry_1, compose_background_1, portuscontainer, compose_
db_1
registry_1    | + cp /secrets/portus.crt /usr/local/share/ca-certificates
registry_1    | + update-ca-certificates
registry_1    | WARNING: ca-certificates.crt does not contain exactly one
 certificate or CRL: skipping
registry_1    | + registry serve /etc/docker/registry/config.yml
registry_1    | time="2020-02-12T22:04:55Z" level=warning msg="No HTTP se
cret provided - generated random secret. This may cause problems with uploads if
 multiple registries are behind a load-balancer. To provide a shared secret, fil
l in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET envir
onment variable." go.version=go1.7.6 instance.id=25680c74-8fa8-45a3-b049-1b8b919
8e223 version=v2.6.2-14-ga66a4c3 
registry_1    | time="2020-02-12T22:04:55Z" level=info msg="configuring e
ndpoint portus (https://portus.pegasusio.io:3000/v2/webhooks/events), timeout=2s
, headers=map[]" go.version=go1.7.6 instance.id=25680c74-8fa8-45a3-b049-1b8b9198
e223 version=v2.6.2-14-ga66a4c3 
registry_1    | time="2020-02-12T22:04:55Z" level=info msg="redis not con
figured" go.version=go1.7.6 instance.id=25680c74-8fa8-45a3-b049-1b8b9198e223 ver
sion=v2.6.2-14-ga66a4c3 
registry_1    | time="2020-02-12T22:04:55Z" level=info msg="debug server 
listening 0.0.0.0:5001" 
registry_1    | time="2020-02-12T22:04:55Z" level=info msg="Starting uplo
ad purge in 27m0s" go.version=go1.7.6 instance.id=25680c74-8fa8-45a3-b049-1b8b91
98e223 version=v2.6.2-14-ga66a4c3 
registry_1    | time="2020-02-12T22:04:55Z" level=info msg="listening on 
[::]:5000, tls" go.version=go1.7.6 instance.id=25680c74-8fa8-45a3-b049-1b8b9198e
223 version=v2.6.2-14-ga66a4c3 
registry_1    | time="2020-02-13T16:17:37Z" level=info msg="PurgeUploads 
starting: olderThan=2020-02-06 16:17:37.814679695 +0000 UTC, actuallyDelete=true
" 
registry_1    | time="2020-02-13T16:17:37Z" level=info msg="Purge uploads
 finished.  Num deleted=0, num errors=1" 
registry_1    | time="2020-02-13T16:17:37Z" level=info msg="Starting uplo
ad purge in 24h0m0s" go.version=go1.7.6 instance.id=25680c74-8fa8-45a3-b049-1b8b
9198e223 version=v2.6.2-14-ga66a4c3 
portuscontainer | PORTUS_DB_PASSWORD=portus
portuscontainer | PORTUS_DB_HOST=db
portuscontainer | HOSTNAME=077b7544ea42
portuscontainer | RAILS_SERVE_STATIC_ASSETS='true'
portuscontainer | PORTUS_DB_POOL=5
portuscontainer | CCONFIG_PREFIX=PORTUS
portuscontainer | PORTUS_KEY_PATH=/certificates/portus.key
portuscontainer | PORTUS_LDAP_AUTHENTICATION_PASSWORD=
portuscontainer | PWD=/
portuscontainer | PORTUS_PUMA_HOST=0.0.0.0:3000
portuscontainer | HOME=/root
portuscontainer | PORTUS_MACHINE_FQDN_VALUE=portus.pegasusio.io
portuscontainer | RAILS_SERVE_STATIC_FILES='true'
portuscontainer | PORTUS_EMAIL_SMTP_PASSWORD=
portuscontainer | GEM_PATH=/srv/Portus/vendor/bundle/ruby/2.5.0
portuscontainer | RAILS_ENV=production
portuscontainer | PORTUS_PASSWORD=12341234
portuscontainer | PORTUS_SECRET_KEY_BASE=b494a25faa8d22e430e843e220e424e1
0ac84d2ce0e64231f5b636d21251eb6d267adb042ad5884cbff0f3891bcf911bdf8abb3ce719849c
cda9a4889249e5c2
portuscontainer | RACK_ENV=production
portuscontainer | PORTUS_SERVICE_FQDN_VALUE=portus.pegasusio.io
portuscontainer | PORTUS_LOG_LEVEL=debug
portuscontainer | PORTUS_PUMA_TLS_CERT=/certificates/portus.crt
portuscontainer | SHLVL=2
portuscontainer | PORTUS_PUMA_TLS_KEY=/certificates/portus.key
portuscontainer | PORTUS_DB_DATABASE=portus_production
portuscontainer | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:
/sbin:/bin
portuscontainer | _=/usr/bin/printenv
portuscontainer | Waiting for mariadb to be ready in 5 seconds
portuscontainer | [Mailer config] Host:     portus.pegasusio.io
portuscontainer | [Mailer config] Protocol: https://
portuscontainer | Configuring database...
portuscontainer | -- create_table("activities", {:force=>:cascade})
portuscontainer |    (76.5ms)  CREATE TABLE `activities` (`i
--More--

See the portuscontainer | RAILS_ENV=production ?

Jean-Baptiste-Lasselle commented 4 years ago

@shqear93 Waiting for your feedback to close the issue with @mssola validation I think

Update

So with the commands I gave, the output shows that I actually did not create a new user, there's an error about the use of the arguments.

We're not on a solution yet, but we're getting very close. I bring any news here as soon as I get something new.

RE-Update

Ok found the issue, see comma separated args (and no spaces) in the update code snippet above

(yeahhhh it works)

and you don't even have to restart portus!!

Jean-Baptiste-Lasselle commented 4 years ago

Important Addendum for automation

I above explained how to silently (non-interactively) create users with admin role in portus. Never the less, what @shqear93 wasasking for,is how to create the first super admin user, non interactively.

Two important things :


export PORTUS_CONTAINER_NAME=${PORTUS_CONTAINER_NAME:-'portuscontainer'}
export REGISTRY_PORTUS_NAME=gracefulregistry
export REGISTRY_PORTUS_HOST=oci-registry.pegasusio.io:5000
export REGISTRY_PORTUS_USE_SSL=true
export REGISTRY_PORTUS_EXTERNAL_NAME=oci-registry.pegasusio.io

echo "Current working directory is [$(pwd)]. Configuring Portus' OCI image registry [$REGISTRY_PORTUS_HOST]... "
# docker-compose config && docker-compose exec portus bundle exec rake portus:create_user[$FIRST_SUPER_ADMIN_USERNAME $FIRST_SUPER_ADMIN_EMAIL $FIRST_SUPER_ADMIN_PASSWORD $FIRST_SUPER_ADMIN_ROLE]
# docker-compose config && docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && bundle exec rake portus:create_user[$FIRST_SUPER_ADMIN_USERNAME $FIRST_SUPER_ADMIN_EMAIL $FIRST_SUPER_ADMIN_PASSWORD $FIRST_SUPER_ADMIN_ROLE]"

docker-compose config || exit 1

docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && gem i bundler -v 1.17.3"
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && ./bin/bundle install"
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && ./bin/bundle update --bundler"

# installation de rake
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && gem i annotate"
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && gem i rake -v 12.3.2"

# Et configuration du registry docker dans portus

#
# Extremely important down there :
# NO SPACES BETWEEN RAKE ARGS, AND COMMA SEPARATED ARGS
#
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && export RAILS_ENV=production && ./bin/bundle exec rake portus:create_registry['$REGISTRY_PORTUS_NAME','$REGISTRY_PORTUS_HOST','$REGISTRY_PORTUS_USE_SSL','$REGISTRY_PORTUS_EXTERNAL_NAME']"
# Enfin, on définit le nouveau user, comme super-admin.
#
# http://port.us.org/docs/Configuring-Portus.html#creating-the-first-admin-user
# 
docker exec $PORTUS_CONTAINER_NAME bash -c "cd /srv/Portus && export RAILS_ENV=production && ./bin/bundle exec rake portus:make_admin[$FIRST_SUPER_ADMIN_USERNAME]"
Jean-Baptiste-Lasselle commented 4 years ago

@shqear93 Now I think we can close this issue, can't we?

@mssola I'll read very carefully any remarks from you, do you think there are other aspects I missed here ?

shqear93 commented 4 years ago

@Jean-Baptiste-Lasselle Sorry for late, thank you very much for your help, I'll close the issue.

Jean-Baptiste-Lasselle commented 4 years ago

Last Most Important update

@shqear93 Hi again, I think we are going to keep this issue closed, but I have something that slowly builded up my mind, and I have to share with you, as it has great importance about production management :

Ok, so my recommandation, is to NOT use the rake-based solution, but instead use the Portus API, with the following flow :

jq --version || sudo apt-get install -y jq || sudo yum install -y jq || sudo zypper install -y jq

export PORTUS_HOST=portus.pegasusio.io
# -- bootstrap (no one has opened the web ui and auto-registered as first super admin)
# first, we need the secret (the token) to authenticate to Portus API
# - The [/bootstrap] Endpoints, POST, to crate users, and PUT to modify permissions
# http://port.us.org/docs/API.html#spec-method-post-api-v1-users-bootstrap

# => With this first Portus API call, you retrieve in the response the token you are going to use to
#     authenticate to the Portus REST API in all the next Portus API calls below
export FIRST_SUPER_ADMIN_USERNAME=superjbl
export FIRST_SUPER_ADMIN_PASSWORD=pokusportus
export FIRST_SUPER_ADMIN_EMAIL=jbl@pegasusio.io
# We don't need the role, cause bootstraping implicitly means
# giving admin role to bootstraped first user.
# export FIRST_SUPER_ADMIN_ROLE=admin

export PAYLOAD="{\"user\": {\"username\": \"$FIRST_SUPER_ADMIN_USERNAME\", \"email\": \"$FIRST_SUPER_ADMIN_EMAIL\", \"password\": \"$FIRST_SUPER_ADMIN_PASSWORD\", \"display_name\": \"$FIRST_SUPER_ADMIN_EMAIL\"}}"

curl --insecure -H 'Content-type: application/json' -H 'Accept: application/json' -X POST --data "$PAYLOAD" https://$PORTUS_HOST/api/v1/users/bootstrap > ./portus.bootstrap.json

export  PORTUS_API_BOOT_TOKEN=(cat ./portus.bootstrap.json|jq '.plain_token'|awk -F '"' '{print $2}')
# And this one guy , $PORTUS_API_BOOT_TOKEN , it's your most critical portus secret, so you immediately secrue it into a secret manager like ansible secret manager, kubernetes / openshift secret manager, personally I prefer hashicorp vault.
# Then you'll use a credential helper, configured with your secret manager, to resolve your Portus API auth secret, for all subsequent Portus API calls.
# Or best: you revoke it once bootstrap process completed
#

# Now we can authenticate to portus api using the token :
# --- #
curl --insecure -X GET --header 'Accept: application/json' --header "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" https://${PORTUS_HOST}/api/v1/_ping | jq '.'
curl --insecure -X GET --header 'Accept: application/json' --header "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" https://${PORTUS_HOST}/api/v1/health | jq '.'
curl --insecure -X GET --header 'Accept: application/json' --header "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" https://${PORTUS_HOST}/api/v1/users | jq '.[]'

# --->> REGISTRY CONFIG
# ok, so now let's configure the OCI image regsitry in portus :
#
# 1./ Configuring the registry, so portus knows how to reach it, onthe network.
# [POST /api/v1/registries]
export PORTUS_REGISTRY_NAME=companyregistry
export PORTUS_REGISTRY_HOSTNAME=oci-registry.pegasusio.io:5000
export PORTUS_REGISTRY_USE_SSL=true
export PORTUS_REGISTRY_EXTERNAL_HOSTNAME=oci-registry.pegasusio.io

export PAYLOAD="{\"registry\": {\"name\": \"$PORTUS_REGISTRY_NAME\", \"hostname\": \"$PORTUS_REGISTRY_HOSTNAME\", \"use_ssl\": \"$PORTUS_REGISTRY_USE_SSL\", \"external_hostname\": \"$PORTUS_REGISTRY_EXTERNAL_HOSTNAME\"}}"

curl --insecure --header 'Content-type: application/json' --header 'Accept: application/json' --header "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" -X POST --data "$PAYLOAD" https://${PORTUS_HOST}/api/v1/registries | jq '.'

# 2./ After configuring the registry, we test itis reachable as is.
# [GET /api/v1/registries/validate]
curl --insecure -X GET --header 'Accept: application/json' --header "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" https://${PORTUS_HOST}/api/v1/registries/validate | jq '.'

# --- #
#
# Using the just created Bootstraped user, we could also now :
#
# => [docker login $OCI_REGISTRY_SERVICE_FQDN], using
# => but even after creating the registry, we could, but won't use the [PORTUS_API_BOOT_TOKEN] to [docker login $OCI_REGISTRY_SERVICE_FQDN] : it is just meant to bootstrap, and :
#    => We use the token to create other users, with less privilege than the first super admin user : for example a Portus User with 'Contributor' Role,and probably set it as a ROBOT user.
#    => We will then create token for that robot user, and docker login using that new token;
#    => We should then even revoke/delete it.

# Here below I demonstrate how to create that robot user

export TEAM_BOT_USER_USERNAME=bumblebee
export TEAM_BOT_USER_EMAIL=bumblebee@pegasusio.io
export TEAM_BOT_USER_PASSWORD=beebeeio
export TEAM_BOT_USER_IS_BOT=true
export PAYLOAD="{\"user\": {\"username\": \"$TEAM_BOT_USER_USERNAME\", \"email\": \"$TEAM_BOT_USER_EMAIL\", \"password\": \"$TEAM_BOT_USER_PASSWORD\", \"display_name\": \"$TEAM_BOT_USER_USERNAME\", \"bot\": \"$TEAM_BOT_USER_IS_BOT\"}}"

curl --insecure -H 'Content-type: application/json' -H 'Accept: application/json' -H "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" -d "$PAYLOAD" -X POST https://${PORTUS_HOST}/api/v1/users > ./team.bot.user.portus.json

cat ./team.bot.user.portus.json | jq '.[]'

export TEAM_BOT_USER_ID=$(cat ./team.bot.user.portus.json | jq .id)

#  So for silently (non-interactively) [docker login $OCI_REGISTRY_SERVICE_FQDN], After creating registry, we
#  need to create an app token for the [$TEAM_BOT_USER_USERNAME] portus user.
#  We'll do that using http://port.us.org/docs/API.html#spec-method-post-api-v1-users-{id}-application_tokens
# [POST /api/v1/users/{id}/application_tokens]

# As it is a bootstraped user, you may expect PORTUS_FIRST_SUPER_ADMIN_USER_ID=1

export PORTUS_USER_ID=$TEAM_BOT_USER_ID
export export PORTUS_APP_TOKEN_NAME=pokusbee
export PAYLOAD="{\"application\": \"$PORTUS_APP_TOKEN_NAME\", \"id\": \"$PORTUS_USER_ID\"}"

curl --insecure -H 'Content-type: application/json' -H 'Accept: application/json' -H "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" -X POST --data "$PAYLOAD" https://$PORTUS_HOST/api/v1/users/${PORTUS_USER_ID}/application_tokens > ./bee.oci.registry.auth.json

export BOT_OCI_REGISTRY_AUTH_TOKEN=$(cat bee.oci.registry.auth.json | jq '.plain_token' | awk -F '"' '{print $2}')

# And this one guy , $BOT_OCI_REGISTRY_AUTH_TOKEN , it's your most critical protus secret, so you immediately secrue it into a secret manager like ansible secret manager, kubernetes / openshift secret manager, personally I prefer hashicorp vault.
# Then you'll use a credential helper, configured with your secret manager, to resolve your Portus API auth secret, for all subsequent Portus API calls.
#
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ export PAYLOAD="{\"user\": {\"username\": \"obelix\", \"email\" : \"obelix@armorique.io\", \"password\" : \"idefixidefix\", \"display_name\" : \"obelixlegros\"}}"
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ echo "$PAYLOAD"
{"user": {"username": "obelix", "email" : "obelix@armorique.io", "password" : "idefixidefix", "display_name" : "obelixlegros"}}
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ curl --insecure -H 'Content-type: application/json' -H 'Accept: application/json' -X POST --data "$PAYLOAD" https://$PORTUS_HOST/api/v1/users/bootstrap
{"id":1,"application":"bootstrap","plain_token":"Lxy_z9MT8d1uBES3uwXK"}jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ export  PORTUS_API_BOOT_TOKEN=(cat ./portus.bootstrap.json|jq '.plain_token'|awk -F '"' '{print $2}')
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ export FIRST_SUPER_ADMIN_USERNAME=obelix
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ curl --insecure -X GET --header 'Accept: application/json' --header "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" https://${PORTUS_HOST}/api/v1/users | jq '.[]'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   579  100   579    0     0   4438      0 --:--:-- --:--:-- --:--:--  4453
{
  "id": 1,
  "username": "portus",
  "email": "portus@portus.com",
  "current_sign_in_at": null,
  "last_sign_in_at": null,
  "created_at": "2020-02-12T22:05:12.000Z",
  "updated_at": "2020-02-12T22:05:21.000Z",
  "admin": true,
  "enabled": true,
  "locked_at": null,
  "namespace_id": null,
  "display_name": null,
  "bot": false
}
{
  "id": 2,
  "username": "obelix",
  "email": "obelix@armorique.io",
  "current_sign_in_at": null,
  "last_sign_in_at": null,
  "created_at": "2020-02-13T20:04:54.000Z",
  "updated_at": "2020-02-13T20:04:54.000Z",
  "admin": true,
  "enabled": true,
  "locked_at": null,
  "namespace_id": null,
  "display_name": "obelixlegros",
  "bot": false
}

export PORTUS_HOST=portus.pegasusio.io
# -- bootstrap (no one has opened the web ui and auto-registered as first super admin)
# - The [/bootstrap] Endpoints, POST, to crate users, and PUT to modify permissions
# http://port.us.org/docs/API.html#spec-method-post-api-v1-users-bootstrap

# => With this one,you retrieve in the response the token you are going to use to
#     authenticate to the Portus REST API in all the next Portus API calls below
export FIRST_SUPER_ADMIN_USERNAME=superjbl
export FIRST_SUPER_ADMIN_PASSWORD=pokusportus
export FIRST_SUPER_ADMIN_EMAIL=jbl@pegasusio.io
# We don't need the role, cause bootstraping implicitly means
# giving admin role to bootstraped first user.
# export FIRST_SUPER_ADMIN_ROLE=admin

export PAYLOAD="{\"user\": {\"username\": \"$FIRST_SUPER_ADMIN_USERNAME\", \"email\": \"$FIRST_SUPER_ADMIN_EMAIL\", \"password\": \"$FIRST_SUPER_ADMIN_PASSWORD\", \"display_name\": \"$FIRST_SUPER_ADMIN_EMAIL\"}}"

curl --insecure -H 'Content-type: application/json' -H 'Accept: application/json' -X POST --data $PAYLOAD https://$PORTUS_HOST/api/v1/users/bootstrap > ./portus.bootstrap.json

export  PORTUS_API_BOOT_TOKEN=(cat ./portus.bootstrap.json|jq '.plain_token'|awk -F '"' '{print $2}')

# --- #
curl --insecure -X GET --header 'Accept: application/json' --header "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" https://${PORTUS_HOST}/api/v1/_ping | jq '.'
curl --insecure -X GET --header 'Accept: application/json' --header "Portus-Auth: $FIRST_SUPER_ADMIN_USERNAME:$PORTUS_API_BOOT_TOKEN" https://${PORTUS_HOST}/api/v1/users | jq '.[]'
# -- Registry configuration in the portus instance
# - The [/users] Endpoints, POST, to crate users, and PUT to modify permissions
# http://port.us.org/docs/API.html#spec-method-post-api-v1-registries

# -- And even the teams, namespaces, repositories, tags,everything
# Teams : http://port.us.org/docs/API.html#spec-teams

# - The [/users] Endpoints, POST, to crate users, and PUT to modify permissions
# http://port.us.org/docs/API.html#spec-method-put-api-v1-registries-{id}
shqear93 commented 4 years ago

@Jean-Baptiste-Lasselle Thank you for you support, it was really helpful. about your last updates, I suggest to make it a part of the entrypoint! example: provide options like USER_CREATE as environment variable to create a list of users with their passwords simply by passing this option to the docker container, inspired by DroneCI option DRONE_USER_CREATE: https://docs.drone.io/server/reference/drone-user-create/

what do you think?

Jean-Baptiste-Lasselle commented 4 years ago

@Jean-Baptiste-Lasselle Thank you for you support, it was really helpful. about your last updates, I suggest to make it a part of the entrypoint! example: provide options like USER_CREATE as environment variable to create a list of users with their passwords simply by passing this option to the docker container, inspired by DroneCI option DRONE_USER_CREATE: https://docs.drone.io/server/reference/drone-user-create/

what do you think?

I updated several times yesterday, and now "it's stable" : at the time I write this sentence, I have tested everything, and the whole Portus REST API-based automation works with portus 2.4.3 (latest stable release of portus).

I understand, with the drone.io documentation line you quote, that you state :

What if protus was working like drone.io ? (bootstrap first user using environment variables)

So, to answer your question, I today recommend a fully-Portus REST API-based automation for portus provisioning. But to give you an example relevant to your eyes, if i had to use drone.io for example, in building a CI/CD factory, my approach would be the following :

Jean-Baptiste-Lasselle commented 4 years ago

@shqear93

drone.io and secret managers :