Merge "Remove Salt deprecations and switch tests to Salt 2017.7"
diff --git a/README.rst b/README.rst
index 930557b..794b56b 100644
--- a/README.rst
+++ b/README.rst
@@ -1608,6 +1608,29 @@
             - fujitsu_eternus_dxl
             - hitachi_vsp1000
 
+PAM LDAP integration
+
+.. code-block:: yaml
+
+    parameters:
+      linux:
+        system:
+          auth:
+            enabled: true
+            ldap:
+              enabled: true
+              binddn: cn=bind,ou=service_users,dc=example,dc=com
+              bindpw: secret
+              uri: ldap://127.0.0.1
+              base: ou=users,dc=example,dc=com
+              ldap_version: 3
+              pagesize: 65536
+              referrals: off
+              filter:
+                passwd: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*))
+                shadow: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*))
+                group:  (&(objectClass=group)(gidNumber=*))
+
 Disabled multipath (the default setup)
 
 .. code-block:: yaml
diff --git a/linux/files/mkhomedir b/linux/files/mkhomedir
new file mode 100644
index 0000000..43c6a49
--- /dev/null
+++ b/linux/files/mkhomedir
@@ -0,0 +1,6 @@
+Name: Create home directory during login
+Default: yes
+Priority: 0
+Session-Type: Additional
+Session-Final:
+    required    pam_mkhomedir.so        skel=/etc/skel  umask=0022 silent
diff --git a/linux/files/nslcd.conf b/linux/files/nslcd.conf
new file mode 100644
index 0000000..772be85
--- /dev/null
+++ b/linux/files/nslcd.conf
@@ -0,0 +1,68 @@
+{%- from "linux/map.jinja" import ldap with context -%}
+# /etc/nslcd.conf
+# nslcd configuration file. See nslcd.conf(5)
+# for details.
+
+# The user and group nslcd should run as.
+uid {{ ldap.uid }}
+gid {{ ldap.gid }}
+
+{%- if ldap.enabled %}
+
+{%- if ldap.uri is defined %}
+# The location at which the LDAP server(s) should be reachable.
+uri {{ ldap.uri }}
+{%- endif %}
+
+{%- if ldap.base is defined %}
+# The search base that will be used for all queries.
+base {{ ldap.base }}
+{%- endif %}
+
+# The LDAP protocol version to use.
+ldap_version {{ ldap.version }}
+
+{%- if ldap.binddn is defined %}
+# The DN to bind with for normal lookups.
+binddn {{ ldap.binddn }}
+{%- if ldap.bindpw is defined %}
+bindpw {{ ldap.bindpw }}
+{%- endif %}
+{%- endif %}
+
+{%- if ldap.rootpwmoddn is defined %}
+# The DN used for password modifications by root.
+rootpwmoddn {{ ldap.rootpwmoddn }}
+{%- endif %}
+
+# SSL options
+#ssl off
+#tls_reqcert never
+#tls_cacertfile /etc/ssl/certs/ca-certificates.crt
+
+# The search scope.
+scope {{ ldap.scope }}
+
+{%- if ldap.pagesize is defined %}
+pagesize {{ ldap.pagesize }}
+{%- endif %}
+{%- if ldap.referrals is defined %}
+referrals {{ ldap.referrals }}
+{%- endif %}
+
+{%- if ldap.filter is defined %}
+# Filters
+{%- for key, value in ldap.filter.iteritems() %}
+filter {{ key }} {{ value }}
+{%- endfor %}
+{%- endif %}
+{%- if ldap.map is defined %}
+# Mappings
+{%- for map_name,map in ldap.map.iteritems() %}
+{%- for key, value in map.iteritems() %}
+map {{ map_name }} {{ key }} {{ value }}
+{%- endfor %}
+{%- endfor %}
+{%- endif %}
+
+{%- endif %}
diff --git a/linux/files/nsswitch.conf b/linux/files/nsswitch.conf
new file mode 100644
index 0000000..74f6332
--- /dev/null
+++ b/linux/files/nsswitch.conf
@@ -0,0 +1,21 @@
+{%- from "linux/map.jinja" import ldap with context -%}
+# /etc/nsswitch.conf
+#
+# Example configuration of GNU Name Service Switch functionality.
+# If you have the `glibc-doc-reference' and `info' packages installed, try:
+# `info libc "Name Service Switch"' for information about this file.
+
+passwd:         compat{%- if ldap.enabled %} ldap{%- endif %}
+group:          compat{%- if ldap.enabled %} ldap{%- endif %}
+shadow:         compat{%- if ldap.enabled %} ldap{%- endif %}
+gshadow:        files
+
+hosts:          files dns
+networks:       files
+
+protocols:      db files
+services:       db files
+ethers:         db files
+rpc:            db files
+
+netgroup:       nis
diff --git a/linux/files/pam-add-profile b/linux/files/pam-add-profile
new file mode 100644
index 0000000..4f1a5fd
--- /dev/null
+++ b/linux/files/pam-add-profile
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+if [ "$(basename $EDITOR 2>/dev/null)" == "$(basename $0)" ]; then
+    PROFILES=$(debconf-get-selections | grep libpam-runtime/profiles | awk '{$1=$2=$3=""; print $0}')
+
+    for profile in ${PROFILE[@]}; do
+        if [[ $PROFILES =~ $profile ]]; then
+            continue
+        else
+            PROFILES="${PROFILES}, ${profile}"
+        fi
+    done
+
+    for profile in /usr/share/pam-configs/*; do
+        profile_name=$(grep Name: $profile | cut -d ' ' -f 2-)
+        PROFILES=$(echo $PROFILES | sed s,$(basename $profile),"${profile_name}",g)
+    done
+
+    cat > $1 <<EOF
+libpam-runtime/profiles="${PROFILES}"
+EOF
+else
+    [ -z $1 ] && { echo "Usage: $0 [PROFILE]"; exit 1; }
+    export PROFILE="$*"
+    EDITOR=/usr/local/bin/pam-add-profile DEBIAN_FRONTEND=editor pam-auth-update --force
+fi
diff --git a/linux/map.jinja b/linux/map.jinja
index 29dba83..185c131 100644
--- a/linux/map.jinja
+++ b/linux/map.jinja
@@ -85,6 +85,37 @@
     },
 }, grain='os_family', merge=salt['pillar.get']('linux:system')) %}
 
+{% set auth = salt['grains.filter_by']({
+    'Arch': {
+        'enabled': false,
+    },
+    'RedHat': {
+        'enabled': false,
+    },
+    'Debian': {
+        'enabled': false,
+    },
+}, grain='os_family', merge=salt['pillar.get']('linux:system:auth')) %}
+
+{% set ldap = salt['grains.filter_by']({
+    'RedHat': {
+        'enabled': false,
+        'pkgs': ['openldap-clients', 'nss-pam-ldapd', 'authconfig'],
+        'version': '3',
+        'scope': 'sub',
+        'uid': 'nslcd',
+        'gid': 'nslcd',
+    },
+    'Debian': {
+        'enabled': false,
+        'pkgs': ['libnss-ldapd', 'libpam-ldapd'],
+        'version': '3',
+        'scope': 'sub',
+        'uid': 'nslcd',
+        'gid': 'nslcd',
+    },
+}, grain='os_family', merge=salt['pillar.get']('linux:system:auth:ldap')) %}
+
 {#    'network_name', #}
 
 {% set interface_params = [
@@ -145,7 +176,7 @@
         'hostname_file': '/etc/hostname',
         'bridge_pkgs': ['bridge-utils'],
         'ovs_pkgs': ['openvswitch-switch', 'bridge-utils'],
-        'dpdk_pkgs': ['dpdk', 'dpdk-dev', 'dpdk-dkms', 'dpdk-igb-uio-dkms', 'dpdk-rte-kni-dkms'],
+        'dpdk_pkgs': ['dpdk', 'dpdk-dev', 'dpdk-igb-uio-dkms', 'dpdk-rte-kni-dkms'],
         'network_manager': False,
         'systemd': {},
         'interface': {},
diff --git a/linux/system/auth.sls b/linux/system/auth.sls
new file mode 100644
index 0000000..70bf6cb
--- /dev/null
+++ b/linux/system/auth.sls
@@ -0,0 +1,100 @@
+{%- from "linux/map.jinja" import auth with context %}
+
+{%- if auth.enabled %}
+
+{%- if auth.get('ldap', {}).get('enabled', False) %}
+{%- from "linux/map.jinja" import ldap with context %}
+
+{%- if grains.os_family == 'Debian' %}
+
+linux_auth_debconf_libnss-ldapd:
+  debconf.set:
+    - name: libnss-ldapd
+    - data:
+        libnss-ldapd/nsswitch:
+          type: 'multiselect'
+          value: 'group, passwd, shadow'
+        libnss-ldapd/clean_nsswitch:
+          type: 'boolean'
+          value: 'false'
+    - require_in:
+      - pkg: linux_auth_ldap_packages
+
+linux_auth_debconf_libpam-ldapd:
+  debconf.set:
+    - name: libpam-ldapd
+    - data:
+        libpam-ldapd/enable_shadow:
+          type: 'boolean'
+          value: 'true'
+
+{#- Setup mkhomedir and ldap PAM profiles #}
+linux_auth_mkhomedir_config:
+  file.managed:
+    - name: /usr/share/pam-configs/mkhomedir
+    - source: salt://linux/files/mkhomedir
+    - require:
+      - pkg: linux_auth_ldap_packages
+
+linux_auth_pam_add_profile:
+  file.managed:
+    - name: /usr/local/bin/pam-add-profile
+    - source: salt://linux/files/pam-add-profile
+    - mode: 755
+
+linux_auth_pam_add_profiles:
+  cmd.run:
+    - name: /usr/local/bin/pam-add-profile ldap mkhomedir
+    - unless: "debconf-get-selections | grep libpam-runtime/profiles | grep mkhomedir | grep ldap"
+    - watch:
+      - file: linux_auth_mkhomedir_config
+    - require:
+      - file: linux_auth_pam_add_profile
+      - pkg: linux_auth_ldap_packages
+
+{%- elif grains.os_family == 'RedHat' %}
+
+linux_auth_config:
+  cmd.run:
+    - name: "authconfig --enableldap --enableldapauth --enablemkhomedir --update"
+    - require:
+      - pkg: linux_auth_ldap_packages
+
+{%- else %}
+
+linux_auth_nsswitch_config_file:
+  file.managed:
+- name: /etc/nsswitch.conf
+  - source: salt://linux/files/nsswitch.conf
+  - template: jinja
+  - mode: 644
+  - require:
+    - pkg: linux_auth_ldap_packages
+  - watch_in:
+    - service: linux_auth_nslcd_service
+
+{%- endif %}
+
+linux_auth_ldap_packages:
+  pkg.installed:
+  - pkgs: {{ ldap.pkgs }}
+
+linux_auth_nslcd_config_file:
+  file.managed:
+  - name: /etc/nslcd.conf
+  - source: salt://linux/files/nslcd.conf
+  - template: jinja
+  - mode: 600
+  - require:
+    - pkg: linux_auth_ldap_packages
+  - watch_in:
+    - service: linux_auth_nslcd_service
+
+linux_auth_nslcd_service:
+  service.running:
+  - enable: true
+  - name: nslcd
+
+{%- endif %}
+
+{%- endif %}
diff --git a/linux/system/init.sls b/linux/system/init.sls
index af61698..f97ccf9 100644
--- a/linux/system/init.sls
+++ b/linux/system/init.sls
@@ -108,3 +108,6 @@
 {%- if system.apt is defined and grains.os_family == 'Debian' %}
 - linux.system.apt
 {%- endif %}
+{%- if system.auth is defined %}
+- linux.system.auth
+{%- endif %}
diff --git a/tests/pillar/system_extra.sls b/tests/pillar/system_extra.sls
index 801c628..a425f6a 100644
--- a/tests/pillar/system_extra.sls
+++ b/tests/pillar/system_extra.sls
@@ -1,6 +1,21 @@
 
 linux:
   system:
+    auth:
+      enabled: true
+      ldap:
+        enabled: true
+        binddn: cn=bind,ou=service_users,dc=example,dc=com
+        bindpw: secret
+        uri: ldap://127.0.0.1
+        base: ou=users,dc=example,dc=com
+        ldap_version: 3
+        pagesize: 65536
+        referrals: off
+        filter:
+          passwd: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*))
+          shadow: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*))
+          group:  (&(objectClass=group)(gidNumber=*))
     enabled: true
     cluster: default
     name: linux