Skip to main content

Raymii.org Logo (IEC resistor symbol)logo

Quis custodiet ipsos custodes?
Home | About | All pages | RSS Feed | Gopher

OpenStack nova get-password, set-password and post encrypted password to metadata service

Published: 25-03-2018 | Author: Remy van Elst | Text only version of this article


Table of Contents


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 yourinstance further. OpenStack provides the metadata service for instances, whichsupplies information for the instance, like its public IP, SSH public key thatwas provided and vendor or user provided data like scripts or information. TheOpenStack metadata service allows an instance to post data to an endpoint wichcan be retreived with the nova get-password command. It is meant to be anencrypted password (with the public SSH key) but it can be any plain text aswell and it doesn't have to be the root password. In this guide I'll go over thescripts I use inside linux images to post a password to the metadata service andthe nova commands such as set-password and get-password. That includesdecrypting a password with an SSH key that is password-protected (Horizon andnova don't support that) and the nova set-password command, which sets theroot password inside an instance when it has the qemu-guest-agent installedand running.

If you like this article, consider sponsoring me by trying out a Digital OceanVPS. With this link you'll get $100 credit for 60 days). (referral link)

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 insidecloud instances.

nova get-password

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

$ nova help get-passwordusage: 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 sidedecryption:

$ 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 (smartcard), OpenPGP token like the NitroKey Start or a YubiKey.

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

password

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 is installed.

$ nova help set-passwordusage: 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. Itthen 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 thesource code of the metadata api there is no command to remove a storedpassword. There is a nova API 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 novasource code we can see that in the case of Libvirt(KVM/QEMU) it calls virDomainSetUserPassword.

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

Post encrypted passwords to the metadata service

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

Why? Because any code running inside the guest can access the password via themetadata 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 rootprivileges 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:

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

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 0fi# Centos 6 doens't support ssh-keygen's pcks8 option.# it has OpenSSH < 5.6if [[ -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 pubkeySSH_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_KEYFILEif [[ $? != 0 ]]; then  logger "[CLOUDVPS] Instance public SSH key not found on metadata service. Unable to set password"  exit 0fi# 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 0fi# set the root password to this random password# add any other password changes like admin for DirectAdmin.echo "root:${RANDOM_PASSWORD}" | chpasswdif [[ -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 || truefi# housekeepingrm -rf $SSH_KEYFILE $SSL_KEYFILEtouch /var/lib/cloud/instance/rootpassword-randomexit 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 cancreate a unit file 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 alsoadd a curl command if you use cloud-config (you do not control the cloud-init config but use user-data).

Tags: cloud, debian, encryption, metadata, nova, openstack, password, security, ssh, tutorials