From 9bf37e06b53a1f621eb4fee314a57d2d4a17c644 Mon Sep 17 00:00:00 2001
From: Juan Antonio Osorio Robles <jaosorior@redhat.com>
Date: Wed, 17 Aug 2016 12:24:23 +0000
Subject: [PATCH] Add HAProxy TLS handled by certmonger as composable service

This adds some basic pieces to get certmonger to manage the
certificates for HAProxy. The aim is to be flexible enough that we
will be able to manage both public and internal certificates.

This also adds a relevant environment to get the endpoints to have
TLS everywhere.

bp tls-via-certmonger

Depends-On: I89001ae32f46c9682aecc118753ef6cd647baa62
Change-Id: Ife5f8c2f07233295bc15b4c605acf3d9bd62f162
---
 .../haproxy-internal-tls-certmonger.yaml      |  4 ++
 .../haproxy-public-tls-certmonger.yaml        |  4 ++
 .../tls-everywhere-endpoints-dns.yaml         | 52 +++++++++++++++++++
 overcloud-resource-registry-puppet.j2.yaml    |  2 +
 .../haproxy-internal-tls-certmonger.yaml      | 51 ++++++++++++++++++
 .../haproxy-public-tls-certmonger.yaml        | 37 +++++++++++++
 puppet/services/haproxy.yaml                  | 43 +++++++++++----
 7 files changed, 183 insertions(+), 10 deletions(-)
 create mode 100644 environments/services/haproxy-internal-tls-certmonger.yaml
 create mode 100644 environments/services/haproxy-public-tls-certmonger.yaml
 create mode 100644 environments/tls-everywhere-endpoints-dns.yaml
 create mode 100644 puppet/services/haproxy-internal-tls-certmonger.yaml
 create mode 100644 puppet/services/haproxy-public-tls-certmonger.yaml

diff --git a/environments/services/haproxy-internal-tls-certmonger.yaml b/environments/services/haproxy-internal-tls-certmonger.yaml
new file mode 100644
index 0000000000..074fec4d4e
--- /dev/null
+++ b/environments/services/haproxy-internal-tls-certmonger.yaml
@@ -0,0 +1,4 @@
+# A Heat environment file which can be used to enable a
+# a TLS for HAProxy via certmonger
+resource_registry:
+  OS::TripleO::Services::HAProxyInternalTLS: ../../puppet/services/haproxy-internal-tls-certmonger.yaml
diff --git a/environments/services/haproxy-public-tls-certmonger.yaml b/environments/services/haproxy-public-tls-certmonger.yaml
new file mode 100644
index 0000000000..d3ad3ad427
--- /dev/null
+++ b/environments/services/haproxy-public-tls-certmonger.yaml
@@ -0,0 +1,4 @@
+# A Heat environment file which can be used to enable a
+# a TLS for HAProxy via certmonger
+resource_registry:
+  OS::TripleO::Services::HAProxyPublicTLS: ../../puppet/services/haproxy-public-tls-certmonger.yaml
diff --git a/environments/tls-everywhere-endpoints-dns.yaml b/environments/tls-everywhere-endpoints-dns.yaml
new file mode 100644
index 0000000000..88a108a60a
--- /dev/null
+++ b/environments/tls-everywhere-endpoints-dns.yaml
@@ -0,0 +1,52 @@
+# Use this environment when deploying an overcloud where all the endpoints are
+# DNS names and there's TLS in all endpoint types.
+parameter_defaults:
+  EndpointMap:
+    AodhAdmin: {protocol: 'https', port: '8042', host: 'CLOUDNAME'}
+    AodhInternal: {protocol: 'https', port: '8042', host: 'CLOUDNAME'}
+    AodhPublic: {protocol: 'https', port: '13042', host: 'CLOUDNAME'}
+    CeilometerAdmin: {protocol: 'https', port: '8777', host: 'CLOUDNAME'}
+    CeilometerInternal: {protocol: 'https', port: '8777', host: 'CLOUDNAME'}
+    CeilometerPublic: {protocol: 'https', port: '13777', host: 'CLOUDNAME'}
+    CinderAdmin: {protocol: 'https', port: '8776', host: 'CLOUDNAME'}
+    CinderInternal: {protocol: 'https', port: '8776', host: 'CLOUDNAME'}
+    CinderPublic: {protocol: 'https', port: '13776', host: 'CLOUDNAME'}
+    GlanceAdmin: {protocol: 'https', port: '9292', host: 'CLOUDNAME'}
+    GlanceInternal: {protocol: 'https', port: '9292', host: 'CLOUDNAME'}
+    GlancePublic: {protocol: 'https', port: '13292', host: 'CLOUDNAME'}
+    GlanceRegistryInternal: {protocol: 'https', port: '9191', host: 'CLOUDNAME'}
+    GnocchiAdmin: {protocol: 'https', port: '8041', host: 'CLOUDNAME'}
+    GnocchiInternal: {protocol: 'https', port: '8041', host: 'CLOUDNAME'}
+    GnocchiPublic: {protocol: 'https', port: '13041', host: 'CLOUDNAME'}
+    HeatAdmin: {protocol: 'https', port: '8004', host: 'CLOUDNAME'}
+    HeatInternal: {protocol: 'https', port: '8004', host: 'CLOUDNAME'}
+    HeatPublic: {protocol: 'https', port: '13004', host: 'CLOUDNAME'}
+    HeatCfnAdmin: {protocol: 'https', port: '8000', host: 'CLOUDNAME'}
+    HeatCfnInternal: {protocol: 'https', port: '8000', host: 'CLOUDNAME'}
+    HeatCfnPublic: {protocol: 'https', port: '13005', host: 'CLOUDNAME'}
+    HorizonPublic: {protocol: 'https', port: '443', host: 'CLOUDNAME'}
+    IronicAdmin: {protocol: 'https', port: '6385', host: 'CLOUDNAME'}
+    IronicInternal: {protocol: 'https', port: '6385', host: 'CLOUDNAME'}
+    IronicPublic: {protocol: 'https', port: '13385', host: 'CLOUDNAME'}
+    KeystoneAdmin: {protocol: 'https', port: '35357', host: 'CLOUDNAME'}
+    KeystoneInternal: {protocol: 'https', port: '5000', host: 'CLOUDNAME'}
+    KeystonePublic: {protocol: 'https', port: '13000', host: 'CLOUDNAME'}
+    ManilaAdmin: {protocol: 'https', port: '8786', host: 'CLOUDNAME'}
+    ManilaInternal: {protocol: 'https', port: '8786', host: 'CLOUDNAME'}
+    ManilaPublic: {protocol: 'https', port: '13786', host: 'CLOUDNAME'}
+    MysqlInternal: {protocol: 'mysql+pymysql', port: '3306', host: 'CLOUDNAME'}
+    NeutronAdmin: {protocol: 'https', port: '9696', host: 'CLOUDNAME'}
+    NeutronInternal: {protocol: 'https', port: '9696', host: 'CLOUDNAME'}
+    NeutronPublic: {protocol: 'https', port: '13696', host: 'CLOUDNAME'}
+    NovaAdmin: {protocol: 'https', port: '8774', host: 'CLOUDNAME'}
+    NovaInternal: {protocol: 'https', port: '8774', host: 'CLOUDNAME'}
+    NovaPublic: {protocol: 'https', port: '13774', host: 'CLOUDNAME'}
+    NovaVNCProxyAdmin: {protocol: 'https', port: '6080', host: 'CLOUDNAME'}
+    NovaVNCProxyInternal: {protocol: 'https', port: '6080', host: 'CLOUDNAME'}
+    NovaVNCProxyPublic: {protocol: 'https', port: '13080', host: 'CLOUDNAME'}
+    SaharaAdmin: {protocol: 'https', port: '8386', host: 'CLOUDNAME'}
+    SaharaInternal: {protocol: 'https', port: '8386', host: 'CLOUDNAME'}
+    SaharaPublic: {protocol: 'https', port: '13386', host: 'CLOUDNAME'}
+    SwiftAdmin: {protocol: 'https', port: '8080', host: 'CLOUDNAME'}
+    SwiftInternal: {protocol: 'https', port: '8080', host: 'CLOUDNAME'}
+    SwiftPublic: {protocol: 'https', port: '13808', host: 'CLOUDNAME'}
diff --git a/overcloud-resource-registry-puppet.j2.yaml b/overcloud-resource-registry-puppet.j2.yaml
index a7185a432a..5f09f52284 100644
--- a/overcloud-resource-registry-puppet.j2.yaml
+++ b/overcloud-resource-registry-puppet.j2.yaml
@@ -142,6 +142,8 @@ resource_registry:
   OS::TripleO::Services::NeutronSriovAgent: OS::Heat::None
   OS::TripleO::Services::RabbitMQ: puppet/services/rabbitmq.yaml
   OS::TripleO::Services::HAproxy: puppet/services/haproxy.yaml
+  OS::TripleO::Services::HAProxyPublicTLS: OS::Heat::None
+  OS::TripleO::Services::HAProxyInternalTLS: OS::Heat::None
   OS::TripleO::Services::Keepalived: puppet/services/keepalived.yaml
   OS::TripleO::Services::Memcached: puppet/services/memcached.yaml
   OS::TripleO::Services::SaharaApi: OS::Heat::None
diff --git a/puppet/services/haproxy-internal-tls-certmonger.yaml b/puppet/services/haproxy-internal-tls-certmonger.yaml
new file mode 100644
index 0000000000..c6d53542db
--- /dev/null
+++ b/puppet/services/haproxy-internal-tls-certmonger.yaml
@@ -0,0 +1,51 @@
+heat_template_version: 2016-10-14
+
+description: >
+  HAProxy deployment with TLS enabled, powered by certmonger
+
+parameters:
+  ServiceNetMap:
+    default: {}
+    description: Mapping of service_name -> network name. Typically set
+                 via parameter_defaults in the resource registry.  This
+                 mapping overrides those in ServiceNetMapDefaults.
+    type: json
+  DefaultPasswords:
+    default: {}
+    type: json
+  EndpointMap:
+    default: {}
+    description: Mapping of service endpoint -> protocol. Typically set
+                 via parameter_defaults in the resource registry.
+    type: json
+
+outputs:
+  role_data:
+    description: Role data for the HAProxy internal TLS via certmonger role.
+    value:
+      service_name: haproxy_internal_tls_certmonger
+      config_settings:
+        generate_service_certificates: true
+        tripleo::haproxy::use_internal_certificates: true
+      certificates_specs:
+        map_merge:
+          repeat:
+            template:
+              haproxy-NETWORK:
+                service_pem: '/etc/pki/tls/certs/overcloud-haproxy-NETWORK.pem'
+                service_certificate: '/etc/pki/tls/certs/overcloud-haproxy-NETWORK.crt'
+                service_key: '/etc/pki/tls/private/overcloud-haproxy-NETWORK.key'
+                hostname: "%{hiera('cloud_name_NETWORK')}"
+                postsave_cmd: "" # TODO
+                principal: "haproxy/%{hiera('cloud_name_NETWORK')}"
+            for_each:
+              NETWORK:
+                # NOTE(jaosorior) Get unique network names to create
+                # certificates for those. We skip the tenant network since
+                # we don't need a certificate for that, and the external
+                # network will be handled in another template.
+                yaql:
+                  expression: list($.data.map.items().map($1[1])).distinct().where($ != external and $ != tenant)
+                  data:
+                    map:
+                      get_param: ServiceNetMap
diff --git a/puppet/services/haproxy-public-tls-certmonger.yaml b/puppet/services/haproxy-public-tls-certmonger.yaml
new file mode 100644
index 0000000000..1551d16a0c
--- /dev/null
+++ b/puppet/services/haproxy-public-tls-certmonger.yaml
@@ -0,0 +1,37 @@
+heat_template_version: 2016-10-14
+
+description: >
+  HAProxy deployment with TLS enabled, powered by certmonger
+
+parameters:
+  ServiceNetMap:
+    default: {}
+    description: Mapping of service_name -> network name. Typically set
+                 via parameter_defaults in the resource registry.  This
+                 mapping overrides those in ServiceNetMapDefaults.
+    type: json
+  DefaultPasswords:
+    default: {}
+    type: json
+  EndpointMap:
+    default: {}
+    description: Mapping of service endpoint -> protocol. Typically set
+                 via parameter_defaults in the resource registry.
+    type: json
+
+outputs:
+  role_data:
+    description: Role data for the HAProxy public TLS via certmonger role.
+    value:
+      service_name: haproxy_public_tls_certmonger
+      config_settings:
+        generate_service_certificates: true
+        tripleo::haproxy::service_certificate: '/etc/pki/tls/certs/overcloud-haproxy-external.pem'
+      certificates_specs:
+        haproxy-external:
+          service_pem: '/etc/pki/tls/certs/overcloud-haproxy-external.pem'
+          service_certificate: '/etc/pki/tls/certs/overcloud-haproxy-external.crt'
+          service_key: '/etc/pki/tls/private/overcloud-haproxy-external.key'
+          hostname: "%{hiera('cloud_name_external')}"
+          postsave_cmd: "" # TODO
+          principal: "haproxy/%{hiera('cloud_name_external')}"
diff --git a/puppet/services/haproxy.yaml b/puppet/services/haproxy.yaml
index 974928c5bf..0813cb7e01 100644
--- a/puppet/services/haproxy.yaml
+++ b/puppet/services/haproxy.yaml
@@ -1,4 +1,4 @@
-heat_template_version: 2016-04-08
+heat_template_version: 2016-10-14
 
 description: >
   HAproxy service configured with Puppet
@@ -48,6 +48,22 @@ parameters:
     default: 'overcloud-haproxy'
     type: string
 
+resources:
+
+  HAProxyPublicTLS:
+    type: OS::TripleO::Services::HAProxyPublicTLS
+    properties:
+      ServiceNetMap: {get_param: ServiceNetMap}
+      DefaultPasswords: {get_param: DefaultPasswords}
+      EndpointMap: {get_param: EndpointMap}
+
+  HAProxyInternalTLS:
+    type: OS::TripleO::Services::HAProxyInternalTLS
+    properties:
+      ServiceNetMap: {get_param: ServiceNetMap}
+      DefaultPasswords: {get_param: DefaultPasswords}
+      EndpointMap: {get_param: EndpointMap}
+
 outputs:
   role_data:
     description: Role data for the HAproxy role.
@@ -55,14 +71,21 @@ outputs:
       service_name: haproxy
       monitoring_subscription: {get_param: MonitoringSubscriptionHaproxy}
       config_settings:
-        tripleo.haproxy.firewall_rules:
-          '107 haproxy stats':
-            dport: 1993
-        tripleo::haproxy::haproxy_log_address: {get_param: HAProxySyslogAddress}
-        tripleo::haproxy::haproxy_stats_user: {get_param: HAProxyStatsUser}
-        tripleo::haproxy::haproxy_stats_password: {get_param: HAProxyStatsPassword}
-        tripleo::haproxy::redis_password: {get_param: RedisPassword}
-        tripleo::haproxy::control_virtual_interface: {get_param: ControlVirtualInterface}
-        tripleo::haproxy::public_virtual_interface: {get_param: PublicVirtualInterface}
+        map_merge:
+          - get_attr: [HAProxyPublicTLS, role_data, config_settings]
+          - get_attr: [HAProxyInternalTLS, role_data, config_settings]
+          - tripleo.haproxy.firewall_rules:
+              '107 haproxy stats':
+                dport: 1993
+            tripleo::haproxy::haproxy_log_address: {get_param: HAProxySyslogAddress}
+            tripleo::haproxy::haproxy_stats_user: {get_param: HAProxyStatsUser}
+            tripleo::haproxy::haproxy_stats_password: {get_param: HAProxyStatsPassword}
+            tripleo::haproxy::redis_password: {get_param: RedisPassword}
+            tripleo::haproxy::control_virtual_interface: {get_param: ControlVirtualInterface}
+            tripleo::haproxy::public_virtual_interface: {get_param: PublicVirtualInterface}
+            tripleo::profile::base::haproxy::certificates_specs:
+              map_merge:
+                - get_attr: [HAProxyPublicTLS, role_data, certificates_specs]
+                - get_attr: [HAProxyInternalTLS, role_data, certificates_specs]
       step_config: |
         include ::tripleo::profile::base::haproxy