Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

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

Closed
shqear93 opened this issue Oct 23, 2019 · 11 comments
Closed

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

shqear93 opened this issue Oct 23, 2019 · 11 comments

Comments

@shqear93
Copy link

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

  • Sorry if this is not the right place for such question
@salzig
Copy link
Contributor

salzig commented Oct 31, 2019

the following should work.

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

@Jean-Baptiste-Lasselle
Copy link

Jean-Baptiste-Lasselle commented Dec 20, 2019

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
Copy link

Jean-Baptiste-Lasselle commented Feb 11, 2020

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]"

  • example output :
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$ 
  • I did successfully test that after that I could docker login using superjbl as username,and pokusportus , as password.
    Will test 2.5 soon, if u have results before me on 2.5, and share, i'll read them with interest.

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.

  • Question 1 : How the heck was I supposed to guess about the RAILS_ENV=production variable ..?
    Answer

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
Copy link

Jean-Baptiste-Lasselle commented Feb 11, 2020

@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
Copy link

Jean-Baptiste-Lasselle commented Feb 12, 2020

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 :

  • to create users, you'll have (or you'll get an error) to first (silently) configure the OCI registry in portus, and that can be done using this :
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']"
  • Second thing is that once created, with admin role, the portus user still isn't a super-admin : it doesn't have permissions to manage users and users' permissions. To do that, you have to execute the following, additionnaly to the above given script for creating a user :
# 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
Copy link

@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
Copy link
Author

shqear93 commented Feb 12, 2020

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

@Jean-Baptiste-Lasselle
Copy link

Jean-Baptiste-Lasselle commented Feb 13, 2020

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 :

  • all in all, I investigated this issue, "as is".
  • Along my investigation,I understood that what @salzig suggested, involved using development tools.
  • And the first thing I noticed, is that someof those tools are not present, and I have to install themlive inside the portus container, in order to be able to perform live ops.
  • But something kept bugging me here : we are in production, we should not need to install any tool, to be able to operate on production.
  • So alright, n I today found what is, I have no doubt, the definitive way to do what you were trying to do :
    • using Portus API, we can create the first super admin user, and do everything we did with rake, connecting live into a container (which is not scalable). At least, all the endpoints to CRUD these, are present in the Portus API.
    • and of course, this is the way it should be done : In today's Cloud world, we operate systems through a REST API they expose. Portus has a REST API, we should be able to use it to automate non-interactively all standards ops on Portus.

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.
#

  • here an example output of the test I ran succesfully bootstraping the first super admin user, using ONLY Portus API :
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
}
  • now that we have a Portus API token, we can then authenticate to the Portus API v1, using the following syntax:
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 '.[]'
  • And we can use the token to proceed:
    • configure the oci image registry (#nobgifatdaemon) for portus,
    • create namespaces, teams, and others users with less permissions on namespaces,
  • And the involved endpoints would be :
# -- 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
Copy link
Author

@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
Copy link

Jean-Baptiste-Lasselle commented Feb 14, 2020

@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 :

  • (you) look up abovein this page, (Ctrl + F) the string And this one guy , $PORTUS_API_BOOT_TOKEN , it's your most critical portus secret
  • You are now in a script I wrote and tested, which shows you how to get a first Portus API token (and create first super admin user, with any value you choose for username password, email, is admin, etc...)
  • In this script with the And this one guy , $PORTUS_API_BOOT_TOKEN , it's your most critical portus secretcomment, I explain that immediately after you get that token, you should secure it in a Secret manager,like HashiCorp Vaultor ansible secret manager
  • So, is there a Drone.io secret manager ?
  • I also bet it is possible to configure drone.io to use an hashicorp vault instance as global or project-level secret manager : so then al you have to know, to use your secret in your pipelines, is how to use drone.io secrets (injection).
  • and every where outside drone.io, you will need to access that secret, you should use what Hashicorp Vault calls credential helpers, see for example the HashiCorp Vault Docker Credential helper . In other words, I will soon release a HashiCorp Vault Portus Credential helper, which will demonstrate that.

@Jean-Baptiste-Lasselle
Copy link

@shqear93

drone.io and secret managers :

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants