This is a text-only version of the following page on https://raymii.org:
---
Title       :   OpenStack nova get-password, set-password and post encrypted password to metadata service
Author      :   Remy van Elst
Date        :   25-03-2018
URL         :   https://raymii.org/s/tutorials/OpenStack_nova_get_-_password_set_-_password_and_post_encrypted_password_to_metadata_service.html
Format      :   Markdown/HTML
---



When you create images for an OpenStack Cloud you want to use 'cloud' features.
Fancy term for automatic resizing of your instance disk, adding an SSH key,
(re)setting passwords and executing scripts on first boot to configure your
instance further. OpenStack provides the metadata service for instances, which
supplies information for the instance, like its public IP, SSH public key that
was provided and vendor or user provided data like scripts or information. The
OpenStack metadata service allows an instance to post data to an endpoint wich
can be retreived with the `nova get-password` command. It is meant to be an
encrypted password (with the public SSH key) but it can be any plain text as
well and it doesn't have to be the root password. In this guide I'll go over the
scripts I use inside linux images to post a password to the metadata service and
the `nova` commands such as `set-password` and `get-password`. That includes
decrypting a password with an SSH key that is password-protected (Horizon and
nova don't support that) and the `nova set-password` command, which sets the
root password inside an instance when it has the `qemu-guest-agent` installed
and running.

<p class="ad"> <b>Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called  Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p>


The first section of the guide goes over the `nova get-password`, `nova set-
password` and the manual decryption of the password.

The second part of the article shows the script I use to set the password inside
cloud instances.

### nova get-password

The `nova` command line client supports the `get-password` command. This command
only works when a password has been posted to the metadata service (by the
instance).



   $ nova help get-password
   usage: nova get-password <server> [<private-key>]

   Get the admin password for a server.

   Positional arguments:
     <server>       Name or ID of server.
     <private-key>  Private key (used locally to decrypt password) (Optional).
                    When specified, the command displays the clear (decrypted) VM
                    password. When not specified, the ciphered VM password is
                    displayed.


Using it on a server that has a password set in the metadata service:



   $ nova get-password $instance_uuid


Output:



   bPLUcppp+MXbiO4kFMsIZ1zj4VbS/1kfafWTvqPBKr3B5Rk9z0eLotwVNDGCJkXYGmDmKca0q4hWdAt03N7QZ58DbgF24cvumgQIHddT5D14M98n85oiI3yPd0DCrEhvZQb5jwmYIWBBhqDCuyl+savwFNmVEn2cxdPleCZqvIN7BHs2HRVY7esHDcPiY1+Xdq5SMa92lcsJyn8LJRUUUuq0o0k75+4TTtEWEtClXiMyURIRDgDOpVDXBl2kgeU22a/e1rRKKOKg6CasVu7g0V9fmmLC/zXRmgTqfMwZlm0wDdq0X4jRUYzRNpxLarj6AXGYoSQT6moKx9ttcNL6JQ==


To decrypt it, supply the SSH private key with the command. It's a client side
decryption:



   $ nova get-password $instance_uuid ~/.ssh/cloud_nopass.priv


Output:



   example_password


If your private key has a password, decrypting only works if you have `ssh-
agent` running:



   $ nova get-password $instance_uuid ~/.ssh/cloud_pass.priv


Output:



   Enter pass phrase for ~/.ssh/cloud_pass.priv:
   example_password


This way you can even use this feature if your SSH private key is stored on a hardware token like [a NitroKey HSM][2] (smartcard), OpenPGP token like the [NitroKey Start][3] or a YubiKey.

Using the Horizon dashboard you can also decrypt the password. This is also a
client side operation and doesn't work if your private key is password
protected.

![password][4]

#### Manual decryption

Using the `openssl` command line tools we can decrypt the encrypted password.
It's base64 encoded so there is an extra step to decode the base64.



   $ echo " bPLUcppp+MXbiO4kFMsIZ1zj4VbS/1kfafWTvqPBKr3B5Rk9z0eLotwVNDGCJkXYGmDmKca0q4hWdAt03N7QZ58DbgF24cvumgQIHddT5D14M98n85oiI3yPd0DCrEhvZQb5jwmYIWBBhqDCuyl+savwFNmVEn2cxdPleCZqvIN7BHs2HRVY7esHDcPiY1+Xdq5SMa92lcsJyn8LJRUUUuq0o0k75+4TTtEWEtClXiMyURIRDgDOpVDXBl2kgeU22a/e1rRKKOKg6CasVu7g0V9fmmLC/zXRmgTqfMwZlm0wDdq0X4jRUYzRNpxLarj6AXGYoSQT6moKx9ttcNL6JQ==" \
   | openssl base64 -d \
   | openssl rsautl -decrypt -inkey ~/.ssh/cloud_pass.priv -keyform PEM


Output:



   Enter pass phrase for ~/.ssh/cloud_pass.priv:
   example_password


### nova set-password

The `nova set-password` command allows you to change the password in a server.
But, only if the [qemu-guest-agent][5] is installed.



   $ nova help set-password
   usage: nova set-password <server>

   Change the admin password for a server.

   Positional arguments:
     <server>  Name or ID of server.


This can only be done by the owner of the instance or an OpenStack admin. It
then allows you to login to the console for example.

By default there is no command line output if successfull.

The `set-password` command fails when a password is already set. Looking at the
source code of the [metadata api][6] there is no command to remove a stored
password. There is a [nova API][7] command but that is admin-only by default.



   $ nova set-password $instance_uuid


Output:



   New password:
   Again:
   ERROR (Conflict): Failed to set admin password on $instance_uuid because error setting admin password (HTTP 409) (Request-ID: req-f3f6feed-4171-45d1-ab16-28a6875688d8)


Following the [nova][8] [source code][9] we can see that in the case of Libvirt
(KVM/QEMU) it calls [virDomainSetUserPassword][10].

So it technically would be possible to (re)set the password again but currently
it's not supported.

### Post encrypted passwords to the metadata service

The metadata service allows posting a password. Technically this can be any sort
of (unencrypted) data that will be made visible in Horizon or the `nova get-
password` command. It is recommended to encrypt this password with the users
provided public key.

Why? Because any code running inside the guest can access the password via the
metadata service. Any web application can request the API URL and get the data:



   $ curl http://169.254.169.254/openstack/latest/password


Output:



    bPLUcppp+MXbiO4kFMsIZ1zj4VbS/1kfafWTvqPBKr3B5Rk9z0eLotwVNDGCJkXYGmDmKca0q4hWdAt03N7QZ58DbgF24cvumgQIHddT5D14M98n85oiI3yPd0DCrEhvZQb5jwmYIWBBhqDCuyl+savwFNmVEn2cxdPleCZqvIN7BHs2HRVY7esHDcPiY1+Xdq5SMa92lcsJyn8LJRUUUuq0o0k75+4TTtEWEtClXiMyURIRDgDOpVDXBl2kgeU22a/e1rRKKOKg6CasVu7g0V9fmmLC/zXRmgTqfMwZlm0wDdq0X4jRUYzRNpxLarj6AXGYoSQT6moKx9ttcNL6JQ==


The password is also stored on the config_drive but that requires root
privileges to mount. If I post unencrypted data to that endpoint:



   DATA="this_is_an_example_raymii.org"
   curl -s -X POST http://169.254.169.254/openstack/latest/password -d $DATA


Horizon (and the API) happily show that:

![][11]

So that's why you want to encrypt it. What we're doing in the script is the
following:

 * Check if password was already set, and if so, exit
 * Get public SSH key from metadata service.
 * Convert the public key to a format usable with OpenSSL
 * Generate random root password
 * Set root password (and any other user like `admin` in the case of DirectAdmin)
 * Encrypt the root password with the SSL keyfile
 * Post the encrypted password to the metadata service.

There is almost no error handling, and a weary catch all in the curl (`||
true`), since this script is meant to run via `/etc/rc.local` (or via cloud-
init) and a non exit 0 in rc.local can result in bootup failure.



   #!/bin/bash
   # Copyright (C) 2018 Remy van Elst.
   # Author: Remy van Elst for https://www.cloudvps.com
   #
   # This program is free software; you can redistribute it and/or modify it
   # under the terms of the GNU General Public License as published by the
   # Free Software Foundation; either version 2 of the License, or (at your
   # option) any later version.
   #
   # This program is distributed in the hope that it will be useful, but
   # WITHOUT ANY WARRANTY; without even the implied warranty of
   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   # General Public License for more details.
   #
   # You should have received a copy of the GNU General Public License along
   # with this program; if not, write to the Free Software Foundation, Inc.,
   # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   #

   # This script sets the root password
   # to a random password generated on the instance
   # and posts that if possible to the openstack
   # metadata service for nova get-password.

   logger "[CLOUDVPS] Started set root password and post to metadata service"

   if [[ -f "/var/lib/cloud/instance/rootpassword-random" ]]; then
       # script already ran on this instance.
       # /var/lib/cloud/instance/ is a symlink to /var/lib/cloud/instances/$instance_uuid
       # if user creates an image and deploys image, this must run again, that file will not exist
       exit 0
   fi

   # Centos 6 doens't support ssh-keygen's pcks8 option.
   # it has OpenSSH < 5.6
   if [[ -f "/etc/redhat-release" ]]; then
       NAME="$(awk '{ print $1 }' /etc/redhat-release)"
       VERSION="$(grep -Eo "[0-9]\.[0-9]" /etc/redhat-release | cut -d . -f 1)"
   fi

   # Two tmp files for the SSH and SSL pubkey
   SSH_KEYFILE=$(mktemp)
   SSL_KEYFILE=$(mktemp)

   # get the ssh public key from the metadata server.
   curl -s -f http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key > $SSH_KEYFILE
   if [[ $? != 0 ]]; then
     logger "[CLOUDVPS] Instance public SSH key not found on metadata service. Unable to set password"
     exit 0
   fi

   # generate a random password
   # our images have haveged installed so should have enough entropy at boot.
   RANDOM_PASSWORD="$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -c 30)"
   if [[ -z ${RANDOM_PASSWORD} ]]; then
       logger "[CLOUDVPS] unable to generate random password."
       exit 0
   fi

   # set the root password to this random password
   # add any other password changes like admin for DirectAdmin.
   echo "root:${RANDOM_PASSWORD}" | chpasswd
   if [[ -s "$SSH_KEYFILE" ]]; then
       # convert the ssh pubkey to an SSL keyfile so that we can use it to encrypt with OpenSSL
       if [[ ${VERSION} -eq 6 && ${NAME} == "CentOS" ]]; then
           logger "centos 6 doesnt support PKCS8 in ssh-keygen so we have a workaround."
           # our centos6 image has this file.
           # otherwise, https://gist.github.com/RaymiiOrg/7cd92ec7b9711fd4b9f6a1178c1bf04f
           python2 /etc/cloudvps/sshpub-to-rsa.py $SSH_KEYFILE > $SSL_KEYFILE
       else
           ssh-keygen -e -f $SSH_KEYFILE -m PKCS8 > $SSL_KEYFILE
       fi
       ENCRYPTED=$(echo "$RANDOM_PASSWORD" | openssl rsautl -encrypt -pubin -inkey $SSL_KEYFILE -keyform PEM | openssl base64 -e -A)
       # post encrypted blob to metadata service. Must return true otherwise instance might fail to boot.
       curl -s -X POST http://169.254.169.254/openstack/2013-04-04/password -d $ENCRYPTED 2>&1 > /dev/null || true
   fi
   # housekeeping
   rm -rf $SSH_KEYFILE $SSL_KEYFILE

   touch /var/lib/cloud/instance/rootpassword-random

   exit 0


#### Running the script at first boot

This script can be ran at first boot of an instance using the good old trusty
`/etc/rc.local` file. If you use a newer distribution with `systemd`, you can
create [a unit file][12] to run this at boot.

You can also leverage `cloud-init` by creating a config file:



   vim /etc/cloud/cloud.cfg.d/00-cloudvps-rootpasswd.cfg


Add the contents:



   runcmd:
   - /bin/bash /etc/cloudvps/rootpassword-to-metadata.sh


In my case the script is injected in the image on that location. You could also
add a `curl` command if you use `cloud-config` (you do not control the cloud-
init config but use `user-data`).

  [1]: https://www.digitalocean.com/?refcode=7435ae6b8212
  [2]: https://raymii.org/s/articles/Get_Started_With_The_Nitrokey_HSM.html
  [3]: https://raymii.org/s/articles/Nitrokey_Start_Getting_started_guide.html
  [4]: https://raymii.org/s/inc/img/retreive_password.png
  [5]: https://wiki.qemu.org/Features/GuestAgent
  [6]: https://web.archive.org/web/20180105005659/https://github.com/openstack/nova/blob/master/nova/api/metadata/password.py#L65
  [7]: https://web.archive.org/web/20180323094911/https://developer.openstack.org/api-ref/compute/#clear-admin-password
  [8]: https://github.com/openstack/nova/blob/master/nova/virt/libvirt/driver.py#L2072
  [9]: https://github.com/openstack/nova/blob/master/nova/virt/libvirt/guest.py#L506
  [10]: https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainSetUserPassword
  [11]: https://raymii.org/s/inc/img/get_plaintext_password.png
  [12]: https://raymii.org/s/tutorials/rc.local_support_on_Arch_Linux_and_systemd.html

---

License:
All the text on this website is free as in freedom unless stated otherwise.
This means you can use it in any way you want, you can copy it, change it
the way you like and republish it, as long as you release the (modified)
content under the same license to give others the same freedoms you've got
and place my name and a link to this site with the article as source.

This site uses Google Analytics for statistics and Google Adwords for
advertisements. You are tracked and Google knows everything about you.
Use an adblocker like ublock-origin if you don't want it.

All the code on this website is licensed under the GNU GPL v3 license
unless already licensed under a license which does not allows this form
of licensing or if another license is stated on that page / in that software:

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

Just to be clear, the information on this website is for meant for educational
purposes and you use it at your own risk. I do not take responsibility if you
screw something up. Use common sense, do not 'rm -rf /' as root for example.
If you have any questions then do not hesitate to contact me.

See https://raymii.org/s/static/About.html for details.