Support shared LDAP share in region config

This patch enhanced region configuration to support LDAP as shared
service optionally for both IPv4 and IPV6 management network.
By sharing LDAP service, management of system users can be
centralized at primary region.

A LDAP_SERVICE_URL=<[ldap://]ip_address[:port]> can be specified in the
[SHARED_SERVICES] section of region configuration file, the secondary
regions will then be setup and share the LDAP service running at the
specified URL of the primary region. If no LDAP_SERVICE_URL is
specified in the [SHARED_SERVICES] section, the secondary regions will
setup and use local LDAP service running at the region's controllers.

Decouple NSLCD from the open-ldap SM service and manage it by PMOND
instead. This is needed because in the Shared LDAP case, we deprovision
the open-ldap service on the Secondary Region which renders NSLCD
unmanaged.

Additionally, we allow the Secondary Region or Sub Clouds to bind
anonymously, but still need to support LDAP read operations in these
regions such as ldapfinger or lsldap. For this purpose, the ldapscripts
runtime library has been modified to allow anonymous binds during LDAP
search operations.

Change-Id: Ic9f4d157c0eab02a8dabbdae28d508d4aef05fa2
This commit is contained in:
Andy Ning 2018-04-12 15:28:27 -04:00 committed by Jack Ding
parent 8761006706
commit d8c8412cb4
8 changed files with 259 additions and 2 deletions

View File

@ -502,6 +502,9 @@ class ConfigAssistant():
self.mtce_ks_password = ""
self.nfv_ks_user_name = ""
self.nfv_ks_password = ""
self.ldap_region_name = ""
self.ldap_service_name = ""
self.ldap_service_uri = ""
# Subcloud config (only valid when region configured)
self.system_controller_subnet = None
@ -2694,6 +2697,15 @@ class ConfigAssistant():
if config.has_option('cREGION', 'GLANCE_PUBLIC_URI'):
self.glance_public_uri = config.get(
'cREGION', 'GLANCE_PUBLIC_URI')
if config.has_option('cREGION', 'LDAP_REGION_NAME'):
self.ldap_region_name = config.get(
'cREGION', 'LDAP_REGION_NAME')
if config.has_option('cREGION', 'LDAP_SERVICE_NAME'):
self.ldap_service_name = config.get(
'cREGION', 'LDAP_SERVICE_NAME')
if config.has_option('cREGION', 'LDAP_SERVICE_URI'):
self.ldap_service_uri = config.get(
'cREGION', 'LDAP_SERVICE_URI')
self.nova_ks_user_name = config.get(
'cREGION', 'NOVA_USER_NAME')
self.nova_ks_password = config.get(
@ -2981,6 +2993,9 @@ class ConfigAssistant():
print "Glance admin URI: " + self.glance_admin_uri
print "Glance internal URI: " + self.glance_internal_uri
print "Glance public URI: " + self.glance_public_uri
print "LDAP service name: " + self.ldap_service_name
print "LDAP region: " + self.ldap_region_name
print "LDAP service URI:" + self.ldap_service_uri
print "Nova user name: " + self.nova_ks_user_name
print "Nova service name: " + self.nova_service_name
print "Nova service type: " + self.nova_service_type
@ -3275,6 +3290,15 @@ class ConfigAssistant():
self.glance_internal_uri)
f.write("GLANCE_PUBLIC_URI=%s\n" %
self.glance_public_uri)
if self.ldap_service_name:
f.write("LDAP_SERVICE_NAME=%s\n" %
self.ldap_service_name)
if self.ldap_region_name:
f.write("LDAP_REGION_NAME=%s\n" %
self.ldap_region_name)
if self.ldap_service_uri:
f.write("LDAP_SERVICE_URI=%s\n" %
self.ldap_service_uri)
f.write("NOVA_USER_NAME=%s\n" %
self.nova_ks_user_name)
f.write("NOVA_PASSWORD=%s\n" %
@ -3724,6 +3748,16 @@ class ConfigAssistant():
'capabilities': capabilities}
client.sysinv.sm_service.service_create(**values)
# if ldap is a shared service
if self.ldap_service_uri:
capabilities = {'service_name': self.ldap_service_name}
capabilities.update({'service_uri': self.ldap_service_uri})
values = {'name': self.ldap_service_name,
'enabled': True,
'region_name': self.ldap_region_name,
'capabilities': capabilities}
client.sysinv.sm_service.service_create(**values)
# neutron service config
capabilities = {'service_name': self.neutron_service_name,
'service_type': self.neutron_service_type,

View File

@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0
import ConfigParser
import os
import subprocess
import sys
import textwrap
import time
@ -473,6 +474,19 @@ def validate_region_one_keystone_config(region_config, token, api_url, users,
endpointtype, expected, endpointtype, found))
def validate_region_one_ldap_config(region_config):
"""Validate ldap on region one by a ldap search"""
ldapserver_uri = region_config.get('SHARED_SERVICES', 'LDAP_SERVICE_URL')
cmd = ["ldapsearch", "-xH", ldapserver_uri,
"-b", "dc=cgcs,dc=local", "(objectclass=*)"]
try:
with open(os.devnull, "w") as fnull:
subprocess.check_call(cmd, stdout=fnull, stderr=fnull)
except subprocess.CalledProcessError:
raise ConfigFail("LDAP configuration error: not accessible")
def set_subcloud_config_defaults(region_config):
"""Set defaults in region_config for subclouds"""
@ -645,6 +659,12 @@ def configure_region(config_file, config_type=REGION_CONFIG):
user_config=user_config)
print "DONE"
# validate ldap if it is shared
if region_config.has_option('SHARED_SERVICES', 'LDAP_SERVICE_URL'):
print "Validating ldap configuration... ",
validate_region_one_ldap_config(region_config)
print "DONE"
# Create cgcs_config file
print "Creating config apply file... ",
try:

View File

@ -201,6 +201,14 @@ def create_cgcs_config_file(output_file, system_config,
cgcs_config.set('cREGION', 'GLANCE_INTERNAL_URI',
glance_internal_url)
# if ldap is a shared service
if (system_config.has_option('SHARED_SERVICES', 'LDAP_SERVICE_URL')):
ldap_service_url = system_config.get('SHARED_SERVICES',
'LDAP_SERVICE_URL')
cgcs_config.set('cREGION', 'LDAP_SERVICE_URI', ldap_service_url)
cgcs_config.set('cREGION', 'LDAP_SERVICE_NAME', 'open-ldap')
cgcs_config.set('cREGION', 'LDAP_REGION_NAME', region_1_name)
# The domains are not available in the validation phase
heat_admin_domain = system_config.get('REGION_2_SERVICES',
'HEAT_ADMIN_DOMAIN')

View File

@ -68,6 +68,8 @@ CINDER_V2_SERVICE_TYPE=volumev2
CINDER_V3_SERVICE_NAME=cinderv3
CINDER_V3_SERVICE_TYPE=volumev3
LDAP_SERVICE_URL=ldap://192.168.204.12:389
[REGION_2_SERVICES]
REGION_NAME=RegionTwo
NOVA_USER_NAME=novaTWO

View File

@ -116,6 +116,9 @@ KEYSTONE_PUBLIC_URI = http://10.10.10.2:8081/keystone/main/v2.0
GLANCE_ADMIN_URI = http://192.168.204.12:9292/v2
GLANCE_PUBLIC_URI = http://10.10.10.2:9292/v2
GLANCE_INTERNAL_URI = http://192.168.204.12:9292/v2
LDAP_SERVICE_URI = ldap://192.168.204.12:389
LDAP_SERVICE_NAME = open-ldap
LDAP_REGION_NAME = RegionOne
HEAT_ADMIN_DOMAIN_NAME = heat
[cAUTHENTICATION]

View File

@ -58,8 +58,11 @@ class platform::ldap::server::local
onlyif => '/usr/bin/test -e /etc/openldap/slapd.conf'
}
file { "/usr/local/etc/ldapscripts/ldapscripts.passwd":
content => $admin_pw,
# don't populate the adminpw if binding anonymously
if ! $bind_anonymous {
file { "/usr/local/etc/ldapscripts/ldapscripts.passwd":
content => $admin_pw,
}
}
file { "/usr/share/cracklib/cracklib-small":
@ -99,6 +102,14 @@ class platform::ldap::client
hasstatus => true,
hasrestart => true,
}
if $::personality == 'controller' {
file { "/usr/local/etc/ldapscripts/ldapscripts.conf":
ensure => 'present',
replace => true,
content => template('platform/ldapscripts.conf.erb'),
}
}
}
class platform::ldap::bootstrap

View File

@ -0,0 +1,163 @@
# Copyright (C) 2005 Gana<6E>l LAPLANCHE - Linagora
# Copyright (C) 2006-2013 Gana<6E>l LAPLANCHE
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# LDAP server
SERVER="ldap://<%= @ldapserver_host %>"
# Suffixes
SUFFIX="dc=cgcs,dc=local" # Global suffix
GSUFFIX="ou=Group" # Groups ou (just under $SUFFIX)
USUFFIX="ou=People" # Users ou (just under $SUFFIX)
MSUFFIX="ou=Machines" # Machines ou (just under $SUFFIX)
# Authentication type
# If empty, use simple authentication
# Else, use the value as an SASL authentication mechanism
SASLAUTH=""
#SASLAUTH="GSSAPI"
# Simple authentication parameters
# The following BIND* parameters are ignored if SASLAUTH is set
#BINDDN="cn=proxyuser,dc=example,dc=com"
# The following file contains the raw password of the BINDDN
# Create it with something like : echo -n 'secret' > $BINDPWDFILE
# WARNING !!!! Be careful not to make this file world-readable
#BINDPWDFILE="/foo/ldapscripts.passwd"
<%- if @bind_anonymous != true -%>
BINDDN="cn=ldapadmin,dc=cgcs,dc=local"
BINDPWDFILE="/usr/local/etc/ldapscripts/ldapscripts.passwd"
<%- end -%>
# For older versions of OpenLDAP, it is still possible to use
# unsecure command-line passwords by defining the following option
# AND commenting the previous one (BINDPWDFILE takes precedence)
#BINDPWD="secret"
# Start with these IDs *if no entry found in LDAP*
GIDSTART="10000" # Group ID
UIDSTART="10000" # User ID
MIDSTART="20000" # Machine ID
# Group membership management
# ObjectCLass used for groups
# Possible values : posixGroup, groupOfNames, groupOfUniqueNames (case-sensitive !)
# Warning : when using groupOf*, be sure to be compliant with RFC 2307bis (AUXILIARY posixGroup).
# Also, do not mix posixGroup and groupOf* entries up in you directory as, within RFC 2307bis,
# the former is a subset of the latter. The ldapscripts wouldn't cope well with this configuration.
GCLASS="posixGroup" # Leave "posixGroup" here if not sure !
# When using groupOfNames or groupOfUniqueNames, creating a group requires an initial
# member. Specify it below, you will be able to remove it once groups are populated.
#GDUMMYMEMBER="uid=dummy,$USUFFIX,$SUFFIX"
# User properties
USHELL="/bin/sh"
UHOMES="/home/%u" # You may use %u for username here
CREATEHOMES="no" # Create home directories and set rights ?
HOMESKEL="/etc/skel" # Directory where the skeleton files are located. Ignored if undefined or nonexistant.
HOMEPERMS="700" # Default permissions for home directories
# User passwords generation
# Command-line used to generate a password for added users.
# You may use %u for username here ; special value "<ask>" will ask for a password interactively
# WARNING !!!! This is evaluated, everything specified here will be run !
# WARNING(2) !!!! Some systems (Linux) use a blocking /dev/random (waiting for enough entropy).
# In this case, consider using /dev/urandom instead.
#PASSWORDGEN="cat /dev/random | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c8"
#PASSWORDGEN="pwgen"
#PASSWORDGEN="echo changeme"
PASSWORDGEN="echo %u"
#PASSWORDGEN="<ask>"
# User passwords recording
# you can keep trace of generated passwords setting PASSWORDFILE and RECORDPASSWORDS
# (useful when performing a massive creation / net rpc vampire)
# WARNING !!!! DO NOT FORGET TO DELETE THE GENERATED FILE WHEN DONE !
# WARNING !!!! DO NOT FORGET TO TURN OFF RECORDING WHEN DONE !
RECORDPASSWORDS="no"
PASSWORDFILE="/var/log/ldapscripts_passwd.log"
# Where to log
LOGFILE="/var/log/ldapscripts.log"
# Temporary folder
TMPDIR="/tmp"
# Various binaries used within the scripts
# Warning : they also use uuencode, date, grep, sed, cut, which...
# Please check they are installed before using these scripts
# Note that many of them should come with your OS
# OpenLDAP client commands
LDAPSEARCHBIN="/usr/bin/ldapsearch"
LDAPADDBIN="/usr/bin/ldapadd"
LDAPDELETEBIN="/usr/bin/ldapdelete"
LDAPMODIFYBIN="/usr/bin/ldapmodify"
LDAPMODRDNBIN="/usr/bin/ldapmodrdn"
LDAPPASSWDBIN="/usr/bin/ldappasswd"
# OpenLDAP client common additional options
# This allows for adding more configuration options to the OpenLDAP clients, e.g. '-ZZ' to enforce TLS
#LDAPBINOPTS="-ZZ"
# OpenLDAP ldapsearch-specific additional options
# The following option disables long-line wrapping (which makes the scripts bug
# when handling long lines). The option was introduced in OpenLDAP 2.4.24, so
# comment it if you are using OpenLDAP < 2.4.24.
LDAPSEARCHOPTS="-o ldif-wrap=no"
# And here is an example to activate paged results
#LDAPSEARCHOPTS="-E pr=500/noprompt"
# Character set conversion : $ICONVCHAR <-> UTF-8
# Comment ICONVBIN to disable UTF-8 conversion
# ICONVBIN="/usr/bin/iconv"
# ICONVCHAR=""
# Base64 decoding
# Comment UUDECODEBIN to disable Base64 decoding
#UUDECODEBIN="/usr/bin/uudecode"
# Getent command to use - choose the ones used
# on your system. Leave blank or comment for auto-guess.
# GNU/Linux
GETENTPWCMD="getent passwd"
GETENTGRCMD="getent group"
# FreeBSD
#GETENTPWCMD="pw usershow"
#GETENTGRCMD="pw groupshow"
# Auto
#GETENTPWCMD=""
#GETENTGRCMD=""
# You can specify custom LDIF templates here
# Leave empty to use default templates
# See *.template.sample for default templates
#GTEMPLATE="/path/to/ldapaddgroup.template"
#UTEMPLATE="/path/to/ldapadduser.template"
#MTEMPLATE="/path/to/ldapaddmachine.template"
GTEMPLATE="/usr/local/etc/ldapscripts/ldapaddgroup.template.cgcs"
UTEMPLATE="/usr/local/etc/ldapscripts/ldapadduser.template.cgcs"
UMTEMPLATE="/usr/local/etc/ldapscripts/ldapmoduser.template.cgcs"
STEMPLATE="/usr/local/etc/ldapscripts/ldapaddsudo.template.cgcs"
SMTEMPLATE="/usr/local/etc/ldapscripts/ldapmodsudo.template.cgcs"
MTEMPLATE=""

View File

@ -13,6 +13,7 @@ from . import base
class LdapPuppet(base.BasePuppet):
"""Class to encapsulate puppet operations for ldap configuration"""
SERVICE_NAME = 'open-ldap'
def get_secure_static_config(self):
password = self._generate_random_password()
@ -53,6 +54,15 @@ class LdapPuppet(base.BasePuppet):
ldapserver_host = self._format_url_address(ldapserver_addr)
bind_anonymous = True
elif self._region_config():
service_config = self.get_service_config(self.SERVICE_NAME)
if service_config is not None:
ldapserver_remote = True
ldapserver_uri = service_config.capabilities.get('service_uri')
addr_index = ldapserver_uri.rfind('/')
ldapserver_host = ldapserver_uri[addr_index + 1:]
bind_anonymous = True
if host.personality != constants.CONTROLLER:
# if storage/compute, use bind anonymously
bind_anonymous = True
@ -80,3 +90,9 @@ class LdapPuppet(base.BasePuppet):
'platform::ldap::params::ldapserver_host': ldapserver_host,
'platform::ldap::params::bind_anonymous': bind_anonymous,
}
def get_service_config(self, service):
configs = self.context.setdefault('_service_configs', {})
if service not in configs:
configs[service] = self._get_service(service)
return configs[service]