Technical Note: 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 [email protected]
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!