Merge pull request #510 from enovance/enable_ssl_haproxy

loadbalancer: Terminate SSL in the HAProxy loadbalancer.
This commit is contained in:
Emilien Macchi 2014-06-17 14:57:48 +02:00
commit 39adda51d7
7 changed files with 220 additions and 113 deletions

View File

@ -47,7 +47,7 @@ fixtures:
ref: '474ad6d2b84f3d1e03000c2fc340ec1e3408488d'
'haproxy':
repo: 'git://github.com/enovance/puppetlabs-haproxy.git'
ref: 'ff713f85d8fac7ade808f3d65d949a1a06b8ea88'
ref: 'fc1166f28d411dfd4f59d4bfd6936595c014a11b'
'keepalived':
repo: 'git://github.com/enovance/puppet-module-keepalived.git'
ref: 'eb345b6d3b25106cbe166028f2b8dd9974a10230'

View File

@ -79,7 +79,7 @@ mod 'fluentd',
:ref => '474ad6d2b84f3d1e03000c2fc340ec1e3408488d'
mod 'haproxy',
:git => 'git://github.com/enovance/puppetlabs-haproxy.git',
:ref => 'ff713f85d8fac7ade808f3d65d949a1a06b8ea88'
:ref => 'fc1166f28d411dfd4f59d4bfd6936595c014a11b'
mod 'inifile',
:git => 'git://github.com/enovance/puppetlabs-inifile.git',
:ref => 'ae23a4db97d2815ec305d0529912685f07746d3c'

View File

@ -172,7 +172,6 @@ class cloud::loadbalancer(
$keystone_api = true,
$keystone_api_admin = true,
$horizon = true,
$horizon_ssl = false,
$spice = true,
$haproxy_auth = 'admin:changeme',
$keepalived_state = 'BACKUP',
@ -181,6 +180,23 @@ class cloud::loadbalancer(
$keepalived_public_ipvs = ['127.0.0.1'],
$keepalived_internal_interface = 'eth1',
$keepalived_internal_ipvs = false,
$ceilometer_bind_options = [],
$cinder_bind_options = [],
$ec2_bind_options = [],
$glance_api_bind_options = [],
$glance_registry_bind_options = [],
$heat_cfn_bind_options = [],
$heat_cloudwatch_bind_options = [],
$heat_api_bind_options = [],
$keystone_bind_options = [],
$keystone_admin_bind_options = [],
$metadata_bind_options = [],
$neutron_bind_options = [],
$nova_bind_options = [],
$swift_bind_options = [],
$spice_bind_options = [],
$horizon_bind_options = [],
$galera_bind_options = [],
$ks_ceilometer_public_port = 8777,
$ks_cinder_public_port = 8776,
$ks_ec2_public_port = 8773,
@ -196,7 +212,6 @@ class cloud::loadbalancer(
$ks_nova_public_port = 8774,
$ks_swift_public_port = 8080,
$horizon_port = 80,
$horizon_ssl_port = 443,
$spice_port = 6082,
$vip_public_ip = ['127.0.0.1'],
$vip_internal_ip = false,
@ -204,6 +219,8 @@ class cloud::loadbalancer(
# Deprecated parameters
$keepalived_interface = false,
$keepalived_ipvs = false,
$horizon_ssl = false,
$horizon_ssl_port = false,
){
# Manage deprecation when using old parameters
@ -219,6 +236,26 @@ class cloud::loadbalancer(
} else {
$keepalived_public_ipvs_real = $keepalived_public_ipvs
}
if $horizon_ssl {
warning('horizon_ssl parameter is deprecated. Specify ssl in the horizon_bind_options instead.')
$horizon_httpchk = 'ssl-hello-chk'
$horizon_options = {
'mode' => 'tcp',
'cookie' => 'sessionid prefix',
'balance' => 'leastconn' }
} else {
$horizon_httpchk = "httpchk GET /${horizon_auth_url} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-${::hostname}\""
$horizon_options = {
'cookie' => 'sessionid prefix',
'balance' => 'leastconn' }
}
if $horizon_ssl_port {
warning('horizon_ssl_port parameter is deprecated. Specify port with the horizon_port instead.')
$horizon_port_real = $horizon_ssl_port
} else {
$horizon_port_real = '443'
}
# end of deprecation support
# Fail if OpenStack and Galera VIP are not in the VIP list
if $vip_public_ip and !($vip_public_ip in $keepalived_public_ipvs_real) {
@ -284,29 +321,35 @@ class cloud::loadbalancer(
# Instanciate HAproxy binding
cloud::loadbalancer::binding { 'keystone_api_cluster':
ip => $keystone_api,
port => $ks_keystone_public_port;
ip => $keystone_api,
port => $ks_keystone_public_port,
bind_options => $keystone_bind_options,
}
cloud::loadbalancer::binding { 'keystone_api_admin_cluster':
ip => $keystone_api_admin,
port => $ks_keystone_admin_port;
ip => $keystone_api_admin,
port => $ks_keystone_admin_port,
bind_options => $keystone_admin_bind_options,
}
cloud::loadbalancer::binding { 'swift_api_cluster':
ip => $swift_api,
port => $ks_swift_public_port,
httpchk => 'httpchk /healthcheck';
ip => $swift_api,
port => $ks_swift_public_port,
bind_options => $swift_bind_options,
httpchk => 'httpchk /healthcheck',
}
cloud::loadbalancer::binding { 'nova_api_cluster':
ip => $nova_api,
port => $ks_nova_public_port;
ip => $nova_api,
port => $ks_nova_public_port,
bind_options => $nova_bind_options,
}
cloud::loadbalancer::binding { 'ec2_api_cluster':
ip => $ec2_api,
port => $ks_ec2_public_port;
ip => $ec2_api,
port => $ks_ec2_public_port,
bind_options => $ec2_bind_options,
}
cloud::loadbalancer::binding { 'metadata_api_cluster':
ip => $metadata_api,
port => $ks_metadata_public_port;
ip => $metadata_api,
port => $ks_metadata_public_port,
bind_options => $metadata_bind_options,
}
cloud::loadbalancer::binding { 'spice_cluster':
ip => $spice,
@ -316,6 +359,7 @@ class cloud::loadbalancer(
'timeout server' => '120m',
'timeout client' => '120m',
},
bind_options => $spice_bind_options,
httpchk => 'httpchk GET /';
}
cloud::loadbalancer::binding { 'glance_api_cluster':
@ -325,74 +369,73 @@ class cloud::loadbalancer(
'timeout server' => '120m',
'timeout client' => '120m',
},
port => $ks_glance_api_public_port;
port => $ks_glance_api_public_port,
bind_options => $glance_api_bind_options,
}
cloud::loadbalancer::binding { 'glance_registry_cluster':
ip => $glance_registry,
port => $ks_glance_registry_internal_port;
ip => $glance_registry,
port => $ks_glance_registry_internal_port,
bind_options => $glance_registry_bind_options,
}
cloud::loadbalancer::binding { 'neutron_api_cluster':
ip => $neutron_api,
port => $ks_neutron_public_port;
ip => $neutron_api,
port => $ks_neutron_public_port,
bind_options => $neutron_bind_options,
}
cloud::loadbalancer::binding { 'cinder_api_cluster':
ip => $cinder_api,
port => $ks_cinder_public_port;
ip => $cinder_api,
port => $ks_cinder_public_port,
bind_options => $cinder_bind_options,
}
cloud::loadbalancer::binding { 'ceilometer_api_cluster':
ip => $ceilometer_api,
port => $ks_ceilometer_public_port;
ip => $ceilometer_api,
port => $ks_ceilometer_public_port,
bind_options => $ceilometer_bind_options,
}
cloud::loadbalancer::binding { 'heat_api_cluster':
ip => $heat_api,
port => $ks_heat_public_port;
ip => $heat_api,
port => $ks_heat_public_port,
bind_options => $heat_api_bind_options,
}
cloud::loadbalancer::binding { 'heat_cfn_api_cluster':
ip => $heat_cfn_api,
port => $ks_heat_cfn_public_port;
ip => $heat_cfn_api,
port => $ks_heat_cfn_public_port,
bind_options => $heat_cfn_bind_options,
}
cloud::loadbalancer::binding { 'heat_cloudwatch_api_cluster':
ip => $heat_cloudwatch_api,
port => $ks_heat_cloudwatch_public_port;
ip => $heat_cloudwatch_api,
port => $ks_heat_cloudwatch_public_port,
bind_options => $heat_cloudwatch_bind_options,
}
if $horizon {
if $horizon_ssl {
cloud::loadbalancer::listen_https{ 'horizon_ssl_cluster':
ports => $horizon_ssl_port,
listen_ip => $vip_public_ip;
}
} else {
# Horizon URL is not the same on Red Hat and Debian/Ubuntu
if $::operatingsystem == 'RedHat' {
$horizon_auth_url = 'dashboard'
} else {
$horizon_auth_url = 'horizon'
}
cloud::loadbalancer::listen_http{ 'horizon_cluster':
ports => $horizon_port,
httpchk => "httpchk GET /${horizon_auth_url} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-${::hostname}\"",
options => {
'cookie' => 'sessionid prefix',
'balance' => 'leastconn' },
listen_ip => $vip_public_ip;
}
}
if $::operatingsystem == 'RedHat' {
$horizon_auth_url = 'dashboard'
} else {
$horizon_auth_url = 'horizon'
}
cloud::loadbalancer::binding { 'horizon_cluster':
ip => $vip_public_ip,
# to maintain backward compatibility
port => $horizon_port_real,
httpchk => $horizon_httpchk,
options => $horizon_options,
bind_options => $horizon_bind_options,
}
if ($galera_ip in $keepalived_public_ipvs_real) {
warning('Exposing Galera cluster to public network is a security issue.')
}
haproxy::listen { 'galera_cluster':
ipaddress => $galera_ip,
ports => 3306,
options => {
ipaddress => $galera_ip,
ports => 3306,
options => {
'mode' => 'tcp',
'balance' => 'roundrobin',
'option' => ['tcpka', 'tcplog', 'httpchk'], #httpchk mandatory expect 200 on port 9000
'timeout client' => '400s',
'timeout server' => '400s',
}
},
bind_options => $galera_bind_options,
}
# Allow HAProxy to bind to a non-local IP address

View File

@ -17,8 +17,9 @@
define cloud::loadbalancer::binding (
$ip,
$port,
$httpchk = undef,
$options = undef
$httpchk = undef,
$options = undef,
$bind_options = undef,
){
include cloud::loadbalancer
@ -55,10 +56,11 @@ define cloud::loadbalancer::binding (
}
}
cloud::loadbalancer::listen_http { $name :
ports => $port,
httpchk => $httpchk,
options => $options,
listen_ip => $listen_ip_real;
ports => $port,
httpchk => $httpchk,
options => $options,
listen_ip => $listen_ip_real,
bind_options => $bind_options;
}
}

View File

@ -18,21 +18,23 @@
# cloud::loadbalancer::listen_http
#
define cloud::loadbalancer::listen_http(
$ports = 'unset',
$httpchk = 'httpchk',
$options = {},
$listen_ip = '0.0.0.0') {
$ports = 'unset',
$httpchk = 'httpchk',
$options = {},
$bind_options = [],
$listen_ip = '0.0.0.0') {
$options_basic = {'mode' => 'http',
'balance' => 'roundrobin',
'http-check' => 'expect ! rstatus ^5',
'option' => ['tcpka', 'tcplog', $httpchk] }
'option' => ['tcpka', 'forwardfor', 'tcplog', $httpchk] }
$options_custom = merge($options_basic, $options)
haproxy::listen { $name:
ipaddress => $listen_ip,
ports => $ports,
options => $options_custom,
ipaddress => $listen_ip,
ports => $ports,
options => $options_custom,
bind_options => $bind_options,
}
}

View File

@ -1,38 +0,0 @@
#
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Define::
#
# cloud::loadbalancer::listen_https
#
define cloud::loadbalancer::listen_https(
$ports = 'unset',
$httpchk = 'ssl-hello-chk',
$options = {},
$listen_ip = '0.0.0.0') {
$options_basic = {'mode' => 'tcp',
'balance' => 'roundrobin',
'http-check' => 'expect ! rstatus ^5',
'option' => ['tcpka', 'tcplog', $httpchk] }
$options_custom = merge($options_basic, $options)
haproxy::listen { $name:
ipaddress => $listen_ip,
ports => $ports,
options => $options_custom,
}
}

View File

@ -37,18 +37,34 @@ describe 'cloud::loadbalancer' do
:keystone_api_admin => true,
:keystone_api => true,
:horizon => true,
:horizon_ssl => false,
:spice => true,
:ceilometer_bind_options => [],
:cinder_bind_options => [],
:ec2_bind_options => [],
:glance_api_bind_options => [],
:glance_registry_bind_options => [],
:heat_cfn_bind_options => [],
:heat_cloudwatch_bind_options => [],
:heat_api_bind_options => [],
:keystone_bind_options => [],
:keystone_admin_bind_options => [],
:metadata_bind_options => [],
:neutron_bind_options => [],
:swift_bind_options => [],
:spice_bind_options => [],
:horizon_bind_options => [],
:galera_bind_options => [],
:haproxy_auth => 'root:secrete',
:keepalived_state => 'BACKUP',
:keepalived_priority => 50,
:keepalived_public_interface => 'eth0',
:keepalived_public_ipvs => ['10.0.0.1', '10.0.0.2'],
:horizon_port => '80',
:horizon_ssl_port => '443',
:spice_port => '6082',
:vip_public_ip => '10.0.0.1',
:galera_ip => '10.0.0.2',
:horizon_ssl => false,
:horizon_ssl_port => false,
:ks_ceilometer_public_port => '8777',
:ks_nova_public_port => '8774',
:ks_ec2_public_port => '8773',
@ -191,7 +207,7 @@ describe 'cloud::loadbalancer' do
:ports => '6082',
:options => {
'mode' => 'http',
'option' => ['tcpka','tcplog','httpchk GET /'],
'option' => ['tcpka', 'forwardfor', 'tcplog','httpchk GET /'],
'http-check' => 'expect ! rstatus ^5',
'balance' => 'leastconn',
'timeout server' => '120m',
@ -282,11 +298,92 @@ describe 'cloud::loadbalancer' do
it_raises 'a Puppet::Error', /galera_ip should be part of keepalived_public_ipvs or keepalived_internal_ipvs./
end
context 'configure OpenStack binding with HTTPS and SSL offloading' do
before do
params.merge!(
:nova_bind_options => ['ssl', 'crt']
)
end
it { should contain_haproxy__listen('nova_api_cluster').with(
:ipaddress => [params[:vip_public_ip]],
:ports => '8774',
:options => {
'mode' => 'http',
'option' => ['tcpka','forwardfor','tcplog','httpchk'],
'http-check' => 'expect ! rstatus ^5',
'balance' => 'roundrobin',
},
:bind_options => ['ssl', 'crt']
)}
end
context 'configure OpenStack binding with HTTP options' do
before do
params.merge!(
:cinder_bind_options => 'something not secure',
)
end
it { should contain_haproxy__listen('cinder_api_cluster').with(
:ipaddress => [params[:vip_public_ip]],
:ports => '8776',
:options => {
'mode' => 'http',
'option' => ['tcpka','forwardfor','tcplog', 'httpchk'],
'http-check' => 'expect ! rstatus ^5',
'balance' => 'roundrobin',
},
:bind_options => ['something not secure']
)}
end
context 'configure OpenStack Horizon SSL with backward compatibility' do
before do
params.merge!(
:horizon_ssl => true,
:horizon_ssl_port => '443'
)
end
it { should contain_haproxy__listen('horizon_cluster').with(
:ipaddress => [params[:vip_public_ip]],
:ports => '443',
:options => {
'mode' => 'tcp',
'http-check' => 'expect ! rstatus ^5',
'option' => ['tcpka','forwardfor','tcplog', 'ssl-hello-chk'],
'cookie' => 'sessionid prefix',
'balance' => 'leastconn',
},
)}
end
context 'configure OpenStack Horizon SSL binding' do
before do
params.merge!(
:horizon_ssl => false,
:horizon_ssl_port => false,
:horizon_bind_options => ['ssl', 'crt']
)
end
it { should contain_haproxy__listen('horizon_cluster').with(
:ipaddress => [params[:vip_public_ip]],
:ports => '443',
:options => {
'mode' => 'http',
'http-check' => 'expect ! rstatus ^5',
'option' => ["tcpka", "forwardfor", "tcplog", "httpchk GET / \"HTTP/1.0\\r\\nUser-Agent: HAproxy-myhost\""],
'cookie' => 'sessionid prefix',
'balance' => 'leastconn',
},
:bind_options => ['ssl', 'crt']
)}
end
end # shared:: openstack loadbalancer
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian',
:hostname => 'myhost',
:concat_basedir => '/var/lib/puppet/concat' }
end
@ -296,6 +393,7 @@ describe 'cloud::loadbalancer' do
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat',
:hostname => 'myhost',
:concat_basedir => '/var/lib/puppet/concat' }
end