Allows users to retrieve ciphered VM passwords

This patch allows users to retrieve VM encrypted passwords using the
`nova get-password` command without specifying the private key.

Change-Id: I13ea132160dca912c6c1643b1006377982b778a1
Implements: blueprint retrieve-ciphered-vm-password
This commit is contained in:
Florent Flament 2013-11-19 15:33:14 +01:00
parent c51dc4a525
commit fabbc87bf2
7 changed files with 105 additions and 10 deletions

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA9QstF/7prDY7a9La7GS9TpMX+MWWXQgK6pHRLakDFp1WX1Q3
Vly7rWitaZUGirUPMm181oJXBwkKlAxFD7hKjyHYaSswNszPYIAsVkc1+AO5epXz
g9kUBNtfg44Pg72UecwLrZ8JpmNZpJlKQOx6vF+yi7JmHrrIf6il/grIGUPzoT2L
yReimpyPoBrGtXhJYaCJ/XbKg1idRZiQdmwh1F/OmZWn9p0wunnsv08a0+qIywuw
WhG9/Zy9fjnEByfusS6gI0GIxDRL4RWzOqphd3PZzunwIBgEKFhgiki9+2DgcRVO
9I5wnDvfwQREJRZWh1uJa5ZTcfPa1EzZryVeOQIDAQABAoIBABxO3Te/cBk/7p9n
LXlPrfrszUEk+ljm+/PbQpIGy1+Kb5b1sKrebaP7ysS+vZG6lvXZZimVxx398mXm
APhu7tYYL9r+bUR3ZqGcTQLumRJ8w6mgtxANPN3Oxfr5p1stxIBJjTPSgpfhNFLq
joRvjUJDv+mZg2ibZVwyDHMLpdAdKp+3XMdyTLZcH9esqwii+natix7rHd1RuF85
L1dfpxjkItwhgHsfdYS++5X3fRByFOhQ+Nhabh/kPQbQMcteRn1bN6zeCWBSglNb
Ka/ZrXb6ApRUc22Ji62mNO2ZPPekLJeCHk2h2E7ezYX+sGDNvvd/jHVDJJ20FjD1
Z9KXuK0CgYEA/2vniy9yWd925QQtWbmrxgy6yj89feMH/LTv4qP298rGZ2nqxsyd
9pdBdb4NMsi4HmV5PG1hp3VRNBHl53DNh5eqzT8WEXnIF+sbrIU3KzrCVAx1kZTl
+OWKA6aVUsvvO3y85SOvInnsV+IsOGmU4/WBSjYoe39Bo7mq/YuZB9MCgYEA9ZlB
KBm6PjFdHQGNgedXahWzRcwC+ALCYqequPYqJolNzhrK4Uc2sWPSGdnldcHZ4XCQ
wbfCxUSwrMpA1oyuIQ0U4aowmOw5DjIueBWI8XBYEVRBlwvJwbXpBZ/DspGzTUDx
MBrrEwEaMadQvxhRnAzhp0rQAepatcz6Fgb1JkMCgYBMwDLiew5kfSav6JJsDMPW
DksurNQgeNEUmZYfx19V1EPMHWKj/CZXS9oqtEIpCXFyCNHmW4PlmvYcrGgmJJpN
7UAwzo0mES8UKNy2+Yy7W7u7H8dQSKrWILtZH3xtVcR8Xp4wSIm+1V40hkz9YpSP
71y7XQzLF1E1DnyYFZOVawKBgAFrmHfd5jjT2kD/sEzPBK9lXrsJmf7LLUqaw578
NXQxmRSXDRNOcR+Hf0CNBQmwTE1EdGHaaTLw2cC2Drfu6lbgl31SmaNYwl+1pJUn
MrqKtseq4BI6jDkljypsKRqQQyQwOvTXQwLCH9+nowzn3Bj17hwkj51jOJESlWOp
OKO3AoGBALm+jjqyqX7gSnqK3FAumB8mlhv3yI1Wr1ctwe18mKfKbz17HxXRu9pF
K/6e7WMCA1p+jhoE8gj1h2WBcH0nV2qt8Ye8gJBbCi4dhI08o4AfrIV47oZx1RlO
qYcA1U9lyaODY5SL8+6PHOy5J/aYtuA+wvfEnWiCIdKQrhWetcn3
-----END RSA PRIVATE KEY-----

View File

@ -483,8 +483,27 @@ class FakeHTTPClient(base_client.HTTPClient):
# Server password
#
# Testing with the following password and key
#
# Clear password: FooBar123
#
# RSA Private Key: novaclient/tests/idfake.pem
#
# Encrypted password
# OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r
# qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho
# QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw
# /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N
# tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk
# Hi/fmZZNQQqj1Ijq0caOIw==
def get_servers_1234_os_server_password(self, **kw):
return (200, {}, {'password': ''})
return (200, {}, {'password':
'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r'
'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho'
'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw'
'/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N'
'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk'
'Hi/fmZZNQQqj1Ijq0caOIw=='})
def delete_servers_1234_os_server_password(self, **kw):
return (202, {}, None)

View File

@ -420,9 +420,35 @@ class ServersTest(utils.TestCase):
self.assertEqual(cs.servers.get_console_output(s, length=50), success)
cs.assert_called('POST', '/servers/1234/action')
# Testing password methods with the following password and key
#
# Clear password: FooBar123
#
# RSA Private Key: novaclient/tests/idfake.pem
#
# Encrypted password
# OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r
# qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho
# QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw
# /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N
# tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk
# Hi/fmZZNQQqj1Ijq0caOIw==
def test_get_password(self):
s = cs.servers.get(1234)
self.assertEqual(s.get_password('/foo/id_rsa'), '')
self.assertEqual(s.get_password('novaclient/tests/idfake.pem'),
b'FooBar123')
cs.assert_called('GET', '/servers/1234/os-server-password')
def test_get_password_without_key(self):
s = cs.servers.get(1234)
self.assertEqual(s.get_password(),
'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r'
'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho'
'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw'
'/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N'
'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk'
'Hi/fmZZNQQqj1Ijq0caOIw==')
cs.assert_called('GET', '/servers/1234/os-server-password')
def test_clear_password(self):

View File

@ -1635,6 +1635,10 @@ class ShellTest(utils.TestCase):
self.run_command('get-password sample-server /foo/id_rsa')
self.assert_called('GET', '/servers/1234/os-server-password')
def test_get_password_without_key(self):
self.run_command('get-password sample-server')
self.assert_called('GET', '/servers/1234/os-server-password')
def test_clear_password(self):
self.run_command('clear-password sample-server')
self.assert_called('DELETE', '/servers/1234/os-server-password')

View File

@ -74,11 +74,15 @@ class Server(base.Resource):
"""
return self.manager.get_spice_console(self, console_type)
def get_password(self, private_key):
def get_password(self, private_key=None):
"""
Get password for a Server.
Returns the clear password of an instance if private_key is
provided, returns the ciphered password otherwise.
:param private_key: Path to private key file for decryption
(optional)
"""
return self.manager.get_password(self, private_key)
@ -497,24 +501,29 @@ class ServerManager(base.BootingManagerWithFind):
return self._action('os-getSPICEConsole', server,
{'type': console_type})[1]
def get_password(self, server, private_key):
def get_password(self, server, private_key=None):
"""
Get password for an instance
Returns the clear password of an instance if private_key is
provided, returns the ciphered password otherwise.
Requires that openssl is installed and in the path
:param server: The :class:`Server` (or its ID) to add an IP to.
:param private_key: The private key to decrypt password
(optional)
"""
_resp, body = self.api.client.get("/servers/%s/os-server-password"
% base.getid(server))
if body and body.get('password'):
ciphered_pw = body.get('password', '') if body else ''
if private_key and ciphered_pw:
try:
return crypto.decrypt_password(private_key, body['password'])
return crypto.decrypt_password(private_key, ciphered_pw)
except Exception as exc:
return '%sFailed to decrypt:\n%s' % (exc, body['password'])
return ''
return '%sFailed to decrypt:\n%s' % (exc, ciphered_pw)
return ciphered_pw
def clear_password(self, server):
"""

View File

@ -1867,7 +1867,12 @@ def do_get_spice_console(cs, args):
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
@utils.arg('private_key',
metavar='<private-key>',
help='Private key (used locally to decrypt password).')
help='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.',
nargs='?',
default=None)
def do_get_password(cs, args):
"""Get password for a server."""
server = _find_server(cs, args.server)

View File

@ -1701,7 +1701,12 @@ def do_get_spice_console(cs, args):
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
@utils.arg('private_key',
metavar='<private-key>',
help='Private key (used locally to decrypt password).')
help='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.',
nargs='?',
default=None)
def do_get_password(cs, args):
"""Get password for a server."""
server = _find_server(cs, args.server)