September 9, 2019

Technical Notes: How to change Juju user password

Juju User

JuJu has an internal user framework, which supports sharing of controllers and models. Using JuJu user feature, a sysadmin could separated users for controllers as in multiple clouds deployment or for users as in multiple systems deployment. Each juju user can have its own permissions at application, model, or controller layer. This separation of responsibilities allow multiple users to manage multiple infrastructures with a clear border of responsibilities.

At controller bootstrap phase, Juju automatically creates a new user named admin with superuser privileges. This user could perform all operations at cluster level. We could confirm the current user and its privileges by 

$ juju whoami
Controller:  kvm
Model:       k8s
User:        admin 

$ juju users
Controller: kvm

Name    Display name  Access     Date created  Last connection
admin*  admin         superuser  23 hours ago  just now  

Authentication

An user needs to submit its username and password in order to login into controller. At bootstrap phase, the admin password is automatically generated by Juju. Admin user is also login by default with generated password. All commands with controllers after login is managed by "cookies", which are stored at JuJu client "~/.local/share/juju/cookies/{cloud_name}.yaml". This file stores all cookies needed for current users to authenticated against Juju controllers. In my case, the file is

$ ll ~/.local/share/juju/cookies/
total 16
drwx------ 2 telescreen telescreen 4096  9月  9 12:55 ./
drwx------ 5 telescreen telescreen 4096  9月  9 12:55 ../
-rw------- 1 telescreen telescreen 2998  9月  9 12:55 kvm.json


If we by accident delete this file, current user will be logout and we need to submit a password to re-authenticated against Juju controller.

$ rm -rf ~/.local/share/juju/cookies/kvm.json
$ ll ~/.local/share/juju/cookies
total 12
drwx------ 2 telescreen telescreen 4096  9月  9 13:05 ./
drwx------ 5 telescreen telescreen 4096  9月  9 13:04 ../
$
$ juju models
please enter password for admin on kvm:
Controller: kvm

Model       Cloud/Region  Type  Status     Machines  Cores  Units  Access  Last connection
controller  kvm           maas  available         1      1  -      admin   just now
default     kvm           maas  available         0      -  -      admin   19 hours ago
k8s*        kvm           maas  available         5      5  16     admin   12 minutes ago



Because the password is generated by default at controller bootstrap phase and we don't know the password, it would be a good idea to change the password after bootstrap. We could change the current user's password by

$ juju change-user-password
new password:
type new password again:
Your password has been changed.


Recover password


In case we forget the administrator password and we happened to logout of Juju, we need to recover the password. The process of recover password is complex but doable.

Access the Database

Firstly, we need to login into the Juju controller machines with Juju mongodb running as PRIMARY instance. The credential for login is stored at ~/.local/share/juju/ssh/. We therefore could login into juju controller machine by
$ juju machines -m controller
Machine  State    DNS           Inst id  Series  AZ       Message
0        started  10.23.23.125  g6es7h   bionic  default  Deployed

$
$ ssh -i ~/.local/share/juju/ssh/juju_id_rsa ubuntu@10.23.23.125
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-60-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Mon Sep  9 04:15:30 UTC 2019

  System load:  0.26              Processes:           83
  Usage of /:   66.1% of 7.27GB   Users logged in:     0
  Memory usage: 45%               IP address for eth0: 10.23.23.125
  Swap usage:   0%

 * Congrats to the Kubernetes community on 1.16 beta 1! Now available
   in MicroK8s for evaluation and testing, with upgrades to RC and GA

     snap info microk8s

 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

0 packages can be updated.
0 updates are security updates.


Last login: Sun Sep  8 08:52:20 2019 from 10.23.23.1
ubuntu@many-shrew:~$



Next, we need to login into mongodb used by Juju. The credential of mongodb admin user is stored at /var/lib/juju/agents/machine-0/agent.conf

$ mongo --sslAllowInvalidCertificates --ssl -u admin -p $(sudo sed -n '/^oldpassword:/s/^oldpassword: //p' /var/lib/juju/agents/machine-0/agent.conf) 127.0.0.1:37017/admin
MongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:37017/admin
2019-09-09T04:19:58.587+0000 W NETWORK  [thread1] SSL peer certificate validation failed: unable to get local issuer certificate
2019-09-09T04:19:58.587+0000 W NETWORK  [thread1] The server certificate does not match the host name. Hostname: 127.0.0.1 does not match CN: *
MongoDB server version: 3.6.3
Server has startup warnings:
2019-09-08T04:07:00.871+0000 I STORAGE  [initandlisten]
2019-09-08T04:07:00.871+0000 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2019-09-08T04:07:00.871+0000 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2019-09-08T04:07:02.872+0000 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2019-09-08T04:07:02.872+0000 I CONTROL  [initandlisten]
juju:PRIMARY>
juju:PRIMARY> use juju
switched to db juju
juju:PRIMARY> db.users.find().pretty()
{
        "_id" : "admin",
        "name" : "admin",
        "displayname" : "admin",
        "passwordhash" : "il/ap8gXWEUp8x1T48bIwOxm",
        "passwordsalt" : "3YTbonyqTpmWCvtQ",
        "createdby" : "admin",
        "datecreated" : ISODate("2019-09-08T04:07:11Z"),
        "txn-revno" : NumberLong(7),
        "txn-queue" : [
                "5d75d082a20e030edc1539c8_953a034a"
        ]
}



Password Hash

According to [1], Juju users' password is hashed using PBKDF2 [2] with following parameters
  • Number of hash Iterations: 8192
  • Hash algorithm: SHA-512
  • Salt length: 12 bytes [3]
  • Password length in bytes: 18 bytes
We could easily generated the hashed value for any pasword using the same parameters above. For example, in Python we could generate a password by
>> import hashlib, binascii
>> dk = hashlib.pbkdf2_hmac('sha512', b'test', b'4AIANdqIiPvpZUS2', 8192, 18)
>> binascii.b2a_base64(dk)
b'iJW8NpnYPG4iwfzQoMkLfY2z\n'


Update password

Finally, we could recover the password by update the "passwordhash" and "passwordsalt" fields using mongodb update command. Because mongodb will overwrite the whole document, we need to include all other fields with old value in our backup.

db.users.update({_id: "admin"},
{
        "_id" : "admin",
        "name" : "admin",
        "displayname" : "admin",
        "passwordhash" : "iJW8NpnYPG4iwfzQoMkLfY2z",
        "passwordsalt" : "4AIANdqIiPvpZUS2",
        "createdby" : "admin",
        "datecreated" : ISODate("2019-09-08T04:07:11Z"),
        "txn-revno" : NumberLong(8),
        "txn-queue" : [
                "5d75d5f3a20e030edc1544bd_3d2483d7"
        ]
})

And we could login into the controller with the new password!

Reference

1. https://github.com/juju/utils/blob/c746c6e86f4fb2a04bc08d66b7a0f7e900d9cbab/password.go#L65
2. https://en.wikipedia.org/wiki/PBKDF2
3. https://github.com/juju/utils/blob/c746c6e86f4fb2a04bc08d66b7a0f7e900d9cbab/password.go#L49