Commit 92ceca45 authored by Ben Howard's avatar Ben Howard

AZURE: support extracting SSH key values from ovf-env.xml

 Azure has or will be offering shortly the ability to directly define the SSH
 key value instead of a fingerprint in the ovf-env.xml file. This patch
 favors defined SSH keys over the fingerprint method (LP: #1506244).
parent e9e86164
...@@ -148,9 +148,15 @@ class DataSourceAzureNet(sources.DataSource): ...@@ -148,9 +148,15 @@ class DataSourceAzureNet(sources.DataSource):
wait_for = [shcfgxml] wait_for = [shcfgxml]
fp_files = [] fp_files = []
key_value = None
for pk in self.cfg.get('_pubkeys', []): for pk in self.cfg.get('_pubkeys', []):
bname = str(pk['fingerprint'] + ".crt") if pk.get('value', None):
fp_files += [os.path.join(ddir, bname)] key_value = pk['value']
LOG.info("ssh authentication: using value from fabric")
else:
bname = str(pk['fingerprint'] + ".crt")
fp_files += [os.path.join(ddir, bname)]
LOG.info("ssh authentication: using fingerprint from fabirc")
missing = util.log_time(logfunc=LOG.debug, msg="waiting for files", missing = util.log_time(logfunc=LOG.debug, msg="waiting for files",
func=wait_for_files, func=wait_for_files,
...@@ -166,7 +172,8 @@ class DataSourceAzureNet(sources.DataSource): ...@@ -166,7 +172,8 @@ class DataSourceAzureNet(sources.DataSource):
metadata['instance-id'] = iid_from_shared_config(shcfgxml) metadata['instance-id'] = iid_from_shared_config(shcfgxml)
except ValueError as e: except ValueError as e:
LOG.warn("failed to get instance id in %s: %s", shcfgxml, e) LOG.warn("failed to get instance id in %s: %s", shcfgxml, e)
metadata['public-keys'] = pubkeys_from_crt_files(fp_files)
metadata['public-keys'] = key_value or pubkeys_from_crt_files(fp_files)
return metadata return metadata
def get_data(self): def get_data(self):
...@@ -497,7 +504,8 @@ def load_azure_ovf_pubkeys(sshnode): ...@@ -497,7 +504,8 @@ def load_azure_ovf_pubkeys(sshnode):
for pk_node in pubkeys: for pk_node in pubkeys:
if not pk_node.hasChildNodes(): if not pk_node.hasChildNodes():
continue continue
cur = {'fingerprint': "", 'path': ""}
cur = {'fingerprint': "", 'path': "", 'value': ""}
for child in pk_node.childNodes: for child in pk_node.childNodes:
if child.nodeType == text_node or not child.localName: if child.nodeType == text_node or not child.localName:
continue continue
......
...@@ -54,10 +54,13 @@ def construct_valid_ovf_env(data=None, pubkeys=None, userdata=None): ...@@ -54,10 +54,13 @@ def construct_valid_ovf_env(data=None, pubkeys=None, userdata=None):
if pubkeys: if pubkeys:
content += "<SSH><PublicKeys>\n" content += "<SSH><PublicKeys>\n"
for fp, path in pubkeys: for fp, path, value in pubkeys:
content += " <PublicKey>" content += " <PublicKey>"
content += ("<Fingerprint>%s</Fingerprint><Path>%s</Path>" % if fp and path:
(fp, path)) content += ("<Fingerprint>%s</Fingerprint><Path>%s</Path>" %
(fp, path))
if value:
content += "<Value>%s</Value>" % value
content += "</PublicKey>\n" content += "</PublicKey>\n"
content += "</PublicKeys></SSH>" content += "</PublicKeys></SSH>"
content += """ content += """
...@@ -297,10 +300,10 @@ class TestAzureDataSource(TestCase): ...@@ -297,10 +300,10 @@ class TestAzureDataSource(TestCase):
self.assertFalse(ret) self.assertFalse(ret)
self.assertFalse('agent_invoked' in data) self.assertFalse('agent_invoked' in data)
def test_cfg_has_pubkeys(self): def test_cfg_has_pubkeys_fingerprint(self):
odata = {'HostName': "myhost", 'UserName': "myuser"} odata = {'HostName': "myhost", 'UserName': "myuser"}
mypklist = [{'fingerprint': 'fp1', 'path': 'path1'}] mypklist = [{'fingerprint': 'fp1', 'path': 'path1', 'value': ''}]
pubkeys = [(x['fingerprint'], x['path']) for x in mypklist] pubkeys = [(x['fingerprint'], x['path'], x['value']) for x in mypklist]
data = {'ovfcontent': construct_valid_ovf_env(data=odata, data = {'ovfcontent': construct_valid_ovf_env(data=odata,
pubkeys=pubkeys)} pubkeys=pubkeys)}
...@@ -309,6 +312,39 @@ class TestAzureDataSource(TestCase): ...@@ -309,6 +312,39 @@ class TestAzureDataSource(TestCase):
self.assertTrue(ret) self.assertTrue(ret)
for mypk in mypklist: for mypk in mypklist:
self.assertIn(mypk, dsrc.cfg['_pubkeys']) self.assertIn(mypk, dsrc.cfg['_pubkeys'])
self.assertIn('pubkey_from', dsrc.metadata['public-keys'][-1])
def test_cfg_has_pubkeys_value(self):
# make sure that provided key is used over fingerprint
odata = {'HostName': "myhost", 'UserName': "myuser"}
mypklist = [{'fingerprint': 'fp1', 'path': 'path1', 'value': 'value1'}]
pubkeys = [(x['fingerprint'], x['path'], x['value']) for x in mypklist]
data = {'ovfcontent': construct_valid_ovf_env(data=odata,
pubkeys=pubkeys)}
dsrc = self._get_ds(data)
ret = dsrc.get_data()
self.assertTrue(ret)
for mypk in mypklist:
self.assertIn(mypk, dsrc.cfg['_pubkeys'])
self.assertIn(mypk['value'], dsrc.metadata['public-keys'])
def test_cfg_has_no_fingerprint_has_value(self):
# test value is used when fingerprint not provided
odata = {'HostName': "myhost", 'UserName': "myuser"}
mypklist = [{'fingerprint': None, 'path': 'path1', 'value': 'value1'}]
pubkeys = [(x['fingerprint'], x['path'], x['value']) for x in mypklist]
data = {'ovfcontent': construct_valid_ovf_env(data=odata,
pubkeys=pubkeys)}
dsrc = self._get_ds(data)
ret = dsrc.get_data()
self.assertTrue(ret)
for mypk in mypklist:
self.assertIn(mypk['value'], dsrc.metadata['public-keys'])
def test_default_ephemeral(self): def test_default_ephemeral(self):
# make sure the ephemeral device works # make sure the ephemeral device works
...@@ -642,8 +678,8 @@ class TestReadAzureOvf(TestCase): ...@@ -642,8 +678,8 @@ class TestReadAzureOvf(TestCase):
DataSourceAzure.read_azure_ovf, invalid_xml) DataSourceAzure.read_azure_ovf, invalid_xml)
def test_load_with_pubkeys(self): def test_load_with_pubkeys(self):
mypklist = [{'fingerprint': 'fp1', 'path': 'path1'}] mypklist = [{'fingerprint': 'fp1', 'path': 'path1', 'value': ''}]
pubkeys = [(x['fingerprint'], x['path']) for x in mypklist] pubkeys = [(x['fingerprint'], x['path'], x['value']) for x in mypklist]
content = construct_valid_ovf_env(pubkeys=pubkeys) content = construct_valid_ovf_env(pubkeys=pubkeys)
(_md, _ud, cfg) = DataSourceAzure.read_azure_ovf(content) (_md, _ud, cfg) = DataSourceAzure.read_azure_ovf(content)
for mypk in mypklist: for mypk in mypklist:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment