Commit 4602f012 authored by Maciej Delmanowski's avatar Maciej Delmanowski

Merge branch 'drybjed-sshd-ldap-improvements'

parents d9713de0 d2ce16ab
Pipeline #56334493 passed with stages
in 28 minutes and 39 seconds
......@@ -73,6 +73,11 @@ Added
.. __: https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers
- A new ``bootstrap-ldap.yml`` Ansible playbook can be used to bootstrap
Debian/Ubuntu hosts with LDAP support enabled by default. The playbook will
configure only the services required for secure LDAP access (PKI, SSH,
PAM/NSS), the rest should be configured using the common playbook.
Changed
~~~~~~~
......@@ -171,6 +176,31 @@ Changed
:ref:`debops.system_groups` role was applied before the :ref:`debops.ldap`
role.
- [debops.sshd] The access control based on UNIX groups defined in the
:file:`/etc/ssh/sshd_config` file has been removed. Instead, the OpenSSH
server uses the PAM access control configuration, managed by the
:ref:`debops.pam_access` Ansible role, to control access by
users/groups/origins. OpenSSH service uses its own access control file,
separate from the global :file:`/etc/security/access.conf` file.
- [debops.sshd] The role will enable client address resolving using DNS by
setting the ``UseDNS yes`` option in OpenSSH server configuration. This
parameter is disabled by default in Debian and upstream, however it is
required for the domain-based access control rules to work as expected.
- [debops.sshd] When the LDAP support is configured on a host by the
:ref:`debops.ldap` role, the :ref:`debops.sshd` role will use the resulting
infrastructure to connect to the LDAP directory and create the ``sshd`` LDAP
account object for each host, used for lookups of the SSH keys in the
directory. The SSH host public keys will be automatically added or updated in
the LDAP device object to allow for centralized generation of the
``~/.ssh/known_hosts`` files based on the data stored in LDAP.
The role will no longer create a separate ``sshd-lookup`` UNIX account to
perform LDAP lookups; the existing ``sshd`` UNIX account will be used
instead. The :command:`ldapsearch` command used for lookups will default to
LDAP over TLS connections instead of LDAPS.
Removed
~~~~~~~
......
---
# This playbook can be used to bootstrap new Debian/Ubuntu host to be used with
# LDAP environment. It will automatically enable LDAP support and prepare
# secure access to the LDAP directory, including PAM/NSS and SSH key lookups.
#
# The configuration applied by this playbook is minimal, just enough to be able
# to login via SSH using information gathered from LDAP. You should apply the
# DebOps 'common.yml' playbook on a host afterwards to complete the initial
# configuration, for example firewall/TCP Wrappers setup.
#
# Usage:
# To connect directly as root, run:
#
# debops bootstrap-ldap -u root -k --limit host
#
# To connect as normal user and switch to sudo, run:
#
# debops bootstrap-ldap --become --limit host
- name: Bootstrap Python support on a host
hosts: [ 'debops_all_hosts', 'debops_service_bootstrap' ]
gather_facts: False
roles:
- role: debops.python/raw
- import_playbook: 'service/core.yml'
- name: Bootstrap host for Ansible management with LDAP
hosts: [ 'debops_all_hosts', 'debops_service_bootstrap' ]
environment: '{{ inventory__environment | d({})
| combine(inventory__group_environment | d({}))
| combine(inventory__host_environment | d({})) }}'
vars:
# Automatically enable LDAP support on new hosts
ldap__enabled: True
roles:
- role: debops.pki/env
tags: [ 'role::pki', 'role::pki:secret', 'role::secret' ]
- role: debops.sshd/env
tags: [ 'role::sshd', 'role::ldap' ]
- role: debops.secret
tags: [ 'role::secret', 'role::pki', 'role::pki:secret' ]
secret_directories:
- '{{ pki_env_secret_directories }}'
- role: debops.apt_preferences
tags: [ 'role::apt_preferences', 'skip::apt_preferences' ]
apt_preferences__dependent_list:
- '{{ sshd__apt_preferences__dependent_list }}'
- role: debops.apt_proxy
tags: [ 'role::apt_proxy', 'skip::apt_proxy' ]
- role: debops.apt
tags: [ 'role::apt', 'skip::apt' ]
- role: debops.etckeeper
tags: [ 'role::etckeeper', 'skip::etckeeper' ]
- 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.netbase
tags: [ 'role::netbase', 'skip::netbase' ]
- role: debops.cron
tags: [ 'role::cron', 'skip::cron' ]
- role: debops.atd
tags: [ 'role::atd', 'skip::atd' ]
- role: debops.dhparam
tags: [ 'role::dhparam', 'skip::dhparam' ]
- role: debops.pki
tags: [ 'role::pki', 'skip::pki' ]
# LDAP client initialization should be done separately to prepare local
# facts for other roles to use in configuration.
- role: debops.ldap
tags: [ 'role::ldap', 'skip::ldap' ]
- role: debops.ldap
tags: [ 'role::ldap', 'skip::ldap' ]
ldap__dependent_tasks:
- '{{ nslcd__ldap__dependent_tasks }}'
- '{{ sshd__ldap__dependent_tasks }}'
- role: debops.nslcd
tags: [ 'role::nslcd', 'skip::nslcd' ]
- role: debops.sudo
tags: [ 'role::sudo', 'skip::sudo', 'role::system_groups' ]
# The 'sudo' APT package modifies '/etc/nsswitch.conf' by itself, running
# this role after 'debops.sudo' role skips additional changes done in the
# configuration later on.
- role: debops.nsswitch
tags: [ 'role::nsswitch', 'skip::nsswitch' ]
nsswitch__dependent_services:
- '{{ nslcd__nsswitch__dependent_services }}'
- role: debops.system_groups
tags: [ 'role::system_groups', 'skip::system_groups' ]
# This role will be replaced by a new role that manages UNIX system accounts
#- role: debops.bootstrap
# tags: [ 'role::bootstrap', 'skip::bootstrap' ]
- role: debops.pam_access
tags: [ 'role::pam_access', 'skip::pam_access' ]
pam_access__dependent_rules:
- '{{ sshd__pam_access__dependent_rules }}'
- role: debops.sshd
tags: [ 'role::sshd', 'skip::sshd' ]
......@@ -38,6 +38,9 @@
- role: debops.python/raw
- import_playbook: 'service/core.yml'
- name: Bootstrap host for Ansible management
hosts: [ 'debops_all_hosts', 'debops_service_bootstrap' ]
......
......@@ -54,6 +54,9 @@
- role: debops.pki/env
tags: [ 'role::pki', 'role::pki:secret', 'role::secret' ]
- role: debops.sshd/env
tags: [ 'role::sshd', 'role::ldap' ]
- role: debops.secret
tags: [ 'role::secret', 'role::pki', 'role::pki:secret' ]
secret_directories:
......@@ -99,6 +102,11 @@
- role: debops.ldap
tags: [ 'role::ldap', 'skip::ldap' ]
- role: debops.ldap
tags: [ 'role::ldap', 'skip::ldap' ]
ldap__dependent_tasks:
- '{{ sshd__ldap__dependent_tasks }}'
- role: debops.apt_listchanges
tags: [ 'role::apt_listchanges', 'skip::apt_listchanges' ]
......@@ -113,6 +121,8 @@
- role: debops.pam_access
tags: [ 'role::pam_access', 'skip::pam_access' ]
pam_access__dependent_rules:
- '{{ sshd__pam_access__dependent_rules }}'
- role: debops.etc_services
tags: [ 'role::etc_services', 'skip::etc_services' ]
......
......@@ -10,6 +10,9 @@
roles:
- role: debops.sshd/env
tags: [ 'role::sshd', 'role::ldap' ]
- role: debops.apt_preferences
tags: [ 'role::apt_preferences', 'skip::apt_preferences' ]
apt_preferences__dependent_list:
......@@ -25,5 +28,22 @@
tcpwrappers_dependent_allow:
- '{{ sshd__tcpwrappers__dependent_allow }}'
- 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:
- '{{ sshd__ldap__dependent_tasks }}'
- role: debops.pam_access
tags: [ 'role::pam_access', 'skip::pam_access' ]
pam_access__dependent_rules:
- '{{ sshd__pam_access__dependent_rules }}'
- role: debops.sshd
tags: [ 'role::sshd', 'skip::sshd' ]
This diff is collapsed.
---
# The SSH host keys gathered by Ansible facts don't contain the full
# information, therefore we should grab them by ourselves
- name: Gather SSH host public keys
shell: 'set -o nounset -o pipefail -o errexit &&
cat /etc/ssh/ssh_host_*_key.pub'
args:
executable: '/bin/bash'
register: sshd__env_register_host_public_keys
changed_when: False
check_mode: False
---
- name: Create OpenSSH lookup system group
group:
name: '{{ sshd__authorized_keys_lookup_group }}'
state: 'present'
system: True
- name: Create OpenSSH lookup system user
user:
name: '{{ sshd__authorized_keys_lookup_user }}'
group: '{{ sshd__authorized_keys_lookup_group }}'
home: '{{ sshd__authorized_keys_lookup_home }}'
comment: 'OpenSSH Authorized Keys lookup'
shell: '/bin/false'
state: 'present'
createhome: False
append: False
system: True
- name: Create OpenSSH LDAP bind password file
environment:
ANSIBLE_SSHD_LDAP_BINDPW: '{{ sshd__ldap_bind_pw }}'
ANSIBLE_SSHD_LDAP_BINDPW: '{{ sshd__ldap_bindpw }}'
shell: echo -n ${ANSIBLE_SSHD_LDAP_BINDPW} > {{ sshd__ldap_bind_pw_file }} ;
chmod 0640 {{ sshd__ldap_bind_pw_file }} ;
chown root.{{ sshd__authorized_keys_lookup_group }} {{ sshd__ldap_bind_pw_file }}
chmod 0600 {{ sshd__ldap_bind_pw_file }} ;
chown {{ sshd__authorized_keys_lookup_user }}:root {{ sshd__ldap_bind_pw_file }}
args:
creates: '{{ sshd__ldap_bind_pw_file }}'
when: sshd__authorized_keys_lookup_type|d() and
......
......@@ -184,6 +184,30 @@
state: 'absent'
notify: [ 'Test sshd configuration and restart' ]
- name: Divert PAM configuration for sshd
command: dpkg-divert --quiet --local
--divert "/etc/pam.d/sshd.dpkg-divert"
--rename "/etc/pam.d/sshd"
args:
creates: '/etc/pam.d/sshd.dpkg-divert'
when: sshd__pam_deploy_state == 'present'
- name: Generate PAM configuration for sshd
template:
src: 'etc/pam.d/sshd.j2'
dest: '/etc/pam.d/sshd'
mode: '0644'
when: sshd__pam_deploy_state == 'present'
- name: Revert default sshd PAM configuration if requested
shell: set -o nounset -o pipefail -o errexit &&
rm -f /etc/pam.d/sshd &&
dpkg-divert --quiet --local --rename --remove /etc/pam.d/sshd
args:
executable: '/bin/bash'
removes: '/etc/pam.d/sshd.dpkg-divert'
when: sshd__pam_deploy_state != 'present'
- name: Make sure that Ansible local fact directory exists
file:
path: '/etc/ansible/facts.d'
......
# {{ ansible_managed }}
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
@include common-auth
# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
account required pam_access.so nodefgroup accessfile={{ sshd__pam_access_file }}
# Standard Un*x authorization.
@include common-account
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Set the loginuid process attribute.
session required pam_loginuid.so
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login.
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv # [1]
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session required pam_env.so # [1]
# In Debian 4.0 (etch), locale-related environment variables were moved to
# /etc/default/locale, so read that as well.
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context. Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# Standard Un*x password updating.
@include common-password
......@@ -19,12 +19,12 @@ domain="$(dnsdomainname)"
# shellcheck disable=SC2034
fqdn="$(hostname --fqdn)"
ldap_binddn="{{ sshd__ldap_bind_dn }}"
ldap_binddn="{{ sshd__ldap_binddn }}"
ldap_bindpw="{{ sshd__ldap_bind_pw_file }}"
ldap_filter="{{ sshd__ldap_filter }}"
ATTRIBUTE=$(ldapsearch -LLL -x -y "${ldap_bindpw}" -D "${ldap_binddn}" -o ldif-wrap=no -S sshPublicKey "${ldap_filter}" sshPublicKey | grep -v 'dn:')
ATTRIBUTE=$(ldapsearch -Z -LLL -x -y "${ldap_bindpw}" -D "${ldap_binddn}" -o ldif-wrap=no -S sshPublicKey "${ldap_filter}" sshPublicKey | grep -v 'dn:')
if [[ $ATTRIBUTE == sshPublicKey::* ]];
then
......
......@@ -4,4 +4,6 @@
# Lookup SSH public key in SSSD
/usr/bin/sss_ssh_authorizedkeys "${1}"
if [ -x "/usr/bin/sss_ssh_authorizedkeys" ] ; then
/usr/bin/sss_ssh_authorizedkeys "${1}"
fi
......@@ -121,6 +121,12 @@ RhostsRSAAuthentication no
# similar for protocol version 2
HostbasedAuthentication no
# If enabled, sshd will resolve the client hostnames and check their reverse
# PTR records to see if they are the same. This is required for hostname
# matching in authorized keys, Allow/Deny options and PAM access rules that use
# hostnames/domains.
UseDNS {{ sshd__use_dns }}
# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
#IgnoreUserKnownHosts yes
......
......@@ -23,6 +23,7 @@ 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`
- :ref:`uid=sshd <sshd__ref_ldap_dit>` -> :ref:`debops.sshd`
- :envvar:`ou=People <ldap__people_rdn>`
- :envvar:`ou=Groups <ldap__groups_rdn>`
......@@ -40,6 +41,7 @@ Object Classes and Attributes
- :envvar:`cn=host.example.org <ldap__device_self_rdn>`
- :ref:`debops.ldap`: :envvar:`Object Classes <ldap__device_object_classes>`, :envvar:`Attributes <ldap__device_attributes>`
- :ref:`debops.sshd`: :envvar:`Object Classes <sshd__ldap_device_object_classes>`, :envvar:`Attributes <sshd__ldap_device_attributes>` (SSH host public keys)
Parent nodes
......
......@@ -4,6 +4,21 @@ Getting started
.. contents::
:local:
Changes from Debian defaults
----------------------------
The ``debops.sshd`` role will configure the OpenSSH server to lookup the client
hostnames in DNS by setting the ``UseDNS`` option to ``yes`` (the Debian and
upstream default is ``no``). This allows use of the DNS hostnames and domains
in the authorized keys files and PAM access control rules. DNS lookup can be
controlled using the :envvar:`sshd__use_dns` variable.
The role will divert the original :file:`/etc/pam.d/sshd` configuration file
and generate a new one, with PAM access control enabled and using the separate
:file:`/etc/security/access-sshd.conf` configuration file. The ACL rules are
defined in the :envvar:`sshd__pam_access__dependent_rules` variable and are
managed by the :ref:`debops.pam_access` Ansible role.
Useful variables
----------------
......@@ -33,7 +48,7 @@ Ansible inventory to customize OpenSSH server:
LDAP key lookup depends on system-wide LDAP configuration in
:file:`/etc/ldap/ldap.conf`, which can be performed (at the moment) using
:ref:`debops.auth` role.
:ref:`debops.ldap` role.
Example inventory
-----------------
......
......@@ -9,6 +9,7 @@ debops.sshd
introduction
getting-started
defaults
ldap-dit
copyright
..
......
.. _sshd__ref_ldap_dit:
LDAP Directory Information Tree
===============================
This document describes how the :ref:`debops.sshd` Ansible role fits in the
:ref:`ldap__ref_dit`.
Directory structure
-------------------
- :ref:`cn=host.example.org <ldap__ref_ldap_dit>` -> :ref:`debops.ldap`
- :envvar:`uid=sshd <sshd__ldap_self_rdn>`
Object Classes and Attributes
-----------------------------
- :envvar:`uid=sshd <sshd__ldap_self_rdn>`
- :ref:`debops.sshd`: :envvar:`Object Classes <sshd__ldap_self_object_classes>`, :envvar:`Attributes <sshd__ldap_self_attributes>`
Parent nodes
------------
- :ref:`debops.ldap <ldap__ref_ldap_dit>`
- :envvar:`ansible_local.ldap.base_dn <ldap__base_dn>` -> :envvar:`sshd__ldap_base_dn`
- :envvar:`ansible_local.ldap.device_dn <ldap__device_dn>` -> :envvar:`sshd__ldap_device_dn`
Child nodes
-----------
There are no child nodes defined for the :ref:`debops.sshd` Ansible role.
......@@ -94,6 +94,52 @@ Inventory variable changes
| ``auth_nslcd_pam_authz_search_host_and_service`` | Removed | No |
+--------------------------------------------------+----------------------------------+--------------------------------------------------+
- The :envvar:`sshd__default_allow_groups` default variable has been changed to
an empty list. The group-based access control has been moved to a PAM access
control rules defined in the :envvar:`sshd__pam_access__dependent_rules`
variable.
Access to the OpenSSH service by the ``admins``, ``sshusers`` and
``sftponly`` UNIX groups members should work the same as before. Access to
the ``root`` account has been limited to hosts in the same DNS domain. UNIX
accounts not in the aforementioned UNIX groups can access the OpenSSH service
from hosts in the same DNS domain (other restrictions like public key
presence still apply). See :ref:`debops.pam_access` documentation for more
details about defining the PAM access rules.
- The default variables in the :ref:`debops.sshd` role related to LDAP support
have been modified:
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| Old variable name | New variable name | Changed value |
+=============================================+================================+==================================================+
| :envvar:`sshd__authorized_keys_lookup` | Not modified | Based on :ref:`debops.ldap` facts |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| :envvar:`sshd__authorized_keys_lookup_user` | Not modified | Yes, to ``sshd`` |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__authorized_keys_lookup_group`` | Removed | No |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__authorized_keys_lookup_home`` | Removed | No |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| :envvar:`sshd__authorized_keys_lookup_type` | Not modified | Yes, ``sss`` included by default |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__ldap_domain`` | Removed | No |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__ldap_base`` | :envvar:`sshd__ldap_base_dn` | Based on :ref:`debops.ldap` facts |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__ldap_bind_basedn`` | :envvar:`sshd__ldap_device_dn` | Based on :ref:`debops.ldap` facts |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__ldap_bind_cn`` | :envvar:`sshd__ldap_self_rdn` | Yes, different attribute, different value source |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__ldap_bind_dn`` | :envvar:`sshd__ldap_binddn` | Yes |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__ldap_bind_bind_pw`` | :envvar:`sshd__ldap_bindpw` | Yes, different password path |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__ldap_bind_basepw`` | Removed | No |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
| ``sshd__ldap_password_length`` | Removed | No |
+---------------------------------------------+--------------------------------+--------------------------------------------------+
v0.8.1 (2019-02-02)
-------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment