From 158e300d5424331ede60de9c2dfd4f95055f0f60 Mon Sep 17 00:00:00 2001 From: Jerry Sun Date: Wed, 19 Dec 2018 11:28:13 -0500 Subject: [PATCH] Docker Registry Keystone Authentication This commit adds functionality for Docker registry to authenticate using Keystone. First, this commit contains puppet changes which are required to manage the new token server required for Keystone authentication. Second, with proper authentication now implemented, we are removing the "insecure" flag for the controller registry in the "daemon.json" file in "/etc/docker". With the "insecure" flag removed, Docker will start complaining about certificate issues. This commit also includes generation of default certificates suitable for use by Docker registry as well as a sysinv command "system certificate-install -m docker_registry" to update the certificate. Docker registry token server works only with PKCS1 style keys while we would like to use PKCS8 keys by default. This is why our default certificate and installed certificate create both a PKCS1 style key as well as a PKCS8 style key. The keys are installed to "/etc/ssl/private/" as registry-cert.crt, registry-cert.key, and registry-cert-pkcs1.key. Story: 2002840 Task: 22783 Depends-On: https://review.openstack.org/#/c/626354/ Change-Id: I0127bd5f10f3950739678929b92eb1b77e2119db Signed-off-by: Jerry Sun --- .../scripts/controller_config | 37 ++++ .../platform/files/registry-token-server | 90 ++++++++++ .../platform/manifests/dockerdistribution.pp | 168 ++++++++++++++++-- .../src/modules/platform/manifests/sm.pp | 13 ++ .../templates/dockerdistribution.conf.erb | 10 +- .../templates/registry-cert-extfile.erb | 10 ++ .../templates/registry-token-server.conf.erb | 7 + .../cgtsclient/v1/certificate_shell.py | 3 +- sysinv/sysinv/sysinv/setup.cfg | 5 +- .../sysinv/api/controllers/v1/certificate.py | 4 +- .../sysinv/sysinv/sysinv/common/constants.py | 15 +- .../sysinv/sysinv/sysinv/conductor/manager.py | 100 ++++++++++- .../sysinv/puppet/dockerdistribution.py | 19 ++ workerconfig/workerconfig/worker_config | 11 ++ 14 files changed, 468 insertions(+), 24 deletions(-) create mode 100644 puppet-manifests/src/modules/platform/files/registry-token-server create mode 100644 puppet-manifests/src/modules/platform/templates/registry-cert-extfile.erb create mode 100644 puppet-manifests/src/modules/platform/templates/registry-token-server.conf.erb create mode 100644 sysinv/sysinv/sysinv/sysinv/puppet/dockerdistribution.py diff --git a/controllerconfig/controllerconfig/scripts/controller_config b/controllerconfig/controllerconfig/scripts/controller_config index 33739c793b..89857f6539 100755 --- a/controllerconfig/controllerconfig/scripts/controller_config +++ b/controllerconfig/controllerconfig/scripts/controller_config @@ -262,6 +262,43 @@ start() fi fi + if [ -e $CONFIG_DIR/registry-cert-pkcs1.key ] + then + cp $CONFIG_DIR/registry-cert-pkcs1.key /etc/ssl/private/registry-cert-pkcs1.key + if [ $? -ne 0 ] + then + fatal_error "Unable to copy $CONFIG_DIR/registry-cert-pkcs1.key" + fi + fi + + if [ -e $CONFIG_DIR/registry-cert.key ] + then + cp $CONFIG_DIR/registry-cert.key /etc/ssl/private/registry-cert.key + if [ $? -ne 0 ] + then + fatal_error "Unable to copy $CONFIG_DIR/registry-cert.key" + fi + fi + + if [ -e $CONFIG_DIR/registry-cert.crt ] + then + cp $CONFIG_DIR/registry-cert.crt /etc/ssl/private/registry-cert.crt + if [ $? -ne 0 ] + then + fatal_error "Unable to copy $CONFIG_DIR/registry-cert.crt to certificates dir" + fi + + # this is management network for now + REGISTRY_IP=$(get_ip controller) + mkdir -p /etc/docker/certs.d/$REGISTRY_IP:9001/ + chmod 700 /etc/docker/certs.d/$REGISTRY_IP:9001/ + cp $CONFIG_DIR/registry-cert.crt /etc/docker/certs.d/$REGISTRY_IP:9001/registry-cert.crt + if [ $? -ne 0 ] + then + fatal_error "Unable to copy $CONFIG_DIR/registry-cert.crt to docker dir" + fi + fi + if [ -e $CONFIG_DIR/iptables.rules ] then cp $CONFIG_DIR/iptables.rules /etc/platform/iptables.rules diff --git a/puppet-manifests/src/modules/platform/files/registry-token-server b/puppet-manifests/src/modules/platform/files/registry-token-server new file mode 100644 index 0000000000..5cfe82a881 --- /dev/null +++ b/puppet-manifests/src/modules/platform/files/registry-token-server @@ -0,0 +1,90 @@ +#!/bin/bash +# +# SPDX-License-Identifier: Apache-2.0 +# +# Startup script for registry-token-server +# + + +DESC="Docker Registry Token Server" +SERVICE="registry-token-server.service" +PIDFILE="/var/run/registry-token-server.pid" + + +status() +{ + if [ "`systemctl is-active registry-token-server.service`" = "active" ]; then + RETVAL=0 + echo "$DESC is running" + return + else + echo "$DESC is Not running" + RETVAL=1 + fi +} + +start() +{ + if [ -e $PIDFILE ]; then + PIDDIR=/proc/$(cat $PIDFILE) + if [ -d $PIDDIR ]; then + echo "$DESC already running." + return + else + echo "Removing stale PID file $PIDFILE" + rm -f $PIDFILE + fi + fi + + echo "Starting $SERVICE..." + + systemctl start $SERVICE + + if [ $? -eq 0 ]; then + echo "Started $SERVICE successfully" + RETVAL=0 + else + echo "$SERVICE failed!" + RETVAL=1 + fi + +} + +stop() +{ + echo -n "Stopping $SERVICE..." + systemctl stop $SERVICE + if [ $? -eq 0 ]; then + echo "$SERVICE stopped." + else + echo "failed to stop $SERVICE!" + fi + + if [ -e $PIDFILE ]; then + echo "Removing stale PID file $PIDFILE" + rm -f $PIDFILE + fi +} + + +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status + ;; + restart) + stop + start + ;; + *) + echo "Usage: $0 {start|stop|status|restart}" + exit 1 + ;; +esac + +exit $RETVAL diff --git a/puppet-manifests/src/modules/platform/manifests/dockerdistribution.pp b/puppet-manifests/src/modules/platform/manifests/dockerdistribution.pp index 5368cfe635..474dabab74 100644 --- a/puppet-manifests/src/modules/platform/manifests/dockerdistribution.pp +++ b/puppet-manifests/src/modules/platform/manifests/dockerdistribution.pp @@ -1,8 +1,11 @@ class platform::dockerdistribution::params ( + $registry_ks_endpoint = undef, ) {} class platform::dockerdistribution::config inherits ::platform::dockerdistribution::params { + include ::platform::params + include ::platform::kubernetes::params include ::platform::network::mgmt::params include ::platform::docker::params @@ -12,13 +15,12 @@ class platform::dockerdistribution::config # check insecure registries if $::platform::docker::params::insecure_registry { # insecure registry is true means unified registry was set - $insecure_registries = "\"${::platform::docker::params::k8s_registry}\", \"${docker_registry_ip}:9001\"" + $insecure_registries = "\"${::platform::docker::params::k8s_registry}\"" } else { - $insecure_registries = "\"${docker_registry_ip}:9001\"" + $insecure_registries = '' } - # currently docker registry is running insecure mode - # when proper authentication is implemented, this would go away + # for external docker registry running insecure mode file { '/etc/docker': ensure => 'directory', owner => 'root', @@ -33,7 +35,7 @@ class platform::dockerdistribution::config content => template('platform/insecuredockerregistry.conf.erb'), } - -> file { '/etc/docker-distribution/registry/config.yml': + file { '/etc/docker-distribution/registry/config.yml': ensure => present, owner => 'root', group => 'root', @@ -41,6 +43,14 @@ class platform::dockerdistribution::config content => template('platform/dockerdistribution.conf.erb'), } + file { '/etc/docker-distribution/registry/token_server.conf': + ensure => present, + owner => 'root', + group => 'root', + mode => '0644', + content => template('platform/registry-token-server.conf.erb'), + } + # copy the startup script to where it is supposed to be file {'docker_distribution_initd_script': ensure => 'present', @@ -48,27 +58,146 @@ class platform::dockerdistribution::config mode => '0755', source => "puppet:///modules/${module_name}/docker-distribution" } + + file {'registry_token_server_initd_script': + ensure => 'present', + path => '/etc/init.d/registry-token-server', + mode => '0755', + source => "puppet:///modules/${module_name}/registry-token-server" + } + + # self-signed certificate for registry use + # this needs to be generated here because the certificate + # need to know the registry ip address for SANs + if str2bool($::is_initial_config_primary) { + $shared_dir = $::platform::params::config_path + $certs_dir = '/etc/ssl/private' + + # create the certificate files + file { "${certs_dir}/registry-cert-extfile.cnf": + ensure => present, + owner => 'root', + group => 'root', + mode => '0400', + content => template('platform/registry-cert-extfile.erb'), + } + + -> exec { 'docker-registry-generate-cert': + command => "openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 \ + -keyout ${certs_dir}/registry-cert.key \ + -out ${certs_dir}/registry-cert.crt \ + -config ${certs_dir}/registry-cert-extfile.cnf", + logoutput => true + } + + -> exec { 'docker-registry-generate-pkcs1-cert-from-pkcs8': + command => "openssl rsa -in ${certs_dir}/registry-cert.key \ + -out ${certs_dir}/registry-cert-pkcs1.key", + logoutput => true + } + + # ensure permissions are set correctly + -> file { "${certs_dir}/registry-cert-pkcs1.key": + ensure => 'file', + owner => 'root', + group => 'root', + mode => '0400', + } + + -> file { "${certs_dir}/registry-cert.key": + ensure => 'file', + owner => 'root', + group => 'root', + mode => '0400', + } + + -> file { "${certs_dir}/registry-cert.crt": + ensure => 'file', + owner => 'root', + group => 'root', + mode => '0400', + } + + # delete the extfile used in certificate generation + -> exec { 'remove-registry-cert-extfile': + command => "rm ${certs_dir}/registry-cert-extfile.cnf" + } + + # copy certificates and keys to shared directory for second controller + # we do not need to worry about second controller being up at this point, + # since we have a is_initial_config_primary check + -> file { "${shared_dir}/registry-cert-pkcs1.key": + ensure => 'file', + owner => 'root', + group => 'root', + mode => '0400', + source => "${certs_dir}/registry-cert-pkcs1.key", + } + + -> file { "${shared_dir}/registry-cert.key": + ensure => 'file', + owner => 'root', + group => 'root', + mode => '0400', + source => "${certs_dir}/registry-cert.key", + } + + -> file { "${shared_dir}/registry-cert.crt": + ensure => 'file', + owner => 'root', + group => 'root', + mode => '0400', + source => "${certs_dir}/registry-cert.crt", + } + + # copy the certificate to docker certificates directory, + # which makes docker trust that specific certificate + # this is required for self-signed and also if the user does + # not have a certificate signed by a "default" CA + + -> file { '/etc/docker/certs.d': + ensure => 'directory', + owner => 'root', + group => 'root', + mode => '0700', + } + + -> file { "/etc/docker/certs.d/${docker_registry_ip}:9001": + ensure => 'directory', + owner => 'root', + group => 'root', + mode => '0700', + } + + -> file { "/etc/docker/certs.d/${docker_registry_ip}:9001/registry-cert.crt": + ensure => 'file', + owner => 'root', + group => 'root', + mode => '0400', + source => "${certs_dir}/registry-cert.crt", + } + } + } # compute also needs the "insecure" flag in order to deploy images from -# the registry. This will go away when proper authentication is implemented +# the registry. This is needed for insecure external registry class platform::dockerdistribution::compute inherits ::platform::dockerdistribution::params { + include ::platform::kubernetes::params + include ::platform::network::mgmt::params include ::platform::docker::params - $docker_registry_ip = $::platform::network::mgmt::params::controller_address - # check insecure registries if $::platform::docker::params::insecure_registry { # insecure registry is true means unified registry was set - $insecure_registries = "\"${::platform::docker::params::k8s_registry}\", \"${docker_registry_ip}:9001\"" + $insecure_registries = "\"${::platform::docker::params::k8s_registry}\"" } else { - $insecure_registries = "\"${docker_registry_ip}:9001\"" + $insecure_registries = '' } - # currently docker registry is running insecure mode - # when proper authentication is implemented, this would go away + # for external docker registry running insecure mode file { '/etc/docker': ensure => 'directory', owner => 'root', @@ -86,8 +215,23 @@ class platform::dockerdistribution::compute class platform::dockerdistribution inherits ::platform::dockerdistribution::params { + include ::platform::kubernetes::params include platform::dockerdistribution::config Class['::platform::docker::config'] -> Class[$name] } + +class platform::dockerdistribution::reload { + platform::sm::restart {'registry-token-server': } + platform::sm::restart {'docker-distribution': } +} + +# this does not update the config right now +# the run time is only used to restart the token server and registry +class platform::dockerdistribution::runtime { + + class {'::platform::dockerdistribution::reload': + stage => post + } +} diff --git a/puppet-manifests/src/modules/platform/manifests/sm.pp b/puppet-manifests/src/modules/platform/manifests/sm.pp index 64404fdb5d..47f114164e 100644 --- a/puppet-manifests/src/modules/platform/manifests/sm.pp +++ b/puppet-manifests/src/modules/platform/manifests/sm.pp @@ -1046,6 +1046,11 @@ class platform::sm command => "sm-configure service_instance docker-distribution docker-distribution \"\"", } + # Docker Registry Token Server + exec { 'Configure Docker Registry Token Server': + command => "sm-configure service_instance registry-token-server registry-token-server \"\"", + } + if $system_mode == 'duplex-direct' or $system_mode == 'simplex' { exec { 'Configure Platform NFS': command => "sm-configure service_instance platform-nfs-ip platform-nfs-ip \"ip=${platform_nfs_ip_param_ip},cidr_netmask=${platform_nfs_ip_param_mask},nic=${mgmt_ip_interface},arp_count=7,dc=yes\"", @@ -1188,6 +1193,14 @@ class platform::sm command => 'sm-provision service docker-distribution', } + # Configure Docker Registry Token Server + exec { 'Provision Docker Registry Token Server (service-group-member)': + command => 'sm-provision service-group-member controller-services registry-token-server', + } + -> exec { 'Provision Docker Registry Token Server (service)': + command => 'sm-provision service registry-token-server', + } + # Barbican if $barbican_enabled { exec { 'Provision OpenStack - Barbican API (service-group-member)': diff --git a/puppet-manifests/src/modules/platform/templates/dockerdistribution.conf.erb b/puppet-manifests/src/modules/platform/templates/dockerdistribution.conf.erb index 6fd11227af..625e18602b 100644 --- a/puppet-manifests/src/modules/platform/templates/dockerdistribution.conf.erb +++ b/puppet-manifests/src/modules/platform/templates/dockerdistribution.conf.erb @@ -10,8 +10,8 @@ storage: http: addr: <%= @docker_registry_ip %>:9001 tls: - certificate: /etc/ssl/private/self-signed-server-cert.pem - key: /etc/ssl/private/self-signed-server-cert.pem + certificate: /etc/ssl/private/registry-cert.crt + key: /etc/ssl/private/registry-cert.key headers: X-Content-Type-Options: [nosniff] health: @@ -19,3 +19,9 @@ health: enabled: true interval: 10s threshold: 3 +auth: + token: + realm: https://<%= @docker_registry_ip %>:9002/token/ + service: <%= @docker_registry_ip %>:9001 + issuer: bird-token-server + rootcertbundle: /etc/ssl/private/registry-cert.crt diff --git a/puppet-manifests/src/modules/platform/templates/registry-cert-extfile.erb b/puppet-manifests/src/modules/platform/templates/registry-cert-extfile.erb new file mode 100644 index 0000000000..c7b85d28e4 --- /dev/null +++ b/puppet-manifests/src/modules/platform/templates/registry-cert-extfile.erb @@ -0,0 +1,10 @@ +[req] +prompt = no +x509_extensions = v3_req +distinguished_name = dn +[dn] +CN = <%= @docker_registry_ip %> +[v3_req] +subjectAltName = @alt_names +[alt_names] +IP = <%= @docker_registry_ip %> diff --git a/puppet-manifests/src/modules/platform/templates/registry-token-server.conf.erb b/puppet-manifests/src/modules/platform/templates/registry-token-server.conf.erb new file mode 100644 index 0000000000..610d6ac0d4 --- /dev/null +++ b/puppet-manifests/src/modules/platform/templates/registry-token-server.conf.erb @@ -0,0 +1,7 @@ +REGISTRY_TOKEN_SERVER_ADDR=<%= @docker_registry_ip %>:9002 +REGISTRY_TOKEN_SERVER_ISSUER=bird-token-server +REGISTRY_TOKEN_SERVER_KS_ENDPOINT=<%= @registry_ks_endpoint %> +REGISTRY_TOKEN_SERVER_TLSCERT=/etc/ssl/private/registry-cert.crt +REGISTRY_TOKEN_SERVER_TLSKEY=/etc/ssl/private/registry-cert.key +REGISTRY_TOKEN_SERVER_REALM=https://<%= @docker_registry_ip %>:9002/token/ +REGISTRY_TOKEN_SERVER_KEY=/etc/ssl/private/registry-cert-pkcs1.key diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/certificate_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/certificate_shell.py index b71bc2f2fd..8f97fb3f83 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/certificate_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/certificate_shell.py @@ -69,7 +69,8 @@ def do_certificate_list(cc, args): help='The passphrase for the PEM file') @utils.arg('-m', '--mode', metavar='', - help="optional mode: 'tpm_mode', 'murano', 'murano_ca'. " + help="optional mode: 'tpm_mode', 'murano', 'murano_ca'," + "'docker_registry'. " "Default is 'ssl'.") def do_certificate_install(cc, args): """Install certificate.""" diff --git a/sysinv/sysinv/sysinv/setup.cfg b/sysinv/sysinv/sysinv/setup.cfg index f4188b65fe..b59f8846c2 100644 --- a/sysinv/sysinv/sysinv/setup.cfg +++ b/sysinv/sysinv/sysinv/setup.cfg @@ -70,8 +70,9 @@ systemconfig.puppet_plugins = 030_smapi = sysinv.puppet.smapi:SmPuppet 031_fm = sysinv.puppet.fm:FmPuppet 032_swift = sysinv.puppet.swift:SwiftPuppet - 033_service_parameter = sysinv.puppet.service_parameter:ServiceParamPuppet - 034_barbican = sysinv.puppet.barbican:BarbicanPuppet + 033_barbican = sysinv.puppet.barbican:BarbicanPuppet + 034_dockerdistribution = sysinv.puppet.dockerdistribution:DockerDistributionPuppet + 099_service_parameter = sysinv.puppet.service_parameter:ServiceParamPuppet systemconfig.helm_plugins = aodh = sysinv.helm.aodh:AodhHelm diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/certificate.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/certificate.py index 372fe59f23..f6e39b82e9 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/certificate.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/certificate.py @@ -268,6 +268,7 @@ class CertificateController(rest.RestController): tpm_mode: install certificate to tpm devices for ssl murano: install certificate for rabbit-murano murano_ca: install ca certificate for rabbit-murano + docker_registry: install certificate for docker registry """ log_start = cutils.timestamped("certificate_do_post_start") @@ -297,7 +298,8 @@ class CertificateController(rest.RestController): system = pecan.request.dbapi.isystem_get_one() capabilities = system.capabilities - if not mode.startswith(constants.CERT_MODE_MURANO): + if not mode.startswith(constants.CERT_MODE_MURANO) and \ + mode != constants.CERT_MODE_DOCKER_REGISTRY: system_https_enabled = capabilities.get('https_enabled', False) if system_https_enabled is False or system_https_enabled == 'n': msg = "No certificates have been added, https is not enabled." diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index 197ab938e9..2e19a4a7e3 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -1329,6 +1329,17 @@ MURANO_CERT_KEY_FILE = os.path.join(CERT_MURANO_DIR, CERT_KEY_FILE) MURANO_CERT_FILE = os.path.join(CERT_MURANO_DIR, CERT_FILE) MURANO_CERT_CA_FILE = os.path.join(CERT_MURANO_DIR, CERT_CA_FILE) +DOCKER_REGISTRY_CERT_FILE = os.path.join(SSL_CERT_DIR, "registry-cert.crt") +DOCKER_REGISTRY_KEY_FILE = os.path.join(SSL_CERT_DIR, "registry-cert.key") +DOCKER_REGISTRY_PKCS1_KEY_FILE = os.path.join(SSL_CERT_DIR, + "registry-cert-pkcs1.key") +DOCKER_REGISTRY_CERT_FILE_SHARED = os.path.join(tsc.CONFIG_PATH, + "registry-cert.crt") +DOCKER_REGISTRY_KEY_FILE_SHARED = os.path.join(tsc.CONFIG_PATH, + "registry-cert.key") +DOCKER_REGISTRY_PKCS1_KEY_FILE_SHARED = os.path.join(tsc.CONFIG_PATH, + "registry-cert-pkcs1.key") + SSL_CERT_CA_DIR = "/etc/ssl/certs/" SSL_CERT_CA_FILE = os.path.join(SSL_CERT_CA_DIR, CERT_CA_FILE) SSL_CERT_CA_FILE_SHARED = os.path.join(tsc.CONFIG_PATH, CERT_CA_FILE) @@ -1338,11 +1349,13 @@ CERT_MODE_SSL_CA = 'ssl_ca' CERT_MODE_TPM = 'tpm_mode' CERT_MODE_MURANO = 'murano' CERT_MODE_MURANO_CA = 'murano_ca' +CERT_MODE_DOCKER_REGISTRY = 'docker_registry' CERT_MODES_SUPPORTED = [CERT_MODE_SSL, CERT_MODE_SSL_CA, CERT_MODE_TPM, CERT_MODE_MURANO, - CERT_MODE_MURANO_CA] + CERT_MODE_MURANO_CA, + CERT_MODE_DOCKER_REGISTRY] # CONFIG file permissions CONFIG_FILE_PERMISSION_ROOT_READ_ONLY = 0o400 diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index bfce9b634f..2c28ec858e 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -94,6 +94,7 @@ from sysinv.openstack.common.gettextutils import _ from sysinv.puppet import common as puppet_common from sysinv.puppet import puppet from sysinv.helm import helm +from sysinv.helm import common as helm_common MANAGER_TOPIC = 'sysinv.conductor_manager' @@ -10154,11 +10155,14 @@ class ConductorManager(service.PeriodicService): alarm.entity_instance_id) @staticmethod - def _extract_keys_from_pem(mode, pem_contents, passphrase=None): + def _extract_keys_from_pem(mode, pem_contents, cert_format, + passphrase=None): """Extract keys from the pem contents - :param mode: mode one of: ssl, tpm_mode, murano, murano_ca + :param mode: mode one of: ssl, tpm_mode, murano, murano_ca, + docker_registry :param pem_contents: pem_contents + :param cert_format: serialization.PrivateFormat :param passphrase: passphrase for PEM file :returns: private_bytes, public_bytes, signature @@ -10178,6 +10182,7 @@ class ConductorManager(service.PeriodicService): if mode in [constants.CERT_MODE_SSL, constants.CERT_MODE_TPM, constants.CERT_MODE_MURANO, + constants.CERT_MODE_DOCKER_REGISTRY, ]: private_mode = True @@ -10205,7 +10210,7 @@ class ConductorManager(service.PeriodicService): private_bytes = private_key.private_bytes( encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, + format=cert_format, encryption_algorithm=serialization.NoEncryption()) signature = mode + '_' + str(cert.serial_number) @@ -10265,6 +10270,16 @@ class ConductorManager(service.PeriodicService): except OSError: pass + def _get_registry_floating_address(self): + """gets the registry floating address. Currently this is mgmt + """ + registry_network = self.dbapi.network_get_by_type( + constants.NETWORK_TYPE_MGMT) + registry_network_addr_pool = self.dbapi.address_pool_get( + registry_network.pool_uuid) + addr = registry_network_addr_pool.floating_address + return addr + def config_certificate(self, context, pem_contents, config_dict): """Configure certificate with the supplied data. @@ -10285,7 +10300,9 @@ class ConductorManager(service.PeriodicService): LOG.info("config_certificate mode=%s file=%s" % (mode, certificate_file)) private_bytes, public_bytes, signature = \ - self._extract_keys_from_pem(mode, pem_contents, passphrase) + self._extract_keys_from_pem(mode, pem_contents, + serialization.PrivateFormat.PKCS8, + passphrase) personalities = [constants.CONTROLLER] tpm = None @@ -10396,6 +10413,77 @@ class ConductorManager(service.PeriodicService): 'permissions': constants.CONFIG_FILE_PERMISSION_DEFAULT, } self._config_update_file(context, config_uuid, config_dict) + elif mode == constants.CERT_MODE_DOCKER_REGISTRY: + LOG.info("Docker registry certificate install") + # docker registry requires a PKCS1 key for the token server + pkcs1_private_bytes, pkcs1_public_bytes, pkcs1_signature = \ + self._extract_keys_from_pem(mode, pem_contents, + serialization.PrivateFormat + .TraditionalOpenSSL, passphrase) + + # install certificate, key, and pkcs1 key to controllers + config_uuid = self._config_update_hosts(context, personalities) + key_path = constants.DOCKER_REGISTRY_KEY_FILE + cert_path = constants.DOCKER_REGISTRY_CERT_FILE + pkcs1_key_path = constants.DOCKER_REGISTRY_PKCS1_KEY_FILE + + config_dict = { + 'personalities': personalities, + 'file_names': [key_path, cert_path, pkcs1_key_path], + 'file_content': {key_path: private_bytes, + cert_path: public_bytes, + pkcs1_key_path: pkcs1_private_bytes}, + 'nobackup': True, + 'permissions': constants.CONFIG_FILE_PERMISSION_ROOT_READ_ONLY, + } + self._config_update_file(context, config_uuid, config_dict) + + # copy certificate to shared directory + with os.fdopen(os.open(constants.DOCKER_REGISTRY_CERT_FILE_SHARED, + os.O_CREAT | os.O_WRONLY, + constants.CONFIG_FILE_PERMISSION_ROOT_READ_ONLY), + 'wb') as f: + f.write(public_bytes) + with os.fdopen(os.open(constants.DOCKER_REGISTRY_KEY_FILE_SHARED, + os.O_CREAT | os.O_WRONLY, + constants.CONFIG_FILE_PERMISSION_ROOT_READ_ONLY), + 'wb') as f: + f.write(private_bytes) + with os.fdopen(os.open(constants.DOCKER_REGISTRY_PKCS1_KEY_FILE_SHARED, + os.O_CREAT | os.O_WRONLY, + constants.CONFIG_FILE_PERMISSION_ROOT_READ_ONLY), + 'wb') as f: + f.write(pkcs1_private_bytes) + + config_uuid = self._config_update_hosts(context, personalities) + config_dict = { + "personalities": personalities, + "classes": ['platform::dockerdistribution::runtime'] + } + self._config_apply_runtime_manifest(context, + config_uuid, + config_dict) + + self._remove_certificate_file(mode, certificate_file) + + # install docker certificate on controllers and workers + registry_full_address = self._get_registry_floating_address() + ":" + helm_common.REGISTRY_PORT + docker_cert_path = os.path.join("/etc/docker/certs.d", + registry_full_address, + "registry-cert.crt") + + personalities = [constants.CONTROLLER, + constants.WORKER] + config_uuid = self._config_update_hosts(context, + personalities) + config_dict = { + 'personalities': personalities, + 'file_names': [docker_cert_path], + 'file_content': public_bytes, + 'nobackup': True, + 'permissions': constants.CONFIG_FILE_PERMISSION_ROOT_READ_ONLY, + } + self._config_update_file(context, config_uuid, config_dict) else: msg = "config_certificate unexpected mode=%s" % mode LOG.warn(msg) @@ -10422,7 +10510,9 @@ class ConductorManager(service.PeriodicService): LOG.info("_config_selfsigned_certificate mode=%s file=%s" % (mode, certificate_file)) private_bytes, public_bytes, signature = \ - self._extract_keys_from_pem(mode, pem_contents, passphrase) + self._extract_keys_from_pem(mode, pem_contents, + serialization.PrivateFormat.PKCS8, + passphrase) personalities = [constants.CONTROLLER] diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/dockerdistribution.py b/sysinv/sysinv/sysinv/sysinv/puppet/dockerdistribution.py new file mode 100644 index 0000000000..ae5a1970d3 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/puppet/dockerdistribution.py @@ -0,0 +1,19 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from sysinv.puppet import base + + +class DockerDistributionPuppet(base.BasePuppet): + """Class to encapsulate puppet operations for docker distribution""" + + def get_system_config(self): + config = { + 'platform::dockerdistribution::params::registry_ks_endpoint': + self._operator.keystone.get_auth_uri() + '/v3', + } + + return config diff --git a/workerconfig/workerconfig/worker_config b/workerconfig/workerconfig/worker_config index 9814cc5ec1..83dd28eccc 100644 --- a/workerconfig/workerconfig/worker_config +++ b/workerconfig/workerconfig/worker_config @@ -249,6 +249,17 @@ start() then fatal_error "This node is running a different load than the active controller and must be reinstalled" fi + + # Install docker certificate if required + # this is management network for now + REGISTRY_IP=$(get_ip controller) + mkdir -p /etc/docker/certs.d/$REGISTRY_IP:9001/ + chmod 700 /etc/docker/certs.d/$REGISTRY_IP:9001/ + cp $CONFIG_DIR/registry-cert.crt /etc/docker/certs.d/$REGISTRY_IP:9001/registry-cert.crt + if [ $? -ne 0 ] + then + fatal_error "Unable to copy $CONFIG_DIR/registry-cert.crt to docker dir" + fi fi # banner customization always returns 0, success: