If you’re adding a REST service to TripleO, chances are that you’ll need your service to be terminated by HAProxy. Unfortunately, adding your service to HAProxy needs extra changes to existing modules. Fortunately, it’s not that hard to do.
You can add your service to be terminated by HAproxy by modifying the manifests/haproxy.pp file.
First off, we need a flag to tell the HAProxy module to write the frontend for
your service in the HAProxy configuration file if your service is deployed. For
this, we will add a parameter for the manifest. If you have followed the
walk-through, you may have noticed that the tripleo-heat-templates yaml
template requires you to set a name for your service in the role_data
output:
...
outputs:
role_data:
description: Description of your service
value:
service_name: my_service
...
The overcloud stack generated from the tripleo-heat-templates will use this name and automatically generate several hieradata entries that are quite useful. One of this entries is a global flag that can tell if your service is enabled at all or not. So we’ll use this flag and fetch it from hiera to set the parameter we need in haproxy.pp:
...
$keystone_admin = hiera('keystone_enabled', false),
$keystone_public = hiera('keystone_enabled', false),
$neutron = hiera('neutron_api_enabled', false),
$cinder = hiera('cinder_api_enabled', false),
$glance_api = hiera('glance_api_enabled', false),
...
$my_service = hiera('my_service_enabled', false),
...
Note that the name of the hiera key matches the following format
“<service name>_enabled” and defaults to false
.
Next, you need to add a parameter that tells HAProxy which network your service is listening on:
...
$barbican_network = hiera('barbican_api_network', false),
$ceilometer_network = hiera('ceilometer_api_network', undef),
$cinder_network = hiera('cinder_api_network', undef),
$glance_api_network = hiera('glance_api_network', undef),
$heat_api_network = hiera('heat_api_network', undef),
...
$my_service_network = hiera('my_service_network', undef),
...
Tripleo-heat-templates will also autogenerate this key for you. However for it
to do this, you need to specify the network for your service in the templates.
The file where this needs to be set is network/service_net_map.j2.yaml, and
you’ll be looking for a parameter called ServiceNetMapDefaults
. It will
look like this:
# Note that the key in this map must match the service_name
# see the description above about conversion from CamelCase to
# snake_case - the names must still match when converted
ServiceNetMapDefaults:
default:
# Note the values in this map are replaced by *NetName
# to allow for sane defaults when the network names are
# overridden.
...
NeutronTenantNetwork: tenant
CeilometerApiNetwork: internal_api
BarbicanApiNetwork: internal_api
CinderApiNetwork: internal_api
GlanceApiNetwork: storage
...
MyServiceNetwork: <some network>
Now, having added this, you’ll have access to the aforementioned hiera key and several others.
Note that the network is used by HAProxy to terminate TLS for your service. This is used when Internal TLS is enabled and you’ll learn more about it in the Internal TLS section.
Then, you need to add the ports that HAProxy will listen on. There is a list
with the defaults which is called default_service_ports
, and you need to
add your service here:
$default_service_ports = {
...
neutron_api_port => 9696,
neutron_api_ssl_port => 13696,
nova_api_port => 8774,
nova_api_ssl_port => 13774,
nova_placement_port => 8778,
nova_placement_ssl_port => 13778,
nova_metadata_port => 8775,
nova_novnc_port => 6080,
nova_novnc_ssl_port => 13080,
...
my_service_port => 5123,
my_service_ssl_port => 13123,
...
}
You are specifying two ports here, one that is the standard port, and another one that is used for SSL in the public VIP/host. This was done initially to address deployments without network isolation. In these cases, deploying TLS would effectively take over the other interfaces, so HAProxy would be listening with TLS everywhere accidentally if only using one port, and further configuration for the services would need to happen to address this. However, this is not really an issue in network isolated deployments, since they would be using different IP addresses. So this extra port might not be needed in the future if network isolation becomes the standard mode of deploying.
Note
The SSL port is not needed if your service is only internal and doesn’t listen on the public VIP.
Note
These ports can be overwritten by using the $service_ports
parameter from this manifest. Once could pass it via hieradata through the
ExtraConfig
tripleo-heat-templates parameter, and setting something
like this as the value:
tripleo::haproxy::service_ports:
my_service_ssl_port: 5123
my_service_2_ssl_port: 5124
Please consider that this will overwrite any entry from the list of defaults, so you have to be careful to update all the relevant entries in tripleo-heat-templates if you want to change port (be it SSL port or non-SSL port).
Finally, you need to add the actual endpoint to HAproxy which will configure
the listen directive (or frontend and backend) in the haproxy configuration.
For this, we have a helper class called ::tripleo::haproxy::endpoint
that
sets the relevant bits for you. All we need to do is pass in all the
information that class needs. And we need to make sure that this only happens
if the service is enabled, so we’ll enclose it with the flag we mentioned
above. So here’s a code snippet that demonstrates what you need to add:
if $my_service {
::tripleo::haproxy::endpoint { 'my_service':
public_virtual_ip => $public_virtual_ip,
internal_ip => hiera('my_service_vip', $controller_virtual_ip),
service_port => $ports[my_service_port],
ip_addresses => hiera('my_service_node_ips', $controller_hosts_real),
server_names => hiera('my_service_node_names', $controller_hosts_names_real),
mode => 'http',
listen_options => {
'http-request' => [
'set-header X-Forwarded-Proto https if { ssl_fc }',
'set-header X-Forwarded-Proto http if !{ ssl_fc }'],
},
public_ssl_port => $ports[my_service_ssl_port],
service_network => $my_service_network,
}
}
public_virtual_ip
variable contains the public IP address that’s used
for your cloud, and it’s the one that people will usually have access to
externally.my_service_node_ips
, my_service_vip
,
my_service_node_names
are automatically generated by
tripleo-heat-templates. These are other keys that you’ll get access to once
you add the network for your service in ServiceNetMapDefaults
.my_service_vip
is, as mentioned, automatically generated, and will point
HAProxy to the non-public VIP where other services will be able to access
your service. This will usually be the Internal API network, but it depends
on your use-case.my_service_node_ips
is, as mentioned, automatically generated, and will
tell HAProxy which nodes are hosting your service, so it will point to them.
The address depends on the network your service is listening on.my_service_node_names
is, as mentioned, automatically generated, and will
be the names that HAProxy will use for the nodes. These are the FQDNs of the
nodes that are hosting your service.http
,
and that we set the option for HAProxy to detect if TLS was used for the
request, and set an appropriate value for the X-Forwarded-Proto
HTTP
header if that’s the case. Not all services can read this HTTP header, so
this depends on your service. For more information on the available options
and the mode, consult the haproxy documentation.Note
If your service is only internal and doesn’t listen on the public VIP, you don’t need all of the parameters listed above, and you would instead do something like this:
if $my_service {
::tripleo::haproxy::endpoint { 'my_service':
internal_ip => hiera('my_service_vip', $controller_virtual_ip),
service_port => $ports[my_service_port],
ip_addresses => hiera('my_service_node_ips', $controller_hosts_real),
server_names => hiera('my_service_node_names', $controller_hosts_names_real),
service_network => $my_service_network,
}
}
The most relevant bits are that we omitted the SSL port and the
public_virtual_ip
, since these won’t be used.
Having added this to the manifest, you should be covered for both getting your service to be proxied by HAProxy, and letting it to TLS in the public interface for you.
If you haven’t read the section TLS everywhere for the overcloud it is highly recommended you read that first before continuing.
As mentioned, the default CA is FreeIPA, which issues the certificates that the nodes request, and they do the requests via certmonger.
FreeIPA needs to have the nodes registered in its database and those nodes need to be enrolled in order to authenticate to the CA. This is already being handled for us, so there’s nothing you need to do for your service on this side.
In order to issue certificates, FreeIPA also needs to have registered a Kerberos principal for the service (or service principal). This way it knows what service is using what certificate. The service principal will look something like this:
<service name>/<host>.<domain>
We assume that the domain matches the kerberos realm, so specifying it is redundant.
Fortunately, one doesn’t need to do much but fill in some boilerplate code in tripleo-heat-templates to get this service principal. And this will be covered in subsequent sections.
So, with this one can finally request certificates for the service and use them.
Aside from the actual certificate request, if your service is a RESTful service, getting TLS to work with the current solution requires usually two fronts:
This can be different for other types of services. For instance, at the time of writing this, RabbitMQ isn’t proxied by HAProxy, so there wasn’t a need to configure anything in HAProxy. Another example is MariaDB: Even though it is proxied by HAProxy, TLS is handled on the MariaDB side and HAProxy doesn’t do TLS termination, so there was no need to configure HAProxy.
Also, for services in general, there are two options for the Subject Alternative Name (SAN) for the certificate:
The usual case for a RESTful service will be the first option. HAProxy will do TLS termination, listening on the cloud’s VIPs, and will then forward the request to your service trying to access it via the node’s internal network interface (not the VIP). So for this case (#1), your service should be serving a TLS certificate with the nodes’ interface as the SAN. RabbitMQ has a similar situation even if it’s not proxied by HAProxy. Services try to access the RabbitMQ cluster through the individual nodes, so each broker server has a certificate with the node’s hostname for a specific network interface as the SAN. On the other hand, MariaDB follows the SAN pattern #2. It’s terminated by HAProxy, so the services access it through a VIP. However, MariaDB handles TLS by itself, so it ultimately serves certificates with the hostname pointing to a VIP interface as the SAN. This way, the hostname validation works as expected.
If you’re not sure how to go forward with your service, consult the TripleO team.
Good news! Certificates are already requested for you and there is a hash where you can fetch the path to the certificates and use them for your service.
In puppet-tripleo you need to go to the manifest that deploys the API for your service. Here, you will add the following parameters to the class:
class tripleo::profile::base::my_service::api (
...
$my_service_network = hiera('my_service_network', undef),
$certificates_specs = hiera('apache_certificates_specs', {}),
$enable_internal_tls = hiera('enable_internal_tls', false),
...
) {
my_service_network
is a hiera key that’s already generated by
tripleo-heat-templates and it references the name of the network your service
is listening on. This was referenced in the Public TLS section.
Where it mentioned the addition of your service’s network to the
ServiceNetMapDefaults
parameter. So, if this was done, you’ll get this
key autogenerated.apache_certificates_specs
is a hash containing the specifications for all
the certificates requested for services running over httpd. These are
network-dependant, which is why we needed the network name. Note that this
also contains the paths where the keys are located in the filesystem.enable_internal_tls
is a flag that tells TripleO if TLS for the internal
network is enabled. We should base the usage of the certificates for your
service on this.In order to get the certificate and key for your application you can use the following boilerplate code:
if $enable_internal_tls {
if !$my_service_network {
fail('my_service_network is not set in the hieradata.')
}
$tls_certfile = $certificates_specs["httpd-${my_service_network}"]['service_certificate']
$tls_keyfile = $certificates_specs["httpd-${my_service_network}"]['service_key']
} else {
$tls_certfile = undef
$tls_keyfile = undef
}
If internal TLS is not enabled, we set the variables for the certificate and
key to undef
, this way TLS won’t be enabled. If it’s enabled, we get the
certificate and key from the hash.
Now, having done this, we can pass in the variables to the class that deploys your service over httpd:
class { '::my_service::wsgi::apache':
ssl_cert => $tls_certfile,
ssl_key => $tls_keyfile,
}
Now, in tripleo-heat-templates, hopefully the template for your service’s
API already uses the base profile for apache services. To verify this, you need
to look in the resources
section of your template for something like this:
ApacheServiceBase:
type: ./apache.yaml
properties:
ServiceNetMap: {get_param: ServiceNetMap}
DefaultPasswords: {get_param: DefaultPasswords}
EndpointMap: {get_param: EndpointMap}
Note that this is of type ./apache.yaml which is the template that contains the common configurations for httpd based services.
You will also need to make sure that the ssl hieradata is set correctly. You will find it usually like this:
my_service::wsgi::apache::ssl: {get_param: EnableInternalTLS}
Where, EnableInternalTLS should be defined in the parameters
section of the
template.
Finally, you also need to add the metadata_settings
to the output of the
template. This section will be in the same level as config_settings
and
step_config
, and will contain the following:
metadata_settings:
get_attr: [ApacheServiceBase, role_data, metadata_settings]
Note that it merely outputs the metadata_settings section that the apache base stack already outputs. This will give the appropriate parameters to a hook that sets the nova metadata, which in turn will be taken by the novajoin service (which was mentioned in the TLS everywhere for the overcloud section) to generate the service principals for httpd for the host.
Now that your service will be serving with TLS enabled, we go back to the manifests/haproxy.pp file. You already have added the HAProxy endpoint resource for your service, so for this, you need to add now the option to tell it to use TLS to communicate with the server backend nodes. This is done by adding this:
if $my_service {
::tripleo::haproxy::endpoint { 'my_service':
...
member_options => union($haproxy_member_options, $internal_tls_member_options),
}
}
This adds the TLS options to the default member options we use in TripleO for HAProxy. It will tell HAProxy to require TLS for your service if internal TLS is enabled; if it’s not enabled, then it won’t use TLS.
This was all the extra configuration you needed to do for HAProxy.
If your service supports being run with TLS enabled, and it’s not python/eventlet-based (see Internal TLS via a TLS-proxy). This section is for you.
In tripleo-heat-templates we’ll need to specify the specs for doing the certificate request, and we’ll need to get the appropriate information to generate a service principal. To make this optional, you should add the following to your service’s base template:
parameters:
...
EnableInternalTLS:
type: boolean
default: false
conditions:
internal_tls_enabled: {equals: [{get_param: EnableInternalTLS}, true]}
...
...
EnableInternalTLS
is a parameter that’s passed via parameter_defaults
which tells the templates that we want to use TLS in the internal network.internal_tls_enabled
is a condition that we’ll furtherly use to add the
relevant bits to the output.The next thing to do is to add the certificate specs, the relevant hieradata
and the required metadata to the output. In the roles_data
output, lets
modify the config_settings
to add what we need:
config_settings:
map_merge:
-
# The regular hieradata for your service goes here.
...
-
if:
- internal_tls_enabled
- generate_service_certificates: true
my_service_certificate_specs:
service_certificate: '/etc/pki/tls/certs/my_service.crt'
service_key: '/etc/pki/tls/private/my_service.key'
hostname:
str_replace:
template: "%{hiera('fqdn_NETWORK')}"
params:
NETWORK: {get_param: [ServiceNetMap, MyServiceNetwork]}
principal:
str_replace:
template: "my_service/%{hiera('fqdn_NETWORK')}"
params:
NETWORK: {get_param: [ServiceNetMap, MyServiceNetwork]}
- {}
...
metadata_settings:
if:
- internal_tls_enabled
-
- service: my_service
network: {get_param: [ServiceNetMap, MyServiceNetwork]}
type: node
- null
The conditional mentioned above is used in the config_settings
. So, if
internal_tls_enabled
evaluates to true
, the hieradata necessary to
enable TLS in the internal network for your service will be added. Else, we
output {}
, which won’t affect the map_merge
and won’t add anything
to the regular hieradata for your service.
For this case, we are only requesting one certificate for the service.
The service will be terminated by HAProxy in a conventional way, which means that the SAN will be case #1 as described in Enabling internal TLS for your service. So the SAN will point to the specific node’s network interface, and not the VIP.
The ServiceNetMap
contains the references to the networks every service
is listening on, and the key to get the network is the name of your service
but using camelCase instead of underscores. This value is the name of the
network and if used under the config_settings
section, it will be
replaced by the actual IP. Else, it will just be the network name.
tripleo-heat-templates automatically generates hieradata that contains the different network-dependant hostnames. They keys are in the following format:
fqdn_<network name>
The my_service_certificate_specs
key will contain the specifications for
the certificate we’ll request. They need to follow some conventions:
service_certificate
will specify the path to the certificate file. It
should be an absolute path.
service_key
will specify the path to the private key file that will be
used for the certificate. It should be an absolute path.
hostname
is the name that will be used both in the Common Name (CN) and
the Subject Alternative Name (SAN) of the certificate. We can get this
value by using the hiera key described above. So we first get the name of
the network the service is listening on from the ServiceNetMap
and we
then use str_replace
to place that in a hiera call in the appropriate
format.
principal
is the service principal that will be the one used for the
certificate request. We can get this in a similar manner as we got the
hostname, and prepending an identifying name for your service. The format
will be as follows:
< service identifier >/< network-based hostname >
These are the names used by convention, and will eventually be passed to
the certmonger_certificate
resource from puppet-certmonger.
The metadata_settings
section will pass some information to a metadata
hook that will create the service principal before the certificate request is
done. The format as as follows:
service
: This contains the service identifier to be used in the
kerberos service principal. It should match the identifier you put in the
principal
section of the certificate specs.network
: Tells the hook what network to use for the service. This will
be used for the hook and novajoin to use an appropriate hostname for the
kerberos principal.type
: Will tell the hook what type of case is this service. The
available options are node
and vip
. These are the cases mentioned
in the Enabling internal TLS for your service for the SANs.Note that this is a list, which can be useful if we’ll be creating several
service principals (which is not the case for our example). Also, if
internal_tls_enabled
evaluates to false
, we then output null
.
Remember to set any relevant flags or parameters that your service might
need as hieradata in config_settings
. These might be things that
explicitly enable TLS such as flags or paths. But these details depend on the
puppet module that deploys your service.
Note
VIP-based hostname case
If your service requires the certificate to contain a VIP-based hostname, as is the case for MariaDB. It would instead look like the following:
metadata_settings:
if:
- internal_tls_enabled
-
- service: my_service
network: {get_param: [ServiceNetMap, MyServiceNetwork]}
type: vip
- null
One can get the hostname for the VIP in a similar fashion as we got the hostname for the node. The VIP hostnames are also network based, and one can get them from a hiera key as well. It has the following format:
cloud_name_< network name >
The type
in the metadata_settings
entry is vip
.
In puppet-tripleo We’ll create a class that does the actual certificate request and add it to the resource that gets the certificates for all the services.
Lets create a class to do the request:
class tripleo::certmonger::my_service (
$hostname,
$service_certificate,
$service_key,
$certmonger_ca = hiera('certmonger_ca', 'local'),
$principal = undef,
) {
include ::my_service::params
$postsave_cmd = "systemctl restart ${::my_service::params::service_name}"
certmonger_certificate { 'my_service' :
ensure => 'present',
certfile => $service_certificate,
keyfile => $service_key,
hostname => $hostname,
dnsname => $hostname,
principal => $principal,
postsave_cmd => $postsave_cmd,
ca => $certmonger_ca,
wait => true,
require => Class['::certmonger'],
}
file { $service_certificate :
owner => $::my_service::params::user,
group => $::my_service::params::group,
require => Certmonger_certificate['my_service'],
}
file { $service_key :
owner => $::my_service::params::user,
group => $::my_service::params::group,
require => Certmonger_certificate['my_service'],
}
File[$service_certificate] ~> Service<| title == $::my_service::params::service_name |>
File[$service_key] ~> Service<| title == $::my_service::params::service_name |>
}
certmonger_ca
is a value that comes from tripleo-heat-templates and tells
certmonger which CA to use.certmonger_certificate
provider and passing all the relevant data for the
request.postsave_cmd
is a
command that will be ran after the certificate is saved. This is useful for
when certmonger has to resubmit the request to get an updated certificate,
since this way we can reload or restart the service so it can serve the new
certificate.file
resource from puppet, we set the appropriate user and
group for the certificate and keys. Fortunately, certmonger has sane defaults
for the file modes, so we didn’t set those here.Having this class, we now need to add to the certmonger_user resource. This resource is in charge of making all the certificate requests and should be available on all roles (or at least it should be added). You would add the certificate specs as a parameter to this class:
class tripleo::profile::base::certmonger_user (
...
$my_service_certificate_specs = hiera('my_service_certificate_specs', {}),
...
) {
And finally, we call the class that does the request:
...
unless empty($my_service_certificate_specs) {
ensure_resource('class', 'tripleo::certmonger::my_service', $my_service_certificate_specs)
}
...
Note
It is also possible to do several requests for your service. See the certmonger_user source code for examples.
Finally, you can do the same steps described in configuring-haproxy-internal-tls to make HAProxy connect to your service using TLS.
If you have a RESTful service that runs over python (most likely using eventlet) or if your service requires a TLS proxy in order to have TLS in the internal network, there are extra steps to be done.
For python-based services, due to performance issues with eventlet, the best thing you can do is try to move your service to run over httpd, and let it handle crypto instead. Then you’ll be able to follow the instructions from the Services that run over httpd section above. If for any reason this can’t be done at the moment, we could still use httpd to service as a TLS proxy in the node. It would then listen on the service’s port and forward all the requests to the service, which would then be listening on localhost.
In puppet-tripleo you need to go to the manifest that deploys the API for your service, and add the following parameters:
class tripleo::profile::base::my_service::api (
...
$certificates_specs = hiera('apache_certificates_specs', {}),
$enable_internal_tls = hiera('enable_internal_tls', false),
$my_service_network = hiera('my_service_api_network', undef),
$tls_proxy_bind_ip = undef,
$tls_proxy_fqdn = undef,
$tls_proxy_port = 5123,
...
) {
...
certificates_specs
, enable_internal_tls
and my_service_network
have already been mentioned in the Services that run over httpd
section.tls_proxy_bind_ip
, tls_proxy_fqdn
and tls_proxy_port
are
parameters that will be used by the httpd-based TLS proxy. They will tell it
where what IP to listen on, the FQDN (which will be used as the servername)
and the port it will use. Usually the port will match your service’s port.
These values are expected to be set from tripleo-heat-templates.Next comes the code for the actual proxy:
...
if $enable_internal_tls {
if !$my_service_network {
fail('my_service_network is not set in the hieradata.')
}
$tls_certfile = $certificates_specs["httpd-${my_service_network}"]['service_certificate']
$tls_keyfile = $certificates_specs["httpd-${my_service_network}"]['service_key']
::tripleo::tls_proxy { 'my_service_proxy':
servername => $tls_proxy_fqdn,
ip => $tls_proxy_bind_ip,
port => $tls_proxy_port,
tls_cert => $tls_certfile,
tls_key => $tls_keyfile,
notify => Class['::my_service::api'],
}
}
...
::tripleo::tls_proxy
is the resource that will configure the TLS
proxy for your service. As you can see, it receives the certificates that
come from the certificates_specs
which contain the specification
for the certificates, including the paths for the keys.In tripleo-heat-templates, you should modify your service’s template and add the following:
parameters:
...
EnableInternalTLS:
type: boolean
default: false
...
conditions:
...
use_tls_proxy: {equals : [{get_param: EnableInternalTLS}, true]}
...
resources:
...
TLSProxyBase:
type: OS::TripleO::Services::TLSProxyBase
properties:
ServiceNetMap: {get_param: ServiceNetMap}
DefaultPasswords: {get_param: DefaultPasswords}
EndpointMap: {get_param: EndpointMap}
EnableInternalTLS: {get_param: EnableInternalTLS}
EnableInternalTLS
is a parameter that’s passed via parameter_defaults
which tells the templates that we want to use TLS in the internal network.
use_tls_proxy
is a condition that we’ll use to modify the behaviour of
the template depending on whether TLS in the internal network is enabled or
not.
TLSProxyBase
will make the default values from the proxy’s template
available to where our service is deployed. We should make sure that we
combine our service’s hieradata with the hieradata coming from that resource
by doing a map_merge
with the config_settings
:
...
config_settings:
map_merge:
- get_attr: [TLSProxyBase, role_data, config_settings]
- # Here goes our service's metadata
...
So, with this, we can tell the service to bind on localhost instead of the
default interface depending if TLS in the internal network is enabled or not.
Lets now set the hieradata that the puppet module needs in our service’s
hieradata, which is in the config_settings
section:
tripleo::profile::base::my_service::api::tls_proxy_bind_ip:
get_param: [ServiceNetMap, MyServiceNetwork]
tripleo::profile::base::my_service::api::tls_proxy_fqdn:
str_replace:
template:
"%{hiera('fqdn_$NETWORK')}"
params:
$NETWORK: {get_param: [ServiceNetMap, MyServiceNetwork]}
tripleo::profile::base::my_service::api::tls_proxy_port:
get_param: [EndpointMap, NeutronInternal, port]
my_service::bind_host:
if:
- use_tls_proxy
- 'localhost'
- {get_param: [ServiceNetMap, MyServiceNetwork]}
The ServiceNetMap
contains the references to the networks every service
is listening on, and the key to get the network is the name of your service
but using camelCase instead of underscores. This value will be automatically
replaced by the actual IP.
tripleo-heat-templates generates automatically hieradata that contains the different network-dependant hostnames. They keys are in the following format:
fqdn_<network name>
So, to get it, we get the network name from the ServiceNetMap
, and do a
str_replace
in heat that will use that network name and add it to a hiera
call that will then gets us the FQDN we need.
The port we can easily get from the EndpointMap
.
The conditional uses the actual IP if there’s no TLS in the internal network enabled and localhost if it is.
Finally, we add the metadata_settings
section to make sure we get a
kerberos service principal:
metadata_settings:
get_attr: [TLSProxyBase, role_data, metadata_settings]
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.