Adds TLS support to configuring OVS with OpenDaylight

Enables configuring OVS with SSL configuration, as well as setting
OVS managers and ODL check url to use SSL/TLS.  Each OVS will also
register their certificate with OpenDaylight's truststore.

Partially-Implements: blueprint opendaylight-ssl-support

Depends-On: Ic026ee0bc4f385e0f8cd7076b3044feeb935ae45

Change-Id: I719e8dddbd00d19fd8e1bd2a20dabd600b7b9d1c
Signed-off-by: Tim Rozet <trozet@redhat.com>
This commit is contained in:
Tim Rozet 2018-01-03 11:39:03 -05:00
parent bcb2a54cb4
commit 65107cb5a8
4 changed files with 190 additions and 15 deletions

View File

@ -0,0 +1,19 @@
Puppet::Functions.create_function(:convert_cert_to_string) do
dispatch :convert_cert_to_string do
param 'String', :cert_file
end
def convert_cert_to_string(cert_file)
unless File.file?(cert_file)
raise puppet::ParseError, "Certificate file not found: #{cert_file}"
end
text=File.readlines(cert_file)
cert_string = ''
text.each do |line|
unless line.include? '-----'
cert_string += line.strip
end
end
return cert_string
end
end

View File

@ -71,6 +71,22 @@
# supported from ovs 2.8.0.
# Defaults to False.
#
# [*enable_tls*]
# (optional) Configure OVS to use SSL/TLS
# Defaults to False.
#
# [*tls_key_file*]
# (optional) Private key file path to use for TLS configuration
# Defaults to False. Required if enabling TLS.
#
# [*tls_cert_file*]
# (optional) Certificate file path to use for TLS configuration
# Defaults to False. Required if enabling TLS.
#
# [*tls_ca_cert_file*]
# (optional) CA Certificate file path to use for TLS configuration
# Defaults to False.
#
class neutron::plugins::ovs::opendaylight (
$tunnel_ip,
$odl_username,
@ -86,7 +102,11 @@ class neutron::plugins::ovs::opendaylight (
$enable_dpdk = false,
$vhostuser_socket_dir = '/var/run/openvswitch',
$vhostuser_mode = 'client',
$enable_hw_offload = false
$enable_hw_offload = false,
$enable_tls = false,
$tls_key_file = undef,
$tls_cert_file = undef,
$tls_ca_cert_file = undef
) {
include ::neutron::deps
@ -94,16 +114,98 @@ class neutron::plugins::ovs::opendaylight (
# Handle the case where ODL controller is also on this host
Service<| title == 'opendaylight' |> -> Exec <| title == 'Wait for NetVirt OVSDB to come up' |>
if $enable_tls {
if empty($tls_key_file) or empty($tls_cert_file) {
fail('When enabling TLS, tls_key_file and tls_cert_file must be provided')
}
if ! empty($tls_ca_cert_file) {
vs_ssl { 'system':
ensure => present,
key_file => $tls_key_file,
cert_file => $tls_cert_file,
ca_file => $tls_ca_cert_file,
before => Exec['Set OVS Manager to OpenDaylight']
}
} else {
vs_ssl { 'system':
ensure => present,
key_file => $tls_key_file,
cert_file => $tls_cert_file,
bootstrap => true,
before => Exec['Set OVS Manager to OpenDaylight']
}
}
if $odl_ovsdb_iface =~ /^tcp/ {
warning('TLS enabled but odl_ovsdb_iface set to tcp. Will override to ssl')
$odl_ovsdb_iface_parsed = regsubst($odl_ovsdb_iface, '^tcp', 'ssl')
}
if $ovsdb_server_iface =~ /^ptcp/ {
warning('TLS enabled but ovsdb_server_iface set to ptcp. Will override to pssl')
$ovsdb_server_iface_parsed = regsubst($ovsdb_server_iface, '^ptcp', 'pssl')
}
if $odl_check_url =~ /^http:/ {
warning('TLS enabled but odl_check_url set to http. Will override to https')
$odl_check_url_parsed = regsubst($odl_check_url, '^http:', 'https:')
} else {
$odl_check_url_parsed = $odl_check_url
}
$cert_data = convert_cert_to_string($tls_cert_file)
$rest_data = @("END":json/L)
{\
"aaa-cert-rpc:input": {\
"aaa-cert-rpc:node-alias": "${::hostname}",\
"aaa-cert-rpc:node-cert": "${cert_data}"\
}\
}
|-END
$odl_url_prefix = $odl_check_url_parsed ? {
/^(https:\/\/.*?)\// => $1,
default => undef
}
if $odl_url_prefix == undef {
fail("Unable to parse URL prefix from ${odl_check_url_parsed}")
}
$curl_post = "curl -k -X POST -o /dev/null --fail --silent -H 'Content-Type: application/json' -H 'Cache-Control: no-cache'"
$curl_get = "curl -k -X POST --fail --silent -H 'Content-Type: application/json' -H 'Cache-Control: no-cache'"
$cert_rest_url = "${odl_url_prefix}/restconf/operations/aaa-cert-rpc:setNodeCertifcate"
$cert_rest_get = "${odl_url_prefix}/restconf/operations/aaa-cert-rpc:getNodeCertifcate"
$rest_get_data = @("END":json/L)
{\
"aaa-cert-rpc:input": {\
"aaa-cert-rpc:node-alias": "${::hostname}"\
}\
}
|-END
exec { "Add trusted cert: ${tls_cert_file}":
command => "${curl_post} -u ${odl_username}:${odl_password} -d '${rest_data}' ${cert_rest_url}",
tries => 5,
try_sleep => 30,
unless => "${curl_get} -u ${odl_username}:${odl_password} -d '${rest_get_data}' ${cert_rest_get} | grep -q ${cert_data}",
path => '/usr/sbin:/usr/bin:/sbin:/bin',
before => Exec['Set OVS Manager to OpenDaylight'],
require => Exec['Wait for NetVirt OVSDB to come up']
}
} else {
$odl_ovsdb_iface_parsed = $odl_ovsdb_iface
$ovsdb_server_iface_parsed = $ovsdb_server_iface
$odl_check_url_parsed = $odl_check_url
}
exec { 'Wait for NetVirt OVSDB to come up':
command => "curl -o /dev/null --fail --silent --head -u ${odl_username}:${odl_password} ${odl_check_url}",
command => "curl -k -o /dev/null --fail --silent --head -u ${odl_username}:${odl_password} ${odl_check_url_parsed}",
tries => $retry_count,
try_sleep => $retry_interval,
path => '/usr/sbin:/usr/bin:/sbin:/bin',
}
# OVS manager
-> exec { 'Set OVS Manager to OpenDaylight':
command => "ovs-vsctl set-manager ${ovsdb_server_iface} ${odl_ovsdb_iface}",
unless => "ovs-vsctl show | grep 'Manager \"${ovsdb_server_iface} ${odl_ovsdb_iface}\"'",
command => "ovs-vsctl set-manager ${ovsdb_server_iface_parsed} ${odl_ovsdb_iface_parsed}",
unless => "ovs-vsctl show | grep 'Manager \"${ovsdb_server_iface_parsed} ${odl_ovsdb_iface_parsed}\"'",
path => '/usr/sbin:/usr/bin:/sbin:/bin',
}

View File

@ -0,0 +1,6 @@
---
features:
- |
Enables using TLS in Open vSwitch (OVS) with OpenDaylight. OVS is
configured to with certificates and private key to use in enabling a
secure connection to OpenDaylight via OVSDB.

View File

@ -2,16 +2,6 @@ require 'spec_helper'
describe 'neutron::plugins::ovs::opendaylight' do
let :pre_condition do
"class { '::neutron::keystone::authtoken':
password => 'passw0rd',
}
class { 'neutron::server': }
class { 'neutron':
rabbit_password => 'passw0rd',
core_plugin => 'ml2' }"
end
let :default_params do
{
:odl_check_url => 'http://127.0.0.1:8080/restconf/operational/network-topology:network-topology/topology/netvirt:1',
@ -25,7 +15,8 @@ describe 'neutron::plugins::ovs::opendaylight' do
:enable_dpdk => false,
:vhostuser_socket_dir => '/var/run/openvswitch',
:vhostuser_mode => 'client',
:enable_hw_offload => false
:enable_hw_offload => false,
:enable_tls => false,
}
end
@ -70,7 +61,53 @@ describe 'neutron::plugins::ovs::opendaylight' do
end
it_raises 'a Puppet::Error',/Enabling hardware offload and DPDK is not allowed/
end
it_configures 'with default parameters'
context 'with TLS and no key or certificates' do
before do
params.merge!({ :enable_tls => true })
end
it_raises 'a Puppet::Error',/When enabling TLS, tls_key_file and tls_cert_file must be provided/
end
context 'with TLS and no CA cert' do
before do
File.stubs(:file?).returns(true)
File.stubs(:readlines).returns(["MIIFGjCCBAKgAwIBAgICA"])
params.merge!({
:enable_tls => true,
:tls_key_file => 'dummy.pem',
:tls_cert_file => 'dummy.crt'})
end
it_configures 'with TLS enabled'
it {is_expected.to contain_vs_ssl('system').with(
'ensure' => 'present',
'key_file' => 'dummy.pem',
'cert_file' => 'dummy.crt',
'bootstrap' => true,
'before' => 'Exec[Set OVS Manager to OpenDaylight]'
)}
end
context 'with TLS and CA cert' do
before do
File.stubs(:file?).returns(true)
File.stubs(:readlines).returns(["MIIFGjCCBAKgAwIBAgICA"])
params.merge!({
:enable_tls => true,
:tls_key_file => 'dummy.pem',
:tls_cert_file => 'dummy.crt',
:tls_ca_cert_file => 'ca.crt'})
end
it_configures 'with TLS enabled'
it {is_expected.to contain_vs_ssl('system').with(
'ensure' => 'present',
'key_file' => 'dummy.pem',
'cert_file' => 'dummy.crt',
'ca_file' => 'ca.crt',
'before' => 'Exec[Set OVS Manager to OpenDaylight]'
)}
end
end
shared_examples_for 'with default parameters' do
@ -110,6 +147,17 @@ describe 'neutron::plugins::ovs::opendaylight' do
end
end
shared_examples_for 'with TLS enabled' do
it 'configures OVS for ODL' do
is_expected.to contain_exec('Add trusted cert: dummy.crt')
is_expected.to contain_exec('Set OVS Manager to OpenDaylight')
is_expected.to contain_vs_config('other_config:local_ip')
is_expected.not_to contain_vs_config('other_config:provider_mappings')
is_expected.to contain_vs_config('external_ids:odl_os_hostconfig_hostid')
is_expected.to contain_vs_config('external_ids:odl_os_hostconfig_config_odl_l2')
end
end
context 'on RedHat platforms' do
let :facts do
@default_facts.merge(test_facts.merge({