SSH known_hosts config
authorOliver Walsh <owalsh@redhat.com>
Fri, 24 Mar 2017 14:35:09 +0000 (14:35 +0000)
committerOliver Walsh <owalsh@redhat.com>
Thu, 13 Apr 2017 20:53:59 +0000 (21:53 +0100)
Fetch the host public keys from each node, combine them all and write to the
system-wide ssh known hosts. The alternative of disabling host key
 verification is vulnerable to a MITM attack.

Change-Id: Ib572b5910720b1991812256e68c975f7fbe2239c

extraconfig/tasks/ssh/host_public_key.yaml [new file with mode: 0644]
extraconfig/tasks/ssh/known_hosts_config.yaml [new file with mode: 0644]
overcloud-resource-registry-puppet.j2.yaml
overcloud.j2.yaml
puppet/blockstorage-role.yaml
puppet/cephstorage-role.yaml
puppet/compute-role.yaml
puppet/controller-role.yaml
puppet/objectstorage-role.yaml
puppet/role.role.j2.yaml
releasenotes/notes/ssh_known_hosts-287563590632d1aa.yaml [new file with mode: 0644]

diff --git a/extraconfig/tasks/ssh/host_public_key.yaml b/extraconfig/tasks/ssh/host_public_key.yaml
new file mode 100644 (file)
index 0000000..847c877
--- /dev/null
@@ -0,0 +1,42 @@
+heat_template_version: ocata
+
+description: >
+  This is a template which will fetch the ssh host public key.
+
+parameters:
+  server:
+    description: ID of the node to apply this config to
+    type: string
+
+resources:
+  SshHostPubKeyConfig:
+    type: OS::Heat::SoftwareConfig
+    properties:
+      group: script
+      outputs:
+      - name: rsa
+      - name: ecdsa
+      - name: ed25519
+      config: |
+        #!/bin/sh -x
+        test -e '/etc/ssh/ssh_host_rsa_key.pub' && cat /etc/ssh/ssh_host_rsa_key.pub > $heat_outputs_path.rsa
+        test -e '/etc/ssh/ssh_host_ecdsa_key.pub' && cat /etc/ssh/ssh_host_ecdsa_key.pub > $heat_outputs_path.ecdsa
+        test -e '/etc/ssh/ssh_host_ed25519_key.pub' && cat /etc/ssh/ssh_host_ed25519_key.pub > $heat_outputs_path.ed25519
+
+  SshHostPubKeyDeployment:
+    type: OS::Heat::SoftwareDeployment
+    properties:
+      config: {get_resource: SshHostPubKeyConfig}
+      server: {get_param: server}
+
+
+outputs:
+  ecdsa:
+    description: Host ssh public key (ecdsa)
+    value:  {get_attr: [SshHostPubKeyDeployment, ecdsa]}
+  rsa:
+    description: Host ssh public key (rsa)
+    value:  {get_attr: [SshHostPubKeyDeployment, rsa]}
+  ed25519:
+    description: Host ssh public key (ed25519)
+    value:  {get_attr: [SshHostPubKeyDeployment, ed25519]}
diff --git a/extraconfig/tasks/ssh/known_hosts_config.yaml b/extraconfig/tasks/ssh/known_hosts_config.yaml
new file mode 100644 (file)
index 0000000..2ebcb63
--- /dev/null
@@ -0,0 +1,36 @@
+heat_template_version: ocata
+description: 'SSH Known Hosts Config'
+
+parameters:
+  known_hosts:
+    type: string
+
+resources:
+
+  SSHKnownHostsConfig:
+    type: OS::Heat::SoftwareConfig
+    properties:
+      group: script
+      inputs:
+        - name: known_hosts
+          default: {get_param: known_hosts}
+      config: |
+        #!/bin/bash
+        set -eux
+        set -o pipefail
+
+        echo "Creating ssh known hosts file"
+
+        if [ ! -z "${known_hosts}" ]; then
+          echo "${known_hosts}"
+          echo -ne "${known_hosts}" > /etc/ssh/ssh_known_hosts
+          chmod 0644 /etc/ssh/ssh_known_hosts
+        else
+          rm -f /etc/ssh/ssh_known_hosts
+          echo "No ssh known hosts"
+        fi
+
+outputs:
+  OS::stack_id:
+    description: The SSHKnownHostsConfig resource.
+    value: {get_resource: SSHKnownHostsConfig}
\ No newline at end of file
index d9eaf8d..b70d4a2 100644 (file)
@@ -5,6 +5,8 @@ resource_registry:
   OS::TripleO::PostUpgradeSteps: puppet/post-upgrade.yaml
   OS::TripleO::AllNodes::SoftwareConfig: puppet/all-nodes-config.yaml
   OS::TripleO::Hosts::SoftwareConfig: hosts-config.yaml
+  OS::TripleO::Ssh::HostPubKey: extraconfig/tasks/ssh/host_public_key.yaml
+  OS::TripleO::Ssh::KnownHostsConfig: extraconfig/tasks/ssh/known_hosts_config.yaml
   OS::TripleO::DefaultPasswords: default_passwords.yaml
 
   # Tasks (for internal TripleO usage)
index e99f770..584fdfd 100644 (file)
@@ -249,6 +249,16 @@ resources:
       type: json
       value: {get_attr: [EndpointMap, endpoint_map]}
 
+  SshKnownHostsConfig:
+    type: OS::TripleO::Ssh::KnownHostsConfig
+    properties:
+      known_hosts:
+        list_join:
+          - ''
+          {% for role in roles %}
+          - {get_attr: [{{role.name}}, known_hosts_entry]}
+          {% endfor %}
+
   # Jinja loop for Role in roles_data.yaml
 {% for role in roles %}
   # Resources generated for {{role.name}} Role
@@ -280,6 +290,13 @@ resources:
       config: {get_attr: [hostsConfig, config_id]}
       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
 
+  {{role.name}}SshKnownHostsDeployment:
+    type: OS::Heat::StructuredDeployments
+    properties:
+      name: {{role.name}}SshKnownHostsDeployment
+      config: {get_resource: SshKnownHostsConfig}
+      servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
+
   {{role.name}}AllNodesDeployment:
     type: OS::Heat::StructuredDeployments
     depends_on:
index 51f9aba..16fb4b9 100644 (file)
@@ -457,6 +457,12 @@ resources:
         update_identifier:
           get_param: UpdateIdentifier
 
+  SshHostPubKey:
+    type: OS::TripleO::Ssh::HostPubKey
+    depends_on: BlockStorageDeployment
+    properties:
+        server: {get_resource: BlockStorage}
+
 outputs:
   ip_address:
     description: IP address of the server in the ctlplane network
@@ -504,6 +510,37 @@ outputs:
           MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
           CTLPLANEIP: {get_attr: [BlockStorage, networks, ctlplane, 0]}
           CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+  known_hosts_entry:
+    description: Entry for ssh known hosts
+    value:
+      str_replace:
+        template: "PRIMARYIP,PRIMARYHOST.DOMAIN,PRIMARYHOST,\
+EXTERNALIP,EXTERNALHOST.DOMAIN,EXTERNALHOST,\
+INTERNAL_APIIP,INTERNAL_APIHOST.DOMAIN,INTERNAL_APIHOST,\
+STORAGEIP,STORAGEHOST.DOMAIN,STORAGEHOST,\
+STORAGE_MGMTIP,STORAGE_MGMTHOST.DOMAIN,STORAGE_MGMTHOST,\
+TENANTIP,TENANTHOST.DOMAIN,TENANTHOST,\
+MANAGEMENTIP,MANAGEMENTHOST.DOMAIN,MANAGEMENTHOST,\
+CTLPLANEIP,CTLPLANEHOST.DOMAIN,CTLPLANEHOST HOSTSSHPUBKEY"
+        params:
+          PRIMARYIP: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, BlockStorageHostnameResolveNetwork]}]}
+          DOMAIN: {get_param: CloudDomain}
+          PRIMARYHOST: {get_attr: [BlockStorage, name]}
+          EXTERNALIP: {get_attr: [ExternalPort, ip_address]}
+          EXTERNALHOST: {get_attr: [NetHostMap, value, external, short]}
+          INTERNAL_APIIP: {get_attr: [InternalApiPort, ip_address]}
+          INTERNAL_APIHOST: {get_attr: [NetHostMap, value, internal_api, short]}
+          STORAGEIP: {get_attr: [StoragePort, ip_address]}
+          STORAGEHOST: {get_attr: [NetHostMap, value, storage, short]}
+          STORAGE_MGMTIP: {get_attr: [StorageMgmtPort, ip_address]}
+          STORAGE_MGMTHOST: {get_attr: [NetHostMap, value, storage_mgmt, short]}
+          TENANTIP: {get_attr: [TenantPort, ip_address]}
+          TENANTHOST: {get_attr: [NetHostMap, value, tenant, short]}
+          MANAGEMENTIP: {get_attr: [ManagementPort, ip_address]}
+          MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
+          CTLPLANEIP: {get_attr: [BlockStorage, networks, ctlplane, 0]}
+          CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+          HOSTSSHPUBKEY: {get_attr: [SshHostPubKey, ecdsa]}
   nova_server_resource:
     description: Heat resource handle for the block storage server
     value:
index d7d7f47..4b02245 100644 (file)
@@ -468,6 +468,12 @@ resources:
         update_identifier:
           get_param: UpdateIdentifier
 
+  SshHostPubKey:
+    type: OS::TripleO::Ssh::HostPubKey
+    depends_on: CephStorageDeployment
+    properties:
+        server: {get_resource: CephStorage}
+
 outputs:
   ip_address:
     description: IP address of the server in the ctlplane network
@@ -515,6 +521,37 @@ outputs:
           MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
           CTLPLANEIP: {get_attr: [CephStorage, networks, ctlplane, 0]}
           CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+  known_hosts_entry:
+    description: Entry for ssh known hosts
+    value:
+      str_replace:
+        template: "PRIMARYIP,PRIMARYHOST.DOMAIN,PRIMARYHOST,\
+EXTERNALIP,EXTERNALHOST.DOMAIN,EXTERNALHOST,\
+INTERNAL_APIIP,INTERNAL_APIHOST.DOMAIN,INTERNAL_APIHOST,\
+STORAGEIP,STORAGEHOST.DOMAIN,STORAGEHOST,\
+STORAGE_MGMTIP,STORAGE_MGMTHOST.DOMAIN,STORAGE_MGMTHOST,\
+TENANTIP,TENANTHOST.DOMAIN,TENANTHOST,\
+MANAGEMENTIP,MANAGEMENTHOST.DOMAIN,MANAGEMENTHOST,\
+CTLPLANEIP,CTLPLANEHOST.DOMAIN,CTLPLANEHOST HOSTSSHPUBKEY"
+        params:
+          PRIMARYIP: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, CephStorageHostnameResolveNetwork]}]}
+          DOMAIN: {get_param: CloudDomain}
+          PRIMARYHOST: {get_attr: [CephStorage, name]}
+          EXTERNALIP: {get_attr: [ExternalPort, ip_address]}
+          EXTERNALHOST: {get_attr: [NetHostMap, value, external, short]}
+          INTERNAL_APIIP: {get_attr: [InternalApiPort, ip_address]}
+          INTERNAL_APIHOST: {get_attr: [NetHostMap, value, internal_api, short]}
+          STORAGEIP: {get_attr: [StoragePort, ip_address]}
+          STORAGEHOST: {get_attr: [NetHostMap, value, storage, short]}
+          STORAGE_MGMTIP: {get_attr: [StorageMgmtPort, ip_address]}
+          STORAGE_MGMTHOST: {get_attr: [NetHostMap, value, storage_mgmt, short]}
+          TENANTIP: {get_attr: [TenantPort, ip_address]}
+          TENANTHOST: {get_attr: [NetHostMap, value, tenant, short]}
+          MANAGEMENTIP: {get_attr: [ManagementPort, ip_address]}
+          MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
+          CTLPLANEIP: {get_attr: [CephStorage, networks, ctlplane, 0]}
+          CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+          HOSTSSHPUBKEY: {get_attr: [SshHostPubKey, ecdsa]}
   nova_server_resource:
     description: Heat resource handle for the ceph storage server
     value:
index ebdd762..37331f3 100644 (file)
@@ -492,6 +492,12 @@ resources:
         update_identifier:
           get_param: UpdateIdentifier
 
+  SshHostPubKey:
+    type: OS::TripleO::Ssh::HostPubKey
+    depends_on: NovaComputeDeployment
+    properties:
+        server: {get_resource: NovaCompute}
+
 outputs:
   ip_address:
     description: IP address of the server in the ctlplane network
@@ -559,7 +565,38 @@ outputs:
           MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
           CTLPLANEIP: {get_attr: [NovaCompute, networks, ctlplane, 0]}
           CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+  known_hosts_entry:
+    description: Entry for ssh known hosts
+    value:
+      str_replace:
+        template: "PRIMARYIP,PRIMARYHOST.DOMAIN,PRIMARYHOST,\
+EXTERNALIP,EXTERNALHOST.DOMAIN,EXTERNALHOST,\
+INTERNAL_APIIP,INTERNAL_APIHOST.DOMAIN,INTERNAL_APIHOST,\
+STORAGEIP,STORAGEHOST.DOMAIN,STORAGEHOST,\
+STORAGE_MGMTIP,STORAGE_MGMTHOST.DOMAIN,STORAGE_MGMTHOST,\
+TENANTIP,TENANTHOST.DOMAIN,TENANTHOST,\
+MANAGEMENTIP,MANAGEMENTHOST.DOMAIN,MANAGEMENTHOST,\
+CTLPLANEIP,CTLPLANEHOST.DOMAIN,CTLPLANEHOST HOSTSSHPUBKEY"
+        params:
+          PRIMARYIP: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, ComputeHostnameResolveNetwork]}]}
+          DOMAIN: {get_param: CloudDomain}
+          PRIMARYHOST: {get_attr: [NovaCompute, name]}
+          EXTERNALIP: {get_attr: [ExternalPort, ip_address]}
+          EXTERNALHOST: {get_attr: [NetHostMap, value, external, short]}
+          INTERNAL_APIIP: {get_attr: [InternalApiPort, ip_address]}
+          INTERNAL_APIHOST: {get_attr: [NetHostMap, value, internal_api, short]}
+          STORAGEIP: {get_attr: [StoragePort, ip_address]}
+          STORAGEHOST: {get_attr: [NetHostMap, value, storage, short]}
+          STORAGE_MGMTIP: {get_attr: [StorageMgmtPort, ip_address]}
+          STORAGE_MGMTHOST: {get_attr: [NetHostMap, value, storage_mgmt, short]}
+          TENANTIP: {get_attr: [TenantPort, ip_address]}
+          TENANTHOST: {get_attr: [NetHostMap, value, tenant, short]}
+          MANAGEMENTIP: {get_attr: [ManagementPort, ip_address]}
+          MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
+          CTLPLANEIP: {get_attr: [NovaCompute, networks, ctlplane, 0]}
+          CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+          HOSTSSHPUBKEY: {get_attr: [SshHostPubKey, ecdsa]}
   nova_server_resource:
     description: Heat resource handle for the Nova compute server
     value:
-      {get_resource: NovaCompute}
+      {get_resource: NovaCompute}
\ No newline at end of file
index 2f4f583..4099a3a 100644 (file)
@@ -532,6 +532,12 @@ resources:
         update_identifier:
           get_param: UpdateIdentifier
 
+  SshHostPubKey:
+    type: OS::TripleO::Ssh::HostPubKey
+    depends_on: ControllerDeployment
+    properties:
+        server: {get_resource: Controller}
+
 outputs:
   ip_address:
     description: IP address of the server in the ctlplane network
@@ -599,6 +605,37 @@ outputs:
           MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
           CTLPLANEIP: {get_attr: [Controller, networks, ctlplane, 0]}
           CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+  known_hosts_entry:
+    description: Entry for ssh known hosts
+    value:
+      str_replace:
+        template: "PRIMARYIP,PRIMARYHOST.DOMAIN,PRIMARYHOST,\
+EXTERNALIP,EXTERNALHOST.DOMAIN,EXTERNALHOST,\
+INTERNAL_APIIP,INTERNAL_APIHOST.DOMAIN,INTERNAL_APIHOST,\
+STORAGEIP,STORAGEHOST.DOMAIN,STORAGEHOST,\
+STORAGE_MGMTIP,STORAGE_MGMTHOST.DOMAIN,STORAGE_MGMTHOST,\
+TENANTIP,TENANTHOST.DOMAIN,TENANTHOST,\
+MANAGEMENTIP,MANAGEMENTHOST.DOMAIN,MANAGEMENTHOST,\
+CTLPLANEIP,CTLPLANEHOST.DOMAIN,CTLPLANEHOST HOSTSSHPUBKEY"
+        params:
+          PRIMARYIP: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, ControllerHostnameResolveNetwork]}]}
+          DOMAIN: {get_param: CloudDomain}
+          PRIMARYHOST: {get_attr: [Controller, name]}
+          EXTERNALIP: {get_attr: [ExternalPort, ip_address]}
+          EXTERNALHOST: {get_attr: [NetHostMap, value, external, short]}
+          INTERNAL_APIIP: {get_attr: [InternalApiPort, ip_address]}
+          INTERNAL_APIHOST: {get_attr: [NetHostMap, value, internal_api, short]}
+          STORAGEIP: {get_attr: [StoragePort, ip_address]}
+          STORAGEHOST: {get_attr: [NetHostMap, value, storage, short]}
+          STORAGE_MGMTIP: {get_attr: [StorageMgmtPort, ip_address]}
+          STORAGE_MGMTHOST: {get_attr: [NetHostMap, value, storage_mgmt, short]}
+          TENANTIP: {get_attr: [TenantPort, ip_address]}
+          TENANTHOST: {get_attr: [NetHostMap, value, tenant, short]}
+          MANAGEMENTIP: {get_attr: [ManagementPort, ip_address]}
+          MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
+          CTLPLANEIP: {get_attr: [Controller, networks, ctlplane, 0]}
+          CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+          HOSTSSHPUBKEY: {get_attr: [SshHostPubKey, ecdsa]}
   nova_server_resource:
     description: Heat resource handle for the Nova compute server
     value:
index 6ee06d7..a329d13 100644 (file)
@@ -455,6 +455,12 @@ resources:
         update_identifier:
           get_param: UpdateIdentifier
 
+  SshHostPubKey:
+    type: OS::TripleO::Ssh::HostPubKey
+    depends_on: SwiftStorageHieraDeploy
+    properties:
+        server: {get_resource: SwiftStorage}
+
 outputs:
   ip_address:
     description: IP address of the server in the ctlplane network
@@ -502,6 +508,37 @@ outputs:
           MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
           CTLPLANEIP: {get_attr: [SwiftStorage, networks, ctlplane, 0]}
           CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+  known_hosts_entry:
+    description: Entry for ssh known hosts
+    value:
+      str_replace:
+        template: "PRIMARYIP,PRIMARYHOST.DOMAIN,PRIMARYHOST,\
+EXTERNALIP,EXTERNALHOST.DOMAIN,EXTERNALHOST,\
+INTERNAL_APIIP,INTERNAL_APIHOST.DOMAIN,INTERNAL_APIHOST,\
+STORAGEIP,STORAGEHOST.DOMAIN,STORAGEHOST,\
+STORAGE_MGMTIP,STORAGE_MGMTHOST.DOMAIN,STORAGE_MGMTHOST,\
+TENANTIP,TENANTHOST.DOMAIN,TENANTHOST,\
+MANAGEMENTIP,MANAGEMENTHOST.DOMAIN,MANAGEMENTHOST,\
+CTLPLANEIP,CTLPLANEHOST.DOMAIN,CTLPLANEHOST HOSTSSHPUBKEY"
+        params:
+          PRIMARYIP: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, ObjectStorageHostnameResolveNetwork]}]}
+          DOMAIN: {get_param: CloudDomain}
+          PRIMARYHOST: {get_attr: [SwiftStorage, name]}
+          EXTERNALIP: {get_attr: [ExternalPort, ip_address]}
+          EXTERNALHOST: {get_attr: [NetHostMap, value, external, short]}
+          INTERNAL_APIIP: {get_attr: [InternalApiPort, ip_address]}
+          INTERNAL_APIHOST: {get_attr: [NetHostMap, value, internal_api, short]}
+          STORAGEIP: {get_attr: [StoragePort, ip_address]}
+          STORAGEHOST: {get_attr: [NetHostMap, value, storage, short]}
+          STORAGE_MGMTIP: {get_attr: [StorageMgmtPort, ip_address]}
+          STORAGE_MGMTHOST: {get_attr: [NetHostMap, value, storage_mgmt, short]}
+          TENANTIP: {get_attr: [TenantPort, ip_address]}
+          TENANTHOST: {get_attr: [NetHostMap, value, tenant, short]}
+          MANAGEMENTIP: {get_attr: [ManagementPort, ip_address]}
+          MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
+          CTLPLANEIP: {get_attr: [SwiftStorage, networks, ctlplane, 0]}
+          CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+          HOSTSSHPUBKEY: {get_attr: [SshHostPubKey, ecdsa]}
   nova_server_resource:
     description: Heat resource handle for the swift storage server
     value:
index 1f68f41..8f1f314 100644 (file)
@@ -489,6 +489,12 @@ resources:
         update_identifier:
           get_param: UpdateIdentifier
 
+  SshHostPubKey:
+    type: OS::TripleO::Ssh::HostPubKey
+    depends_on: {{role}}Deployment
+    properties:
+        server: {get_resource: {{role}}}
+
 outputs:
   ip_address:
     description: IP address of the server in the ctlplane network
@@ -536,6 +542,37 @@ outputs:
           MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
           CTLPLANEIP: {get_attr: [{{role}}, networks, ctlplane, 0]}
           CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+  known_hosts_entry:
+    description: Entry for ssh known hosts
+    value:
+      str_replace:
+        template: "PRIMARYIP,PRIMARYHOST.DOMAIN,PRIMARYHOST,\
+EXTERNALIP,EXTERNALHOST.DOMAIN,EXTERNALHOST,\
+INTERNAL_APIIP,INTERNAL_APIHOST.DOMAIN,INTERNAL_APIHOST,\
+STORAGEIP,STORAGEHOST.DOMAIN,STORAGEHOST,\
+STORAGE_MGMTIP,STORAGE_MGMTHOST.DOMAIN,STORAGE_MGMTHOST,\
+TENANTIP,TENANTHOST.DOMAIN,TENANTHOST,\
+MANAGEMENTIP,MANAGEMENTHOST.DOMAIN,MANAGEMENTHOST,\
+CTLPLANEIP,CTLPLANEHOST.DOMAIN,CTLPLANEHOST HOSTSSHPUBKEY"
+        params:
+          PRIMARYIP: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, {{role}}HostnameResolveNetwork]}]}
+          DOMAIN: {get_param: CloudDomain}
+          PRIMARYHOST: {get_attr: [{{role}}, name]}
+          EXTERNALIP: {get_attr: [ExternalPort, ip_address]}
+          EXTERNALHOST: {get_attr: [NetHostMap, value, external, short]}
+          INTERNAL_APIIP: {get_attr: [InternalApiPort, ip_address]}
+          INTERNAL_APIHOST: {get_attr: [NetHostMap, value, internal_api, short]}
+          STORAGEIP: {get_attr: [StoragePort, ip_address]}
+          STORAGEHOST: {get_attr: [NetHostMap, value, storage, short]}
+          STORAGE_MGMTIP: {get_attr: [StorageMgmtPort, ip_address]}
+          STORAGE_MGMTHOST: {get_attr: [NetHostMap, value, storage_mgmt, short]}
+          TENANTIP: {get_attr: [TenantPort, ip_address]}
+          TENANTHOST: {get_attr: [NetHostMap, value, tenant, short]}
+          MANAGEMENTIP: {get_attr: [ManagementPort, ip_address]}
+          MANAGEMENTHOST: {get_attr: [NetHostMap, value, management, short]}
+          CTLPLANEIP: {get_attr: [{{role}}, networks, ctlplane, 0]}
+          CTLPLANEHOST: {get_attr: [NetHostMap, value, ctlplane, short]}
+          HOSTSSHPUBKEY: {get_attr: [SshHostPubKey, ecdsa]}
   nova_server_resource:
     description: Heat resource handle for {{role}} server
     value:
diff --git a/releasenotes/notes/ssh_known_hosts-287563590632d1aa.yaml b/releasenotes/notes/ssh_known_hosts-287563590632d1aa.yaml
new file mode 100644 (file)
index 0000000..8b533b1
--- /dev/null
@@ -0,0 +1,4 @@
+---
+features:
+  - SSH host key exchange. The ssh host keys are collected from each host,
+    combined, and written to /etc/ssh/ssh_known_hosts.