Add TLS protection on external API endpoints

TLS can be used to encrypt and authenticate the connection with
OpenStack endpoints.  This patch provides the necessary
parameters and changes the resulting service configurations to
enable TLS for the Kolla deployed OpenStack cloud.

The new input parameters are:

kolla_enable_tls_external: "yes" or "no" (default is "no")
kolla_external_fqdn_cert: "/etc/kolla/certificates/haproxy.pem"
kolla_external_fqdn_cacert: "/etc/kolla/certificates/haproxy-ca.crt"

Implements: blueprint kolla-ssl

Change-Id: I48ef8a781c3035d58817f9bf6f36d59a488bab41
This commit is contained in:
Dave McCowan 2016-02-29 13:51:11 -05:00
parent a94d537aab
commit 3daded6242
11 changed files with 117 additions and 25 deletions

View File

@ -136,7 +136,7 @@ elasticsearch_port: "9200"
manila_api_port: "8786"
public_protocol: "http"
public_protocol: "{{ 'https' if kolla_enable_tls_external | bool else 'http' }}"
internal_protocol: "http"
admin_protocol: "http"
@ -207,7 +207,9 @@ rabbitmq_user: "openstack"
####################
haproxy_user: "openstack"
haproxy_enable_external_vip: "{{ 'no' if kolla_external_vip_address == kolla_internal_vip_address else 'yes' }}"
kolla_enable_tls_external: "no"
kolla_external_fqdn_cert: "{{ node_config_directory }}/certificates/haproxy.pem"
kolla_external_fqdn_cacert: "{{ node_config_directory }}/certificates/haproxy-ca.crt"
#################################
# Cinder - Block Storage options

View File

@ -21,7 +21,7 @@
- name: Creating Server Certificate
command: creates="{{ item }}" openssl req -new -nodes -sha256 -x509 \
-subj "/C=US/ST=NC/L=RTP/O=kolla/CN={{ kolla_external_address }}" \
-subj "/C=US/ST=NC/L=RTP/O=kolla/CN={{ kolla_external_fqdn }}" \
-config {{ node_config_directory }}/certificates/openssl-kolla.cnf \
-days 3650 \
-extensions v3_req \

View File

@ -7,7 +7,7 @@ countryName = US
stateOrProvinceName = NC
localityName = RTP
organizationalUnitName = kolla
commonName = {{ kolla_external_address }}
commonName = {{ kolla_external_fqdn }}
[v3_req]
subjectAltName = @alt_names

View File

@ -33,3 +33,11 @@
dest: "{{ node_config_directory }}/{{ item }}/{{ item }}.conf"
with_items:
- "keepalived"
- name: Copying over haproxy.pem
when: kolla_enable_tls_external | bool
copy:
src: "{{ kolla_external_fqdn_cert }}"
dest: "{{ node_config_directory }}/haproxy/{{ item }}"
with_items:
- "haproxy.pem"

View File

@ -1,8 +1,14 @@
{% set tls_bind_info = 'ssl crt /etc/haproxy/haproxy.pem' if kolla_enable_tls_external | bool else '' %}
global
daemon
log /var/lib/kolla/heka/log local0
maxconn 4000
stats socket /var/lib/kolla/haproxy/haproxy.sock
{% if kolla_enable_tls_external | bool %}
ssl-default-bind-ciphers DEFAULT:!MEDIUM:!3DES
ssl-default-bind-options no-sslv3 no-tlsv10
tune.ssl.default-dh-param 4096
{% endif %}
defaults
log global
@ -58,13 +64,16 @@ listen mongodb
{% if enable_keystone | bool %}
listen keystone_internal
bind {{ kolla_internal_vip_address }}:{{ keystone_public_port }}
http-request del-header X-Forwarded-Proto
{% for host in groups['keystone'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ keystone_public_port }} check inter 2000 rise 2 fall 5
{% endfor %}
{% if haproxy_enable_external_vip | bool %}
listen keystone_external
bind {{ kolla_external_vip_address }}:{{ keystone_public_port }}
bind {{ kolla_external_vip_address }}:{{ keystone_public_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['keystone'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ keystone_public_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -72,6 +81,7 @@ listen keystone_external
listen keystone_admin
bind {{ kolla_internal_vip_address }}:{{ keystone_admin_port }}
http-request del-header X-Forwarded-Proto
{% for host in groups['keystone'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ keystone_admin_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -92,13 +102,13 @@ listen glance_api
{% if haproxy_enable_external_vip | bool %}
listen glance_registry_external
bind {{ kolla_external_vip_address }}:{{ glance_registry_port }}
bind {{ kolla_external_vip_address }}:{{ glance_registry_port }} {{ tls_bind_info }}
{% for host in groups['glance-registry'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ glance_registry_port }} check inter 2000 rise 2 fall 5
{% endfor %}
listen glance_api_external
bind {{ kolla_external_vip_address }}:{{ glance_api_port }}
bind {{ kolla_external_vip_address }}:{{ glance_api_port }} {{ tls_bind_info }}
{% for host in groups['glance-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ glance_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -108,18 +118,21 @@ listen glance_api_external
{% if enable_nova | bool %}
listen nova_api
bind {{ kolla_internal_vip_address }}:{{ nova_api_port }}
http-request del-header X-Forwarded-Proto
{% for host in groups['nova-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
listen nova_api_ec2
bind {{ kolla_internal_vip_address }}:{{ nova_api_ec2_port }}
http-request del-header X-Forwarded-Proto
{% for host in groups['nova-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_api_ec2_port }} check inter 2000 rise 2 fall 5
{% endfor %}
listen nova_metadata
bind {{ kolla_internal_vip_address }}:{{ nova_metadata_port }}
http-request del-header X-Forwarded-Proto
{% for host in groups['nova-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_metadata_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -127,6 +140,8 @@ listen nova_metadata
{% if nova_console == 'novnc' %}
listen nova_novncproxy
bind {{ kolla_internal_vip_address }}:{{ nova_novncproxy_port }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['nova-novncproxy'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_novncproxy_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -140,32 +155,42 @@ listen nova_spicehtml5proxy
{% if haproxy_enable_external_vip | bool %}
listen nova_api_external
bind {{ kolla_external_vip_address }}:{{ nova_api_port }}
bind {{ kolla_external_vip_address }}:{{ nova_api_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['nova-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
listen nova_api_ec2_external
bind {{ kolla_external_vip_address }}:{{ nova_api_ec2_port }}
bind {{ kolla_external_vip_address }}:{{ nova_api_ec2_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['nova-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_api_ec2_port }} check inter 2000 rise 2 fall 5
{% endfor %}
listen nova_metadata_external
bind {{ kolla_external_vip_address }}:{{ nova_metadata_port }}
bind {{ kolla_external_vip_address }}:{{ nova_metadata_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['nova-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_metadata_port }} check inter 2000 rise 2 fall 5
{% endfor %}
{% if nova_console == 'novnc' %}
listen nova_novncproxy_external
bind {{ kolla_external_vip_address }}:{{ nova_novncproxy_port }}
bind {{ kolla_external_vip_address }}:{{ nova_novncproxy_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['nova-novncproxy'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_novncproxy_port }} check inter 2000 rise 2 fall 5
{% endfor %}
{% elif nova_console == 'spice' %}
listen nova_spicehtml5proxy_external
bind {{ kolla_external_vip_address }}:{{ nova_spicehtml5proxy_port }}
bind {{ kolla_external_vip_address }}:{{ nova_spicehtml5proxy_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['nova-spicehtml5proxy'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ nova_spicehtml5proxy_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -182,7 +207,7 @@ listen neutron_server
{% if haproxy_enable_external_vip | bool %}
listen neutron_server_external
bind {{ kolla_external_vip_address }}:{{ neutron_server_port }}
bind {{ kolla_external_vip_address }}:{{ neutron_server_port }} {{ tls_bind_info }}
{% for host in groups['neutron-server'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ neutron_server_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -192,11 +217,24 @@ listen neutron_server_external
{% if enable_horizon | bool %}
listen horizon
bind {{ kolla_internal_vip_address }}:80
http-request del-header X-Forwarded-Proto
{% for host in groups['horizon'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:80 check inter 2000 rise 2 fall 5
{% endfor %}
{% if haproxy_enable_external_vip | bool %}
{% if haproxy_enable_external_vip | bool %}
{% if kolla_enable_tls_external | bool %}
listen horizon_external
bind {{ kolla_external_vip_address }}:443 {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['horizon'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:80 check inter 2000 rise 2 fall 5
{% endfor %}
frontend horizon_external_redirect {{ kolla_external_vip_address }}:80
redirect scheme https code 301 if !{ ssl_fc }
{% else %}
listen horizon_external
bind {{ kolla_external_vip_address }}:80
{% for host in groups['horizon'] %}
@ -204,17 +242,21 @@ listen horizon_external
{% endfor %}
{% endif %}
{% endif %}
{% endif %}
{% if enable_cinder | bool %}
listen cinder_api
bind {{ kolla_internal_vip_address }}:{{ cinder_api_port }}
http-request del-header X-Forwarded-Proto
{% for host in groups['cinder-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ cinder_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
{% if haproxy_enable_external_vip | bool %}
listen cinder_api_external
bind {{ kolla_external_vip_address }}:{{ cinder_api_port }}
bind {{ kolla_external_vip_address }}:{{ cinder_api_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['cinder-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ cinder_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -224,25 +266,31 @@ listen cinder_api_external
{% if enable_heat | bool %}
listen heat_api
bind {{ kolla_internal_vip_address }}:{{ heat_api_port }}
http-request del-header X-Forwarded-Proto
{% for host in groups['heat-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ heat_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
listen heat_api_cfn
bind {{ kolla_internal_vip_address }}:{{ heat_api_cfn_port }}
http-request del-header X-Forwarded-Proto
{% for host in groups['heat-api-cfn'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ heat_api_cfn_port }} check inter 2000 rise 2 fall 5
{% endfor %}
{% if haproxy_enable_external_vip | bool %}
listen heat_api_external
bind {{ kolla_external_vip_address }}:{{ heat_api_port }}
bind {{ kolla_external_vip_address }}:{{ heat_api_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['heat-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ heat_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
listen heat_api_cfn_external
bind {{ kolla_external_vip_address }}:{{ heat_api_cfn_port }}
bind {{ kolla_external_vip_address }}:{{ heat_api_cfn_port }} {{ tls_bind_info }}
http-request del-header X-Forwarded-Proto
http-request set-header X-Forwarded-Proto https if { ssl_fc }
{% for host in groups['heat-api-cfn'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ heat_api_cfn_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -258,7 +306,7 @@ listen ironic_api
{% if haproxy_enable_external_vip | bool %}
listen ironic_api_external
bind {{ kolla_external_vip_address }}:{{ ironic_api_port }}
bind {{ kolla_external_vip_address }}:{{ ironic_api_port }} {{ tls_bind_info }}
{% for host in groups['ironic-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ ironic_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -274,7 +322,7 @@ listen swift_api
{% if haproxy_enable_external_vip | bool %}
listen swift_api_external
bind {{ kolla_external_vip_address }}:{{ swift_proxy_server_port }}
bind {{ kolla_external_vip_address }}:{{ swift_proxy_server_port }} {{ tls_bind_info }}
{% for host in groups['swift-proxy-server'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ swift_proxy_server_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -290,7 +338,7 @@ listen murano_api
{% if haproxy_enable_external_vip | bool %}
listen murano_api_external
bind {{ kolla_external_vip_address }}:{{ murano_api_port }}
bind {{ kolla_external_vip_address }}:{{ murano_api_port }} {{ tls_bind_info }}
{% for host in groups['murano-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ murano_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -306,7 +354,7 @@ listen magnum_api
{% if haproxy_enable_external_vip | bool %}
listen magnum_api_external
bind {{ kolla_external_vip_address }}:{{ magnum_api_port }}
bind {{ kolla_external_vip_address }}:{{ magnum_api_port }} {{ tls_bind_info }}
{% for host in groups['magnum-api'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ magnum_api_port }} check inter 2000 rise 2 fall 5
{% endfor %}
@ -322,7 +370,7 @@ listen radosgw
{% if haproxy_enable_external_vip | bool %}
listen radosgw_external
bind {{ kolla_external_vip_address }}:{{ rgw_port }}
bind {{ kolla_external_vip_address }}:{{ rgw_port }} {{ tls_bind_info }}
{% for host in groups['ceph-rgw'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ rgw_port }} check inter 2000 rise 2 fall 5
{% endfor %}

View File

@ -6,6 +6,13 @@
"dest": "/etc/haproxy/haproxy.cfg",
"owner": "root",
"perm": "0644"
},
{
"source": "{{ container_config_directory }}/haproxy.pem",
"dest": "/etc/haproxy/haproxy.pem",
"owner": "root",
"perm": "0600",
"optional": "true"
}
]
}

View File

@ -22,3 +22,7 @@ Listen {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['addr
SetHandler None
</Location>
</Virtualhost>
{% if kolla_enable_tls_external | bool %}
Header edit Location ^http://(.*)$ https://$1
{% endif %}

View File

@ -41,6 +41,12 @@ ALLOWED_HOSTS = ['*']
#CSRF_COOKIE_SECURE = True
#SESSION_COOKIE_SECURE = True
{% if kolla_enable_tls_external | bool %}
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
{% endif %}
# Overrides for OpenStack API versions. Use this setting to force the
# OpenStack dashboard to use a specific API version for a given service API.
# Versions specified here should be integers or floats, not strings.
@ -147,8 +153,8 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# ('http://cluster2.example.com:5000/v2.0', 'cluster2'),
#]
OPENSTACK_HOST = "{{ kolla_external_fqdn }}"
OPENSTACK_KEYSTONE_URL = "{{ public_protocol }}://%s:{{ keystone_public_port }}/v3" % OPENSTACK_HOST
OPENSTACK_HOST = "{{ kolla_internal_fqdn }}"
OPENSTACK_KEYSTONE_URL = "{{ internal_protocol }}://%s:{{ keystone_public_port }}/v3" % OPENSTACK_HOST
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "_member_"
# Enables keystone web single-sign-on if set to True.
@ -292,7 +298,7 @@ IMAGE_RESERVED_CUSTOM_PROPERTIES = []
# OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints
# in the Keystone service catalog. Use this setting when Horizon is running
# external to the OpenStack environment. The default is 'publicURL'.
#OPENSTACK_ENDPOINT_TYPE = "publicURL"
OPENSTACK_ENDPOINT_TYPE = "internalURL"
# SECONDARY_ENDPOINT_TYPE specifies the fallback endpoint type to use in the
# case that OPENSTACK_ENDPOINT_TYPE is not present in the endpoints

View File

@ -4,5 +4,7 @@ debug = {{ keystone_logging_debug }}
# NOTE(elemoine) log_dir alone does not work for Keystone
log_file = /var/log/kolla/keystone/keystone.log
secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
[database]
connection = mysql+pymysql://{{ keystone_database_user }}:{{ keystone_database_password }}@{{ keystone_database_address }}/{{ keystone_database_name }}

View File

@ -8,6 +8,10 @@ use_forwarded_for = true
api_paste_config = /etc/nova/api-paste.ini
state_path = /var/lib/nova
{% if kolla_enable_tls_external | bool %}
secure_proxy_ssl_header = X-Forwarded-Proto
{% endif %}
osapi_compute_listen = {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}
osapi_compute_listen_port = {{ nova_api_port }}

View File

@ -72,6 +72,17 @@ neutron_external_interface: "eth1"
#neutron_plugin_agent: "openvswitch"
####################
# TLS options
####################
# To provide encryption and authentication on the kolla_external_vip_interface,
# TLS can be enabled. When TLS is enabled, certificates must be provided to
# allow clients to perform authentication. The default is TLS disabled.
# kolla_enable_tls_external: "yes"
# kolla_external_fqdn_cert: "{{ node_config_directory }}/certificates/haproxy.pem"
# kolla_external_fqdn_cacert: "{{ node_config_directory }}/certificates/haproxy-ca.crt"
####################
# OpenStack options
####################