Add new role, 'debops.nslcd'

parent 53cf0eec
......@@ -922,6 +922,14 @@ stages:
JANE_DIFF_PATTERN: '.*/debops.nodejs/.*'
JANE_LOG_PATTERN: '\[debops\.nodejs\]'
'nslcd role':
<<: *test_role_2nd_deps
variables:
JANE_TEST_PLAY: '${DEBOPS_PLAYBOOKS}/service/nslcd.yml'
JANE_INVENTORY_GROUPS: 'debops_service_nslcd'
JANE_DIFF_PATTERN: '.*/debops.nslcd/.*'
JANE_LOG_PATTERN: '\[debops\.nslcd\]'
'nsswitch role':
<<: *test_role_no_deps
variables:
......
......@@ -32,6 +32,9 @@ Added
roles, playbooks, and users via Ansible inventory. The role is included in
the ``common.yml`` playbook, but is disabled by default.
- :ref:`debops.nslcd` role can be used to configure LDAP lookups for NSS and
PAM services on a Linux host.
- [debops.nginx] The role will automatically generate configuration which
redirects short hostnames or subdomains to their FQDN equivalents. This
allows HTTP clients to reach websites by specifying their short names via DNS
......
---
- name: Manage nslcd service
hosts: [ 'debops_service_nslcd' ]
become: True
environment: '{{ inventory__environment | d({})
| combine(inventory__group_environment | d({}))
| combine(inventory__host_environment | d({})) }}'
roles:
- role: debops.python
tags: [ 'role::python', 'skip::python', 'role::ldap' ]
python__dependent_packages3:
- '{{ ldap__python__dependent_packages3 }}'
python__dependent_packages2:
- '{{ ldap__python__dependent_packages2 }}'
- role: debops.ldap
tags: [ 'role::ldap', 'skip::ldap' ]
ldap__dependent_tasks:
- '{{ nslcd__ldap__dependent_tasks }}'
- role: debops.nslcd
tags: [ 'role::nslcd', 'skip::nslcd' ]
- role: debops.nsswitch
tags: [ 'role::nsswitch', 'skip::nsswitch' ]
nsswitch__dependent_services:
- '{{ nslcd__nsswitch__dependent_services }}'
......@@ -24,6 +24,8 @@
- import_playbook: slapd.yml
- import_playbook: nslcd.yml
- import_playbook: iscsi.yml
- import_playbook: cryptsetup.yml
......
../service/nslcd.yml
\ No newline at end of file
debops.nslcd - Configure LDAP support for NSS and PAM lookups
Copyright (C) 2019 Maciej Delmanowski <drybjed@gmail.com>
Copyright (C) 2019 DebOps Project https://debops.org/
This Ansible role is part of DebOps.
DebOps is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
DebOps 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 DebOps. If not, see http://www.gnu.org/licenses/.
---
# .. vim: foldmarker=[[[,]]]:foldmethod=marker
# debops.nslcd default variables
# ==============================
# .. contents:: Sections
# :local:
#
# .. include:: ../../../includes/global.rst
# APT packages [[[
# ----------------
# .. envvar:: nslcd__base_packages [[[
#
# List of APT packages required for LDAP lookups via NSS and PAM.
nslcd__base_packages: [ 'libpam-ldapd', 'libnss-ldapd',
'nslcd', 'nslcd-utils',
'openssl', 'ca-certificates' ]
# ]]]
# .. envvar:: nslcd__packages [[[
#
# List of additional APT packages to install with :command:`nslcd` package.
nslcd__packages: []
# ]]]
# ]]]
# UNIX environment [[[
# --------------------
# .. envvar:: nslcd__user [[[
#
# Name of the UNIX system account which will be used to perform LDAP lookups
# via the :command:`nslcd` service.
nslcd__user: 'nslcd'
# ]]]
# .. envvar:: nslcd__group [[[
#
# Name of the UNIX system group which will be used to perform LDAP lookups via
# the :command:`nslcd` service.
nslcd__group: 'nslcd'
# ]]]
# .. envvar:: nslcd__mkhomedir_umask [[[
#
# Default umask for new home directories created by the ``pam_mkhomedir`` PAM
# module.
nslcd__mkhomedir_umask: '{{ ansible_local.core.homedir_umask
if (ansible_local|d() and ansible_local.core|d() and
ansible_local.core.homedir_umask|d())
else "0027" }}'
# ]]]
# ]]]
# LDAP environment [[[
# --------------------
# .. envvar:: nslcd__ldap_base_dn [[[
#
# The base Distinguished Name which should be used to create Distinguished
# Names of the LDAP directory objects, defined as a YAML list.
nslcd__ldap_base_dn: '{{ ansible_local.ldap.base_dn
if (ansible_local|d() and ansible_local.ldap|d() and
ansible_local.ldap.base_dn|d())
else [] }}'
# ]]]
# .. envvar:: nslcd__ldap_device_dn [[[
#
# The Distinguished Name of the current host LDAP object, defined as a YAML
# list. It will be used as a base for the :command:`nslcd` service account LDAP
# object. If the list is empty, the role will not create the account LDAP
# object automatically.
nslcd__ldap_device_dn: '{{ ansible_local.ldap.device_dn
if (ansible_local|d() and ansible_local.ldap|d() and
ansible_local.ldap.device_dn|d())
else [] }}'
# ]]]
# .. envvar:: nslcd__ldap_self_rdn [[[
#
# The Relative Distinguished Name of the account LDAP object used by the
# :command:`nslcd` service to access the LDAP directory.
nslcd__ldap_self_rdn: '{{ "uid=" + nslcd__user }}'
# ]]]
# .. envvar:: nslcd__ldap_self_object_classes [[[
#
# List of the LDAP object classes which will be used to create the LDAP object
# used by the :command:`nslcd` service to access the LDAP directory.
nslcd__ldap_self_object_classes: [ 'account', 'simpleSecurityObject' ]
# ]]]
# .. envvar:: nslcd__ldap_self_attributes [[[
#
# YAML dictionary that defines the attributes of the LDAP object used by the
# :command:`nslcd` service to access the LDAP directory.
nslcd__ldap_self_attributes:
uid: '{{ nslcd__ldap_self_rdn.split("=")[1] }}'
userPassword: '{{ nslcd__ldap_bindpw }}'
host: '{{ [ ansible_fqdn, ansible_hostname ] | unique }}'
description: 'Account used by the "nslcd" service to access the LDAP directory'
# ]]]
# .. envvar:: nslcd__ldap_binddn [[[
#
# The Distinguished Name of the account LDAP object used by the
# :command:`nslcd` service to bind to the LDAP directory.
nslcd__ldap_binddn: '{{ ([ nslcd__ldap_self_rdn ] + nslcd__ldap_device_dn) | join(",") }}'
# ]]]
# .. envvar:: nslcd__ldap_bindpw [[[
#
# The password stored in the account LDAP object used by the :command:`nslcd`
# service to bind to the LDAP directory.
nslcd__ldap_bindpw: '{{ lookup("password", secret + "/ldap/credentials/"
+ nslcd__ldap_binddn | to_uuid + ".password length=32") }}'
# ]]]
# ]]]
# Service configuration [[[
# -------------------------
# These variables define the contents of the :file:`/etc/nslcd.conf`
# configuration file. See :ref:`nslcd__ref_configuration` for more details, and
# :man:`nslcd.conf(5)` for possible configuration parameters.
# .. envvar:: nslcd__default_configuration [[[
#
# The default :command:`nslcd` configuration options defined by the role.
nslcd__default_configuration:
- name: 'uid'
comment: 'The user and group nslcd should run as.'
value: '{{ nslcd__user }}'
- name: 'gid'
value: '{{ nslcd__group }}'
- name: 'uri'
comment: 'The location at which the LDAP server(s) should be reachable.'
value: '{{ ansible_local.ldap.uri
if (ansible_local|d() and ansible_local.ldap|d() and
ansible_local.ldap.uri|d())
else "" }}'
- name: 'base'
comment: 'The search base that will be used for all queries.'
value: '{{ nslcd__ldap_base_dn | join(",") }}'
- name: 'ldap_version'
comment: 'The LDAP protocol version to use.'
value: '3'
state: 'comment'
- name: 'binddn'
comment: 'The DN to bind with for normal lookups.'
value: '{{ nslcd__ldap_binddn }}'
- name: 'bindpw'
value: '{{ nslcd__ldap_bindpw }}'
- name: 'rootpwmoddn'
comment: 'The DN used for password modifications by root.'
value: 'cn=admin,dc=example,dc=com'
state: 'comment'
- name: 'ssl'
comment: 'SSL options'
value: '{{ "start_tls"
if (ansible_local|d() and ansible_local.ldap|d() and
(ansible_local.ldap.start_tls|d())|bool)
else "on" }}'
- name: 'tls_reqcert'
value: 'demand'
- name: 'tls_cacertfile'
value: '/etc/ssl/certs/ca-certificates.crt'
- name: 'scope'
comment: 'The search scope.'
value: 'sub'
state: 'comment'
- name: 'nss_min_uid'
comment: |
First valid UID/GID number expected to be in the LDAP directory.
UIDs/GIDs lower than this value will be ignored.
value: '{{ ansible_local.ldap.uid_gid_min
if (ansible_local|d() and ansible_local.ldap|d() and
ansible_local.ldap.uid_gid_min|d())
else "10000" }}'
- name: 'map_group_id'
comment: |
Use the 'gid' attribute instead of 'cn' as the POSIX group name.
option: 'map'
map: 'group'
value: 'cn gid'
# ]]]
# .. envvar:: nslcd__configuration [[[
#
# The :command:`nslcd` configuration options defined on all hosts in the
# Ansible inventory.
nslcd__configuration: []
# ]]]
# .. envvar:: nslcd__group_configuration [[[
#
# The :command:`nslcd` configuration options defined on hosts in a specific
# Ansible inventory group.
nslcd__group_configuration: []
# ]]]
# .. envvar:: nslcd__host_configuration [[[
#
# The :command:`nslcd` configuration options defined on specific hosts in the
# Ansible inventory.
nslcd__host_configuration: []
# ]]]
# .. envvar:: nslcd__combined_configuration [[[
#
# The variable that combines other :command:`nslcd` configuration options and
# is used in the role template.
nslcd__combined_configuration: '{{ nslcd__default_configuration
+ nslcd__configuration
+ nslcd__group_configuration
+ nslcd__host_configuration }}'
# ]]]
# ]]]
# Configuration for other Ansible roles [[[
# -----------------------------------------
# .. envvar:: nslcd__ldap__dependent_tasks [[[
#
# Configuration for the :ref:`debops.ldap` Ansible role.
nslcd__ldap__dependent_tasks:
- name: 'Create nslcd account for {{ nslcd__ldap_device_dn | join(",") }}'
dn: '{{ nslcd__ldap_binddn }}'
objectClass: '{{ nslcd__ldap_self_object_classes }}'
attributes: '{{ nslcd__ldap_self_attributes }}'
no_log: True
state: '{{ "present" if nslcd__ldap_device_dn|d() else "ignore" }}'
# ]]]
# .. envvar:: nslcd__nsswitch__dependent_services [[[
#
# Configuration for the :ref:`debops.nsswitch` Ansible role.
nslcd__nsswitch__dependent_services: [ 'ldap' ]
# ]]]
# ]]]
---
dependencies:
- role: debops.secret
- role: debops.ansible_plugins
galaxy_info:
role_name: 'nslcd'
company: 'DebOps'
author: 'Maciej Delmanowski'
description: 'Configure LDAP support for NSS and PAM lookups'
license: 'GPL-3.0'
min_ansible_version: '2.7.0'
platforms:
- name: Ubuntu
versions:
- xenial
- bionic
- name: Debian
versions:
- jessie
- stretch
- buster
galaxy_tags:
- ldap
- nslcd
- nss
- pam
- system
---
- name: Configure pam-mkhomedir to create home directories
template:
src: 'usr/share/pam-configs/mkhomedir.j2'
dest: '/usr/share/pam-configs/mkhomedir'
mode: '0644'
register: nslcd__register_mkhomedir
- name: Enable mkhomedir PAM module
shell: pam-auth-update --package --enable mkhomedir 2>/dev/null
when: nslcd__register_mkhomedir is changed
- name: Install packages for nslcd support
package:
name: '{{ q("flattened", nslcd__base_packages + nslcd__packages) }}'
state: 'present'
register: nslcd__register_packages
until: nslcd__register_packages is succeeded
- name: Generate nslcd configuration
template:
src: 'etc/nslcd.conf.j2'
dest: '/etc/nslcd.conf'
group: '{{ nslcd__group }}'
mode: '0640'
register: nslcd__register_config
- name: Restart nslcd if its configuration was modified
service:
name: 'nslcd'
state: 'restarted'
when: nslcd__register_config is changed
- name: Make sure that Ansible local facts directory exists
file:
path: '/etc/ansible/facts.d'
state: 'directory'
mode: '0755'
- name: Save nslcd local facts
template:
src: 'etc/ansible/facts.d/nslcd.fact.j2'
dest: '/etc/ansible/facts.d/nslcd.fact'
mode: '0755'
register: nslcd__register_facts
- name: Update Ansible facts if they were modified
action: setup
when: nslcd__register_facts is changed
#!{{ ansible_python['executable'] }}
# {{ ansible_managed }}
from __future__ import print_function
from json import dumps, loads
from sys import exit
import os
def cmd_exists(cmd):
return any(
os.access(os.path.join(path, cmd), os.X_OK)
for path in os.environ["PATH"].split(os.pathsep)
)
output = {'installed': cmd_exists('nslcd')}
print(dumps(output, sort_keys=True, indent=4))
# {{ ansible_managed }}
# /etc/nslcd.conf
# nslcd configuration file. See nslcd.conf(5)
# for details.
{% for option in nslcd__combined_configuration | parse_kv_config %}
{% if option.state|d('present') not in [ 'absent', 'ignore' ] %}
{% if option.separator|bool and not option.comment|d() and not loop.first %}
{% endif %}
{% if option.comment|d() %}
{{ option.comment | regex_replace('\n$','') | comment(prefix='', postfix='') -}}
{% endif %}
{% set option_map = '' %}
{% if option.map|d() %}
{% set option_map = (' ' + option.map) %}
{% endif %}
{% if option.raw|d() %}
{% if option.state|d('present') == 'comment' %}
{{ option.raw | regex_replace('\n$','') | comment(prefix='', postfix='') -}}
{% else %}
{{ option.raw | regex_replace('\n$','') }}
{% endif %}
{% else %}
{{ '{}{}{} {}'.format(('#' if (option.state|d('present') == 'comment') else ''), (option.option | d(option.name)), option_map, (option.value if option.value is string else (option.value | selectattr("name", "defined") | map(attribute="name") | list | join(' ')))) }}
{% endif %}
{% endif %}
{% endfor %}
# {{ ansible_managed }}
# Enable support for libpam-mkhomedir in PAM
Name: activate mkhomedir
Default: yes
Priority: 900
Session-Type: Additional
Session:
required pam_mkhomedir.so umask={{ nslcd__mkhomedir_umask }} skel=/etc/skel
......@@ -112,6 +112,15 @@ Databases
- ``debops.phpmyadmin``
Directory services
------------------
- :ref:`debops.ldap`
- :ref:`debops.nslcd`
- :ref:`debops.nsswitch`
- :ref:`debops.slapd`
Encryption
----------
......@@ -268,6 +277,7 @@ System configuration
- :ref:`debops.machine`
- :ref:`debops.mount`
- :ref:`debops.netbase`
- :ref:`debops.nslcd`
- :ref:`debops.nsswitch`
- :ref:`debops.ntp`
- :ref:`debops.resources`
......
......@@ -22,6 +22,8 @@ Directory structure
- :envvar:`cn=host.example.org <ldap__device_self_rdn>` (:envvar:`conditional <ldap__device_enabled>`)
- :ref:`uid=nslcd <nslcd__ref_ldap_dit>` -> :ref:`debops.nslcd`
- :envvar:`ou=People <ldap__people_rdn>`
- :envvar:`ou=Groups <ldap__groups_rdn>`
- :envvar:`ou=Machines <ldap__machines_rdn>`
......@@ -40,10 +42,10 @@ Object Classes and Attributes
- :ref:`debops.ldap`: :envvar:`Object Classes <ldap__device_object_classes>`, :envvar:`Attributes <ldap__device_attributes>`
Parent node
-----------
Parent nodes
------------
There's no parent node defined for the :ref:`debops.ldap` Ansible role.
There are no parent nodes defined for the :ref:`debops.ldap` Ansible role.
Child nodes
......
Default variable details
========================
Some of the ``debops.nslcd`` default variables have more extensive
configuration than simple strings or lists, here you can find documentation and
examples for them.
.. _nslcd__ref_configuration:
nslcd__configuration
--------------------
The ``nslcd__*_configuration`` variables define the contents of the
:file:`/etc/nslcd.conf` configuration file. The variables are merged in order
defined by the :envvar:`nslcd__combined_configuration` variable, which allows
modification of the default configuration through the Ansible inventory. See
:man:`nslcd.conf(5)` for possible configuration parameters and their values.
Examples
~~~~~~~~
See :envvar:`nslcd__default_configuration` variable for an example of
existing configuration.
Limit UNIX accounts and groups that appear on the server based on the ``host``
attribute. The value can be:
- ``host.example.org`` or ``host`` (specific host)
- ``*.example.org`` (specific subdomain)
- ``*`` (all hosts)
.. code-block:: yaml
nslcd__configuration:
- name: 'filter_passwd_group'
comment: 'Limit which UNIX accounts and groups are present on a host'
raw: |
filter passwd (&(objectClass=posixAccount)(|(host={{ ansible_fqdn }})(host=\2a.{{ ansible_domain }})(host={{ ansible_hostname }})(host=\2a)))
filter group (&(objectClass=posixGroupId)(|(host={{ ansible_fqdn }})(host=\2a.{{ ansible_domain }})(host={{ ansible_hostname }})(host=\2a)))
filter shadow (&(objectClass=shadowAccount)(|(host={{ ansible_fqdn }})(host=\2a.{{ ansible_domain }})(host={{ ansible_hostname }})(host=\2a)))
Send debug logs to ``syslog`` to allow easier debugging:
.. code-block:: yaml
nslcd__configuration:
- name: 'log'
value: 'syslog debug'
Syntax
~~~~~~
The variables contain a list of YAML dictionaries, each dictionary can have
specific parameters:
``name``
Required. Name of the :man:`nslcd.conf(5)` configuration option. The
configuration options with the same ``name`` parameter will be merged in
order of appearance.
If you want to specify multiple configuration options with the same name,
make sure that the ``name`` parameter is unique and use the ``option``
parameter to specify the "real" option name to use.
``value``
Required. The value of a given configuration option. It can be either
a string, or a YAML list (elements will be joined with spaces).
``option``
Optional. When configuration options are specified multiple times, this
parameter can be used to specify the option name instead of the ``name``
parameter.
``map``
Optional. Name of the "map" to configure, inserted between the option name,
and its value. You can find more about map usage in the :man:`nslcd.conf(5)`
documentation.
``raw``
Optional. String or YAML text block which will be included in the
configuration file "as is". If this parameter is specified, ``name``,
``option`` and ``map`` parameters are ignored - you need to specify the
entire line(s) with configuration option names as well.
``state``
Optional. If not defined or ``present``, a given configuration option will be
included in the generated configuration file. If ``absent``, a given
configuration option will be removed from the generated file. If ``comment``,
the option will be included, but commented out and inactive. If ``ignore``,
the role will not evaluate the configuration entry during template
generation, this can be used for conditional activation of
:man:`nslcd.conf(5)` configuration options.
``comment``
Optional. String or YAML text block that contains comments about a given
configuration option.
``separator``
Optional, boolean. If ``True``, and additional empty line will be added
before a given configuration option to separate it from the other options for
readability.
Getting started
===============
.. contents::
:local:
Example inventory
-----------------
To enable the :command:`nslcd` service on a host, you need to add it to the
``[debops_service_nslcd]`` Ansible inventory group. The host should also be
configured with base LDAP support via the :ref:`debops.ldap` role (see its
documentation for more details):
.. code-block:: none
[debops_service_ldap]
hostname
[debops_service_nslcd]
hostname
A common case is configuration of LDAP authentication in the entire cluster of
hosts. You can enable :command:`debops.nslcd` role on all DebOps hosts in the
Ansible inventory at once:
.. code-block:: none
[debops_all_hosts]
hostname1
hostname2
[debops_service_nslcd:children]
debops_all_hosts
The :command:`nslcd` service can also be installed and configured by other
playbooks, for example ``bootstrap-ldap.yml``. In such cases the custom
playbook will configure the :command:`nslcd` service on a host, but the role
playbook will not work on a host automatically; you will have to include that
host in the ``[debops_service_nslcd]`` Ansible inventory group via one of the
methods above to be able to change the service configuration.
Example playbook
----------------
If you are using this role without DebOps, here's an example Ansible playbook
that uses the ``debops.nslcd`` role:
.. literalinclude:: ../../../../ansible/playbooks/service/nslcd.yml
:language: yaml
Ansible tags
------------
You can use Ansible ``--tags`` or ``--skip-tags`` parameters to limit what
tasks are performed during Ansible run. This can be used after host is first
configured to speed up playbook execution, when you are sure that most of the
configuration has not been changed.
Available role tags:
``role::nslcd``
Main role tag, should be used in the playbook to execute all of the role
tasks as well as role dependencies.
Other resources
---------------
List of other useful resources related to the ``debops.nslcd`` Ansible role:
- Manual pages: :man:`nslcd.conf(5)`
- LDAP support in DebOps: :ref:`client-side <debops.ldap>`, :ref:`server-side <debops.slapd>`