Merge "Bring in ovs_config module/state"
diff --git a/.gitignore b/.gitignore
index aa8e42a..cc3ab8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,8 @@
 .kitchen
+.bundle
+bundle/
 tests/build/
 *.swp
 *.pyc
 .ropeproject
+Gemfile*
diff --git a/.travis.yml b/.travis.yml
index 78246a5..fac2153 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,6 @@
+language: python
+python:
+- "2.7.13"
 sudo: required
 services:
   - docker
diff --git a/README.rst b/README.rst
index 986ec72..f35bce7 100644
--- a/README.rst
+++ b/README.rst
@@ -70,6 +70,30 @@
             home: '/home/elizabeth'
             password: "$6$nUI7QEz3$dFYjzQqK5cJ6HQ38KqG4gTWA9eJu3aKx6TRVDFh6BVJxJgFWg2akfAA7f1fCxcSUeOJ2arCO6EEI6XXnHXxG10"
 
+Configure password expiration parameters
+----------------------------------------
+The following login.defs parameters can be overridden per-user:
+
+* PASS_MAX_DAYS
+* PASS_MIN_DAYS
+* PASS_WARN_DAYS
+* INACTIVE
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        ...
+        user:
+          jdoe:
+            name: 'jdoe'
+            enabled: true
+            ...
+            maxdays: <PASS_MAX_DAYS>
+            mindays: <PASS_MIN_DAYS>
+            warndays: <PASS_WARN_DAYS>
+            inactdays: <INACTIVE>
+
 Configure sudo for users and groups under ``/etc/sudoers.d/``.
 This ways ``linux.system.sudo`` pillar map to actual sudo attributes:
 
@@ -235,7 +259,54 @@
           automatic_reboot: true
           automatic_reboot_time: "02:00"
 
-Linux with cron jobs
+Managing cron tasks
+-------------------
+
+There are two data structures that are related to managing cron itself and
+cron tasks:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        cron:
+
+and
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        job:
+
+`linux:system:cron` manages cron packages, services, and '/etc/cron.allow' file.
+
+'deny' files are managed the only way - we're ensuring they are absent, that's
+a requirement from CIS 5.1.8
+
+'cron' pillar structure is the following:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        cron:
+          enabled: true
+          pkgs: [ <cron packages> ]
+          services: [ <cron services> ]
+          user:
+            <username>:
+              enabled: true
+
+To add user to '/etc/cron.allow' use 'enabled' key as shown above.
+
+'/etc/cron.deny' is not managed as CIS 5.1.8 requires it was removed.
+
+A user would be ignored if any of the following is true:
+* user is disabled in `linux:system:user:<username>`
+* user is disabled in `linux:system:cron:user:<username>`
+
+`linux:system:job` manages individual cron tasks.
 
 By default, it will use name as an identifier, unless identifier key is
 explicitly set or False (then it will use Salt's default behavior which is
@@ -255,6 +326,32 @@
             hour: 2
             minute: 0
 
+Managing 'at' tasks
+-------------------
+
+Pillar for managing `at` tasks is similar to one for `cron` tasks:
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        at:
+          enabled: true
+          pkgs: [ <at packages> ]
+          services: [ <at services> ]
+          user:
+            <username>:
+              enabled: true
+
+To add a user to '/etc/at.allow' use 'enabled' key as shown above.
+
+'/etc/at.deny' is not managed as CIS 5.1.8 requires it was removed.
+
+A user will be ignored if any of the following is true:
+* user is disabled in `linux:system:user:<username>`
+* user is disabled in `linux:system:at:user:<username>`
+
+
 Linux security limits (limit sensu user memory usage to max 1GB):
 
 .. code-block:: yaml
@@ -732,6 +829,22 @@
                 power/state: "root:power"
             - devices/system/cpu/cpu0/cpufreq/scaling_governor: powersave
 
+Sysfs definition with disabled automatic write. Attributes are saved
+to configuration, but are not applied during the run.
+Thay will be applied automatically after the reboot.
+
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        sysfs:
+          enable_apply: false
+          scheduler:
+            block/sda/queue/scheduler: deadline
+
+.. note:: The `enable_apply` parameter defaults to `True` if not defined.
+
 Huge Pages
 ~~~~~~~~~~~~
 
@@ -829,6 +942,31 @@
                priority: 900
                package: '*'
 
+If you need to add multiple pin rules for one repo, please use new,ordered definition format
+('pinning' definition will be in priotity to use):
+
+.. code-block:: yaml
+
+  linux:
+    system:
+      repo:
+        mcp_saltstack:
+          source: "deb [arch=amd64] http://repo.saltstack.com/apt/ubuntu/16.04/amd64/2017.7/ xenial main"
+          architectures: amd64
+          clean_file: true
+          pinning:
+            10:
+              enabled: true
+              pin: 'release o=SaltStack'
+              priority: 50
+              package: 'libsodium18'
+            20:
+              enabled: true
+              pin: 'release o=SaltStack'
+              priority: 1100
+              package: '*'
+
+
 .. note:: For old Ubuntu releases (<xenial)
           extra packages for apt transport, like ``apt-transport-https``
           may be required to be installed manually.
@@ -1556,6 +1694,23 @@
             export FTP_PROXY=ftp://127.0.3.3:2121
             export NO_PROXY='.local'
 
+
+Configure login.defs parameters
+-------------------------------
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        login_defs:
+          <opt_name>:
+            enabled: true
+            value: <opt_value>
+
+<opt_name> is a configurational option defined in 'man login.defs'.
+<opt_name> is case sensitive, should be UPPERCASE only!
+
+
 Linux with hosts
 
 Parameter ``purge_hosts`` will enforce whole ``/etc/hosts file``,
@@ -2080,6 +2235,240 @@
                 interface: bond0
                 mac: "ff:ff:ff:ff:ff:ff" (optional)
 
+Check network params on the environment
+---------------------------------------
+
+Grab nics and nics states
+
+.. code-block:: bash
+
+   salt osd001\* net_checks.get_nics
+
+**Example of system output:**
+
+.. code-block:: bash
+
+   osd001.domain.com:
+       |_
+         - bond0
+         - None
+         - 1e:c8:64:42:23:b9
+         - 0
+         - 1500
+       |_
+         - bond1
+         - None
+         - 3c:fd:fe:27:3b:00
+         - 1
+         - 9100
+       |_
+         - fourty1
+         - None
+         - 3c:fd:fe:27:3b:00
+         - 1
+         - 9100
+       |_
+         - fourty2
+         - None
+         - 3c:fd:fe:27:3b:02
+         - 1
+         - 9100
+
+Grab 10G nics PCI addresses for hugepages setup
+
+.. code-block:: bash
+
+   salt cmp001\* net_checks.get_ten_pci
+
+**Example of system output:**
+
+.. code-block:: bash
+
+   cmp001.domain.com:
+       |_
+         - ten1
+         - 0000:19:00.0
+       |_
+         - ten2
+         - 0000:19:00.1
+       |_
+         - ten3
+         - 0000:19:00.2
+       |_
+         - ten4
+         - 0000:19:00.3
+
+Grab ip address for an interface
+
+.. code-block:: bash
+
+   salt cmp001\* net_checks.get_ip iface=one4
+
+**Example of system output:**
+
+.. code-block:: bash
+
+   cmp001.domain.com:
+       10.200.177.101
+
+Grab ip addresses map
+
+.. code-block:: bash
+
+   salt-call net_checks.nodes_addresses
+
+**Example of system output:**
+
+.. code-block:: bash
+
+   local:
+    |_
+      - cid01.domain.com
+      |_
+        |_
+          - pxe
+          - 10.200.177.91
+        |_
+          - control
+          - 10.200.178.91
+    |_
+      - cmn02.domain.com
+      |_
+        |_
+          - storage_access
+          - 10.200.181.67
+        |_
+          - pxe
+          - 10.200.177.67
+        |_
+          - control
+          - 10.200.178.67
+    |_
+      - cmp010.domain.com
+      |_
+        |_
+          - pxe
+          - 10.200.177.110
+        |_
+          - storage_access
+          - 10.200.181.110
+        |_
+          - control
+          - 10.200.178.110
+        |_
+          - vxlan
+          - 10.200.179.110
+
+Verify full mesh connectivity
+
+.. code-block:: bash
+
+   salt-call net_checks.ping_check
+
+**Example of positive system output:**
+
+.. code-block:: bash
+
+   ['PASSED']
+   [INFO    ] ['PASSED']
+   local:
+       True
+
+**Example of system output in case of failure:**
+
+.. code-block:: bash
+
+   FAILED
+   [ERROR   ] FAILED
+   ['control: 10.0.1.92 -> 10.0.1.224: Failed']
+   ['control: 10.0.1.93 -> 10.0.1.224: Failed']
+   ['control: 10.0.1.51 -> 10.0.1.224: Failed']
+   ['control: 10.0.1.102 -> 10.0.1.224: Failed']
+   ['control: 10.0.1.13 -> 10.0.1.224: Failed']
+   ['control: 10.0.1.81 -> 10.0.1.224: Failed']
+   local:
+       False
+
+For this feature to work, please mark addresses with some role.
+Otherwise 'default' role is assumed and mesh would consist of all
+addresses on the environment.
+
+Mesh mark is needed only for interfaces which are enabled and have
+ip address assigned.
+
+Checking dhcp pxe network meaningless, as it is used for salt
+master vs minion communications, therefore treated as checked.
+
+.. code-block:: yaml
+
+   parameters:
+     linux:
+       network:
+         interface:
+           ens3:
+             enabled: true
+             type: eth
+             proto: static
+             address: ${_param:deploy_address}
+             netmask: ${_param:deploy_network_netmask}
+             gateway: ${_param:deploy_network_gateway}
+             mesh: pxe
+
+Check pillars for ip address duplicates
+
+.. code-block:: bash
+
+   salt-call net_checks.verify_addresses
+
+**Example of positive system output:**
+
+.. code-block:: bash
+
+   ['PASSED']
+   [INFO    ] ['PASSED']
+   local:
+       True
+
+**Example of system output in case of failure:**
+
+.. code-block:: bash
+
+   FAILED. Duplicates found
+   [ERROR   ] FAILED. Duplicates found
+   ['gtw01.domain.com', 'gtw02.domain.com', '10.0.1.224']
+   [ERROR   ] ['gtw01.domain.com', 'gtw02.domain.com', '10.0.1.224']
+   local:
+       False
+
+Generate csv report for the env
+
+.. code-block:: bash
+
+   salt -C 'kvm* or cmp* or osd*' net_checks.get_nics_csv \
+     | grep '^\ ' | sed 's/\ *//g' | grep -Ev ^server \
+     | sed '1 i\server,nic_name,ip_addr,mac_addr,link,mtu,chassis_id,chassis_name,port_mac,port_descr'
+
+**Example of system output:**
+
+.. code-block:: bash
+
+   server,nic_name,ip_addr,mac_addr,link,mtu,chassis_id,chassis_name,port_mac,port_descr
+   cmp010.domain.com,bond0,None,b4:96:91:10:5b:3a,1,1500,,,,
+   cmp010.domain.com,bond0.21,10.200.178.110,b4:96:91:10:5b:3a,1,1500,,,,
+   cmp010.domain.com,bond0.22,10.200.179.110,b4:96:91:10:5b:3a,1,1500,,,,
+   cmp010.domain.com,bond1,None,3c:fd:fe:34:ad:22,0,1500,,,,
+   cmp010.domain.com,bond1.24,10.200.181.110,3c:fd:fe:34:ad:22,0,1500,,,,
+   cmp010.domain.com,fourty5,None,3c:fd:fe:34:ad:20,0,9000,,,,
+   cmp010.domain.com,fourty6,None,3c:fd:fe:34:ad:22,0,9000,,,,
+   cmp010.domain.com,one1,None,b4:96:91:10:5b:38,0,1500,,,,
+   cmp010.domain.com,one2,None,b4:96:91:10:5b:39,1,1500,f0:4b:3a:8f:75:40,exnfvaa18-20,548,ge-0/0/22
+   cmp010.domain.com,one3,None,b4:96:91:10:5b:3a,1,1500,f0:4b:3a:8f:75:40,exnfvaa18-20,547,ge-0/0/21
+   cmp010.domain.com,one4,10.200.177.110,b4:96:91:10:5b:3b,1,1500,f0:4b:3a:8f:75:40,exnfvaa18-20,546,ge-0/0/20
+   cmp011.domain.com,bond0,None,b4:96:91:13:6c:aa,1,1500,,,,
+   cmp011.domain.com,bond0.21,10.200.178.111,b4:96:91:13:6c:aa,1,1500,,,,
+   cmp011.domain.com,bond0.22,10.200.179.111,b4:96:91:13:6c:aa,1,1500,,,,
+   ...
+
 Usage
 =====
 
diff --git a/_modules/net_checks.py b/_modules/net_checks.py
new file mode 100644
index 0000000..cb0f4fc
--- /dev/null
+++ b/_modules/net_checks.py
@@ -0,0 +1,279 @@
+from os import listdir, path
+from subprocess import Popen,PIPE
+from re import findall as refindall
+from re import search as research
+import salt.utils
+import socket, struct, fcntl
+import logging
+
+logger = logging.getLogger(__name__)
+stream = logging.StreamHandler()
+logger.addHandler(stream)
+
+def get_ip(iface='ens2'):
+
+    ''' Get ip address from an interface if applicable
+
+    :param iface: Interface name. Type: str
+
+    '''
+
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sockfd = sock.fileno()
+    SIOCGIFADDR = 0x8915
+    ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)
+
+    try:
+        res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
+    except:
+        logger.debug("No ip addresses assigned to %s" % iface)
+        return None
+
+    ip = struct.unpack('16sH2x4s8x', res)[2]
+    return socket.inet_ntoa(ip)
+
+def get_nics():
+
+    ''' List nics '''
+
+    nics = []
+    nics_list = listdir('/sys/class/net/')
+    for nic_name in nics_list:
+        if research('(br|bond|ens|enp|eth|one|ten|fourty)[0-9]+', nic_name):
+
+            # Interface should be in "up" state in order to get carrier status
+            Popen("ip li set dev " + nic_name + " up", shell=True, stdout=PIPE)
+
+            with open("/sys/class/net/" + nic_name + "/carrier", 'r') as f:
+                try:
+                    carrier = int(f.read())
+                except:
+                    carrier = 0
+
+            bond = ""
+            if path.isfile("/sys/class/net/" + nic_name + "/master/uevent"):
+                with open("/sys/class/net/" + nic_name + "/master/uevent", 'r') as f:
+                    for line in f:
+                        sline = line.strip()
+                        if 'INTERFACE=bond' in sline:
+                            bond = sline.split('=')[1]
+            if len(bond) == 0:
+                with open("/sys/class/net/" + nic_name + "/address", 'r') as f:
+                    macaddr = f.read().strip()
+            else:
+                with open("/proc/net/bonding/" + bond, 'r') as f:
+                    line = f.readline()
+                    if_struct = False
+                    while line:
+                        sline = line.strip()
+                        if 'Slave Interface: ' + nic_name in sline and not if_struct:
+                            if_struct = True
+                        if 'Permanent HW addr: ' in sline and if_struct:
+                            macaddr = sline.split()[3]
+                            break
+                        line = f.readline()
+
+            with open("/sys/class/net/" + nic_name + "/mtu", 'r') as f:
+                mtu = f.read()
+
+            ip = str(get_ip(nic_name))
+
+            nics.append([nic_name, ip, macaddr, carrier, mtu])
+
+    return sorted(nics)
+
+def get_ten_pci():
+
+    ''' List ten nics pci addresses '''
+
+    nics = []
+    nics_list = listdir('/sys/class/net/')
+    for nic_name in nics_list:
+        if research('ten[0-9]+', nic_name):
+            with open("/sys/class/net/" + nic_name + "/device/uevent", 'r') as f:
+                for line in f:
+                    sline = line.strip()
+                    if "PCI_SLOT_NAME=" in sline:
+                        nics.append([nic_name , sline.split("=")[1]])
+
+    return sorted(nics)
+
+def mesh_ping(mesh):
+
+    ''' One to many ICMP check
+
+    :param hosts: Target hosts. Type: list of ip addresses
+
+    '''
+
+    io = []
+    minion_id = __salt__['config.get']('id')
+
+    for host, hostobj in mesh:
+        if host == minion_id:
+            for mesh_net, addr, targets in hostobj:
+                if addr in targets:
+                    targets.remove(addr)
+                for tgt in targets:
+                    # This one will run in parallel with everyone else
+                    worker = Popen("ping -c 1 -w 1 -W 1 " + str(tgt), \
+                        shell=True, stdout=PIPE, stderr=PIPE)
+                    ping_out = worker.communicate()[0]
+                    if worker.returncode != 0:
+                        io.append(mesh_net + ': ' + addr + ' -> ' + tgt + ': Failed')
+
+    return io
+
+def minion_list():
+
+    ''' List registered minions '''
+
+    return listdir('/etc/salt/pki/master/minions/')
+
+def verify_addresses():
+
+    ''' Verify addresses taken from pillars '''
+
+    nodes = nodes_addresses()
+    verifier = {}
+    failed = []
+
+    for node, nodeobj in nodes:
+        for item in nodeobj:
+            addr = item[1]
+            if addr in verifier:
+                failed.append([node,verifier[addr],addr])
+            else:
+                verifier[addr] = node
+
+    if failed:
+        logger.error("FAILED. Duplicates found")
+        logger.error(failed)
+        return False
+    else:
+        logger.setLevel(logging.INFO)
+        logger.info(["PASSED"])
+        return True
+
+def nodes_addresses():
+
+    ''' List servers addresses '''
+
+    compound = 'linux:network:interface'
+    out = __salt__['saltutil.cmd']( tgt='I@' + compound,
+                                    tgt_type='compound',
+                                    fun='pillar.get',
+                                    arg=[compound],
+                                    timeout=10
+                                  ) or None
+
+    servers = []
+    for minion in minion_list():
+        addresses = []
+        if minion in out:
+            ifaces = out[minion]['ret']
+            for iface in ifaces:
+                ifobj = ifaces[iface]
+                if ifobj['enabled'] and 'address' in ifobj:
+                    if 'mesh' in ifobj:
+                        mesh = ifobj['mesh']
+                    else:
+                        mesh = 'default'
+                    addresses.append([mesh, ifobj['address']])
+            servers.append([minion,addresses])
+
+    return servers
+
+def get_mesh():
+
+    ''' Build addresses mesh '''
+
+    full_mesh = {}
+    nodes = nodes_addresses()
+
+    for node, nodeobj in nodes:
+        for item in nodeobj:
+            mesh = item[0]
+            addr = item[1]
+            if not mesh in full_mesh:
+                full_mesh[mesh] = []
+            full_mesh[mesh].append(addr)
+
+    for node, nodeobj in nodes:
+        for item in nodeobj:
+            mesh = item[0]
+            tgts = full_mesh[mesh]
+            item.append(tgts)
+
+    return nodes
+
+def ping_check():
+
+    ''' Ping addresses in a mesh '''
+
+    mesh = get_mesh()
+    out = __salt__['saltutil.cmd']( tgt='*',
+                                    tgt_type='glob',
+                                    fun='net_checks.mesh_ping',
+                                    arg=[mesh],
+                                    timeout=10
+                                  ) or None
+
+    failed = []
+
+    if out:
+        for minion in out:
+            ret = out[minion]['ret']
+            if ret:
+                failed.append(ret)
+    else:
+        failed = ["No response from minions"]
+
+    if failed:
+        logger.error("FAILED")
+        logger.error('\n'.join(str(x) for x in failed))
+        return False
+    else:
+        logger.setLevel(logging.INFO)
+        logger.info(["PASSED"])
+        return True
+
+def get_nics_csv(delim=","):
+
+    ''' List nics in csv format
+
+    :param delim: Delimiter char. Type: str
+
+    '''
+
+    header = "server,nic_name,ip_addr,mac_addr,link,chassis_id,chassis_name,port_mac,port_descr\n"
+    io = ""
+
+    # Try to reuse lldp output if possible
+    try:
+        lldp_info = Popen("lldpcli -f keyvalue s n s", shell=True, stdout=PIPE).communicate()[0]
+    except:
+        lldp_info = ""
+
+    for nic in get_nics():
+        lldp = ""
+        nic_name = nic[0]
+        if research('(one|ten|fourty)[0-9]+', nic_name):
+            # Check if we can fetch lldp data for that nic
+            for line in lldp_info.splitlines():
+                chassis = 'lldp.' + nic[0] + '.chassis'
+                port = 'lldp.' + nic[0] + '.port'
+                if chassis in line or port in line:
+                    lldp += delim + line.split('=')[1]
+        if not lldp:
+            lldp = delim + delim + delim + delim
+
+        io += __salt__['config.get']('id') + \
+              delim + nic_name + \
+              delim + str(nic[1]).strip() + \
+              delim + str(nic[2]).strip() + \
+              delim + str(nic[3]).strip() + \
+              delim + str(nic[4]).strip() + \
+              lldp + "\n"
+
+    return header + io
diff --git a/linux/files/cron_users.jinja b/linux/files/cron_users.jinja
new file mode 100644
index 0000000..fe47059
--- /dev/null
+++ b/linux/files/cron_users.jinja
@@ -0,0 +1,5 @@
+# This file is managed by Salt, do not edit
+{%- for user_name in users %}
+{{ user_name }}
+{%- endfor %}
+{# IMPORTANT: This file SHOULD ends with a newline #}
\ No newline at end of file
diff --git a/linux/files/login.defs.jinja b/linux/files/login.defs.jinja
new file mode 100644
index 0000000..572c558
--- /dev/null
+++ b/linux/files/login.defs.jinja
@@ -0,0 +1,62 @@
+{%- from "linux/map.jinja" import login_defs with context -%}
+# This file is managed by Salt, do not edit
+{%- set allowed_options = [
+    'CHFN_RESTRICT',
+    'CONSOLE_GROUPS',
+    'CREATE_HOME',
+    'DEFAULT_HOME',
+    'ENCRYPT_METHOD',
+    'ENV_HZ',
+    'ENV_PATH',
+    'ENV_SUPATH',
+    'ERASECHAR',
+    'FAIL_DELAY',
+    'FAKE_SHELL',
+    'GID_MAX',
+    'GID_MIN',
+    'HUSHLOGIN_FILE',
+    'KILLCHAR',
+    'LOG_OK_LOGINS',
+    'LOG_UNKFAIL_ENAB',
+    'LOGIN_RETRIES',
+    'LOGIN_TIMEOUT',
+    'MAIL_DIR',
+    'MAIL_FILE',
+    'MAX_MEMBERS_PER_GROUP',
+    'MD5_CRYPT_ENAB',
+    'PASS_MAX_DAYS',
+    'PASS_MIN_DAYS',
+    'PASS_WARN_AGE',
+    'SHA_CRYPT_MIN_ROUNDS',
+    'SHA_CRYPT_MAX_ROUNDS',
+    'SULOG_FILE',
+    'SU_NAME',
+    'SUB_GID_MIN',
+    'SUB_GID_MAX',
+    'SUB_GID_COUNT',
+    'SUB_UID_MIN',
+    'SUB_UID_MAX',
+    'SUB_UID_COUNT',
+    'SYS_GID_MAX',
+    'SYS_GID_MIN',
+    'SYS_UID_MAX',
+    'SYS_UID_MIN',
+    'SYSLOG_SG_ENAB',
+    'SYSLOG_SU_ENAB',
+    'TTYGROUP',
+    'TTYPERM',
+    'TTYTYPE_FILE',
+    'UID_MAX',
+    'UID_MIN',
+    'UMASK',
+    'USERDEL_CMD',
+    'USERGROUPS_ENAB'
+] %}
+{%- for opt_name in allowed_options %}
+  {%- if opt_name in login_defs %}
+    {%- set opt_params = login_defs.get(opt_name) %}
+    {%- if opt_params.get('enabled', true) %}
+{{ opt_name.ljust(20) }} {{ opt_params.value }}
+    {%- endif %}
+  {%- endif %}
+{%- endfor %}
diff --git a/linux/files/preferences_repo b/linux/files/preferences_repo
index 603d313..91e9f9b 100644
--- a/linux/files/preferences_repo
+++ b/linux/files/preferences_repo
@@ -1,8 +1,19 @@
-{%- from "linux/map.jinja" import system with context %}
-{%- set repo = system.repo[repo_name] %}
-{%- for pin in repo.pin %}
-{%- set package = pin.get('package', '*') %}
+{%- from "linux/map.jinja" import system with context -%}
+{%- set repo = system.repo[repo_name] -%}
+{%- if repo.pinning is defined  -%}
+  {%- for id,pin in repo.pinning|dictsort -%}
+    {% if pin.get('enabled', False) %}
+
+Package: {{ pin.get('package','*') }}
+Pin: {{ pin.pin }}
+Pin-Priority: {{ pin.priority }}
+    {%- endif %}
+  {%- endfor -%}
+{%- elif repo.pin is defined -%}
+  {%- for pin in repo.pin -%}
+    {%- set package = pin.get('package', '*') %}
 Package: {{ package }}
 Pin: {{ pin.pin }}
 Pin-Priority: {{ pin.priority }}
-{% endfor %}
+  {%- endfor %}
+{%- endif -%}
diff --git a/linux/map.jinja b/linux/map.jinja
index c333a89..4f8b5b7 100644
--- a/linux/map.jinja
+++ b/linux/map.jinja
@@ -85,6 +85,24 @@
     },
 }, grain='os_family', merge=salt['pillar.get']('linux:system')) %}
 
+{% set at = salt['grains.filter_by']({
+    'Debian': {
+        'enabled': false,
+        'pkgs': ['at'],
+        'services': ['atd'],
+        'user': {}
+    },
+}, grain='os_family', merge=salt['pillar.get']('linux:system:at')) %}
+
+{% set cron = salt['grains.filter_by']({
+    'Debian': {
+        'enabled': false,
+        'pkgs': ['cron'],
+        'services': ['cron'],
+        'user': {}
+    },
+}, grain='os_family', merge=salt['pillar.get']('linux:system:cron')) %}
+
 {% set banner = salt['grains.filter_by']({
     'BaseDefaults': {
         'enabled': false,
@@ -122,6 +140,70 @@
     },
 }, grain='os_family', merge=salt['pillar.get']('linux:system:auth:ldap')) %}
 
+{%- load_yaml as login_defs_defaults %}
+Debian:
+    CHFN_RESTRICT:
+        value: 'rwh'
+    DEFAULT_HOME:
+        value: 'yes'
+    ENCRYPT_METHOD:
+        value: 'SHA512'
+    ENV_PATH:
+        value: 'PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games'
+    ENV_SUPATH:
+        value: 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
+    ERASECHAR:
+        value: '0177'
+    FAILLOG_ENAB:
+        value: 'yes'
+    FTMP_FILE:
+        value: '/var/log/btmp'
+    GID_MAX:
+        value: '60000'
+    GID_MIN:
+        value: '1000'
+    HUSHLOGIN_FILE:
+        value: '.hushlogin'
+    KILLCHAR:
+        value: '025'
+    LOGIN_RETRIES:
+        value: '5'
+    LOGIN_TIMEOUT:
+        value: '60'
+    LOG_OK_LOGINS:
+        value: 'no'
+    LOG_UNKFAIL_ENAB:
+        value: 'no'
+    MAIL_DIR:
+        value: '/var/mail'
+    PASS_MAX_DAYS:
+        value: '99999'
+    PASS_MIN_DAYS:
+        value: '0'
+    PASS_WARN_AGE:
+        value: '7'
+    SU_NAME:
+        value: 'su'
+    SYSLOG_SG_ENAB:
+        value: 'yes'
+    SYSLOG_SU_ENAB:
+        value: 'yes'
+    TTYGROUP:
+        value: 'tty'
+    TTYPERM:
+        value: '0600'
+    UID_MAX:
+        value: '60000'
+    UID_MIN:
+        value: '1000'
+    UMASK:
+        value: '022'
+    USERGROUPS_ENAB:
+        value: 'yes'
+{%- endload %}
+{%- set login_defs = salt['grains.filter_by'](login_defs_defaults,
+    grain='os_family', merge=salt['pillar.get']('linux:system:login_defs')) %}
+
 {#    'network_name', #}
 
 {% set interface_params = [
diff --git a/linux/meta/fluentd.yml b/linux/meta/fluentd.yml
index a6f9cc4..f6d6720 100644
--- a/linux/meta/fluentd.yml
+++ b/linux/meta/fluentd.yml
@@ -77,6 +77,8 @@
             record:
               - name: severity_label
                 value: '${ {"TRACE"=>8,"DEBUG"=>7,"INFO"=>6,"NOTICE"=>5,"WARNING"=>4,"ERROR"=>3,"CRITICAL"=>2,"ALERT"=>1,"EMERGENCY"=>0}.key(record["Severity"].to_i) }'
+              - name: source
+                value: systemd
         match:
           rewrite_tag:
             tag: systemd.source
@@ -84,9 +86,9 @@
             rule:
               - name: ident
                 regexp: '^(.*)$'
-                result: __TAG__.$1
+                result: $1.systemd
           push_to_default:
-            tag: 'systemd.source.*'
+            tag: '*.systemd'
             type: copy
             store:
               - type: relabel
diff --git a/linux/network/dpdk.sls b/linux/network/dpdk.sls
index 2c7dcb9..786f7c8 100644
--- a/linux/network/dpdk.sls
+++ b/linux/network/dpdk.sls
@@ -106,7 +106,7 @@
 linux_network_dpdk_bond_interface_{{ interface_name }}:
   cmd.run:
     - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} add-bond {{ interface.bridge }} {{ interface_name }} {{ bond_interfaces.keys()|join(' ') }}"
-    - unless: "ovs-vsctl show | grep {{ interface_name }}"
+    - unless: "ovs-vsctl list-ports {{ interface.bridge }} | grep -w {{ interface_name }}"
     - require:
       - cmd: linux_network_dpdk_bridge_interface_{{ interface.bridge }}
 
@@ -178,8 +178,8 @@
 
 linux_network_dpdk_bridge_port_interface_{{ interface_name }}:
   cmd.run:
-    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} add-port {{ interface.bridge }} dpdk0 -- set Interface dpdk0 type=dpdk options:dpdk-devargs={{ interface.pci }}"
-    - unless: "ovs-vsctl show | grep dpdk0"
+    - name: "ovs-vsctl{%- if network.ovs_nowait %} --no-wait{%- endif %} add-port {{ interface.bridge }} {{ interface_name }} -- set Interface {{ interface_name }} type=dpdk options:dpdk-devargs={{ interface.pci }}"
+    - unless: "ovs-vsctl list-ports {{ interface.bridge }} | grep -w {{ interface_name }}"
     - require:
       - cmd: linux_network_dpdk_bridge_interface_{{ interface.bridge }}
 
diff --git a/linux/network/interface.sls b/linux/network/interface.sls
index c2d2a23..a39fc37 100644
--- a/linux/network/interface.sls
+++ b/linux/network/interface.sls
@@ -2,7 +2,8 @@
 {%- from "linux/map.jinja" import system with context %}
 {%- if network.enabled %}
 
-{%- if network.get('dpdk', {}).get('enabled', False) %}
+{%- set dpdk_enabled = network.get('dpdk', {}).get('enabled', False) %}
+{%- if dpdk_enabled %}
 include:
 - linux.network.dpdk
 {%- endif %}
@@ -117,7 +118,7 @@
   - name: {{ interface_name }}
   - bridge: {{ interface.bridge }}
   - require:
-    {%- if network.interface.get(interface.bridge, {}).get('type', 'ovs_bridge') == 'dpdk_ovs_bridge' %}
+    {%- if dpdk_enabled and network.interface.get(interface.bridge, {}).get('type', 'ovs_bridge') == 'dpdk_ovs_bridge' %}
     - cmd: linux_network_dpdk_bridge_interface_{{ interface.bridge }}
     {%- else %}
     - openvswitch_bridge: ovs_bridge_{{ interface.bridge }}
diff --git a/linux/storage/swap.sls b/linux/storage/swap.sls
index 7b8d82e..3b3fd80 100644
--- a/linux/storage/swap.sls
+++ b/linux/storage/swap.sls
@@ -53,6 +53,24 @@
 
 {%- endif %}
 
+{%- else %}
+
+{{ swap.device }}:
+  module.run:
+    - name: mount.rm_fstab
+    - m_name: none
+    - device: {{ swap.device }}
+    - onlyif: grep -q {{ swap.device }} /etc/fstab
+
+linux_disable_swap_{{ swap.engine }}_{{ swap.device }}:
+  cmd.run:
+  {%- if swap.engine == 'partition' %}
+    - name: 'swapoff {{ swap.device }}'
+  {%- elif swap.engine == 'file' %}
+    - name: 'swapoff {{ swap.device }} && rm -f {{ swap.device }}'
+  {%- endif %}
+    - onlyif: file -L -s {{ swap.device }} | grep -q 'swap file'
+
 {%- endif %}
 
 {%- endfor %}
diff --git a/linux/system/at.sls b/linux/system/at.sls
new file mode 100644
index 0000000..864ae0c
--- /dev/null
+++ b/linux/system/at.sls
@@ -0,0 +1,62 @@
+{%- from "linux/map.jinja" import system, at with context %}
+
+{%- if at.get('enabled', false) %}
+
+at_packages:
+  pkg.installed:
+    - names: {{ at.pkgs }}
+
+at_services:
+  service.running:
+    - enable: true
+    - names: {{ at.services }}
+    - require:
+      - pkg: at_packages
+  {%- if grains.get('noservices') %}
+    - onlyif: /bin/false
+  {%- endif %}
+
+  {%- set allow_users = [] %}
+  {%- for user_name, user_params in at.get('user', {}).items() %}
+    {%- set user_enabled = user_params.get('enabled', false) and
+        system.get('user', {}).get(
+          user_name, {'enabled': true}).get('enabled', true) %}
+    {%- if user_enabled %}
+      {%- do allow_users.append(user_name) %}
+    {%- endif %}
+  {%- endfor %}
+
+etc_at_allow:
+  {%- if allow_users %}
+  file.managed:
+    - name: /etc/at.allow
+    - template: jinja
+    - source: salt://linux/files/cron_users.jinja
+    - user: root
+    - group: daemon
+    - mode: 0640
+    - defaults:
+        users: {{ allow_users | yaml }}
+    - require:
+      - cron_packages
+  {%- else %}
+  file.absent:
+    - name: /etc/at.allow
+  {%- endif %}
+
+
+{#
+    /etc/at.deny should be absent to comply with
+    CIS 5.1.8 Ensure at/cron is restricted to authorized users
+#}
+etc_at_deny:
+  file.absent:
+    - name: /etc/at.deny
+
+{%- else %}
+
+fake_linux_system_at:
+  test.nop:
+    - comment: Fake state to satisfy 'require sls:linux.system.at'
+
+{%- endif %}
diff --git a/linux/system/cron.sls b/linux/system/cron.sls
new file mode 100644
index 0000000..a5f57a4
--- /dev/null
+++ b/linux/system/cron.sls
@@ -0,0 +1,87 @@
+{%- from "linux/map.jinja" import system, cron with context %}
+
+{%- if cron.get('enabled', false) %}
+
+cron_packages:
+  pkg.installed:
+    - names: {{ cron.pkgs }}
+
+cron_services:
+  service.running:
+    - enable: true
+    - names: {{ cron.services }}
+    - require:
+      - pkg: cron_packages
+  {%- if grains.get('noservices') %}
+    - onlyif: /bin/false
+  {%- endif %}
+
+  {%- set allow_users = [] %}
+  {%- for user_name, user_params in cron.get('user', {}).items() %}
+    {%- set user_enabled = user_params.get('enabled', false) and
+        system.get('user', {}).get(
+          user_name, {'enabled': true}).get('enabled', true) %}
+    {%- if user_enabled %}
+      {%- do allow_users.append(user_name) %}
+    {%- endif %}
+  {%- endfor %}
+
+etc_cron_allow:
+  {%- if allow_users %}
+  file.managed:
+    - name: /etc/cron.allow
+    - template: jinja
+    - source: salt://linux/files/cron_users.jinja
+    - user: root
+    - group: crontab
+    - mode: 0640
+    - defaults:
+        users: {{ allow_users | yaml }}
+    - require:
+      - cron_packages
+  {%- else %}
+  file.absent:
+    - name: /etc/cron.allow
+  {%- endif %}
+
+{#
+    /etc/cron.deny should be absent to comply with
+    CIS 5.1.8 Ensure at/cron is restricted to authorized users
+#}
+etc_cron_deny:
+  file.absent:
+    - name: /etc/cron.deny
+
+etc_crontab:
+  file.managed:
+    - name: /etc/crontab
+    - user: root
+    - group: root
+    - mode: 0600
+    - replace: False
+    - require:
+      - cron_packages
+
+etc_cron_dirs:
+  file.directory:
+    - names:
+      - /etc/cron.d
+      - /etc/cron.daily
+      - /etc/cron.hourly
+      - /etc/cron.monthly
+      - /etc/cron.weekly
+    - user: root
+    - group: root
+    - dir_mode: 0600
+    - recurse:
+      - ignore_files
+    - require:
+      - cron_packages
+
+{%- else %}
+
+fake_linux_system_cron:
+  test.nop:
+    - comment: Fake state to satisfy 'require sls:linux.system.cron'
+
+{%- endif %}
diff --git a/linux/system/grub.sls b/linux/system/grub.sls
index 74ea553..49277ff 100644
--- a/linux/system/grub.sls
+++ b/linux/system/grub.sls
@@ -7,6 +7,7 @@
     - makedirs: True
 
 {%- if grains['os_family'] == 'RedHat' %}
+  {%- set boot_grub_cfg = '/boot/grub2/grub.cfg' %}
 /etc/default/grub:
   file.append:
     - text:
@@ -14,14 +15,26 @@
 
 grub_update:
   cmd.wait:
-  - name: grub2-mkconfig -o /boot/grub2/grub.cfg
+  - name: grub2-mkconfig -o {{ boot_grub_cfg }}
 
 {%- else %}
+  {%- set boot_grub_cfg = '/boot/grub/grub.cfg' %}
 
-{%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %}
 grub_update:
   cmd.wait:
   - name: update-grub
-{%- endif %}
+  {%- if grains.get('virtual_subtype') in ['Docker', 'LXC'] %}
+  - onlyif: /bin/false
+  {%- endif %}
 
 {%- endif %}
+
+grub_cfg_permissions:
+  file.managed:
+    - name: {{ boot_grub_cfg }}
+    - user: 'root'
+    - owner: 'root'
+    - mode: '400'
+    - onlyif: test -f {{ boot_grub_cfg }}
+    - require:
+      - cmd: grub_update
diff --git a/linux/system/init.sls b/linux/system/init.sls
index ad3681a..20d39d9 100644
--- a/linux/system/init.sls
+++ b/linux/system/init.sls
@@ -3,6 +3,12 @@
 include:
 - linux.system.env
 - linux.system.profile
+- linux.system.shell
+{%- if system.login_defs is defined %}
+- linux.system.login_defs
+{%- endif %}
+- linux.system.at
+- linux.system.cron
 {%- if system.repo|length > 0 %}
 - linux.system.repo
 {%- endif %}
diff --git a/linux/system/job.sls b/linux/system/job.sls
index af42b58..4cdb946 100644
--- a/linux/system/job.sls
+++ b/linux/system/job.sls
@@ -3,45 +3,46 @@
 
 include:
 - linux.system.user
+- linux.system.cron
 
-{%- for name, job in system.job.items() %}
+  {%- for name, job in system.job.items() %}
+    {%- set job_user = job.get('user', 'root') %}
 
 linux_job_{{ job.command }}:
-  {%- if job.enabled|default(True) %}
+    {%- if job.get('enabled', True) %}
   cron.present:
     - name: >
         {{ job.command }}
-    {%- if job.get('identifier', True) %}
+      {%- if job.get('identifier', True) %}
     - identifier: {{ job.get('identifier', job.get('name', name)) }}
-    {%- endif %}
-    - user: {{ job.user|default("root") }}
-    {%- if job.minute is defined %}
+      {%- endif %}
+    - user: {{ job_user }}
+      {%- if job.minute is defined %}
     - minute: '{{ job.minute }}'
-    {%- endif %}
-    {%- if job.hour is defined %}
+      {%- endif %}
+      {%- if job.hour is defined %}
     - hour: '{{ job.hour }}'
-    {%- endif %}
-    {%- if job.daymonth is defined %}
+      {%- endif %}
+      {%- if job.daymonth is defined %}
     - daymonth: '{{ job.daymonth }}'
-    {%- endif %}
-    {%- if job.month is defined %}
+      {%- endif %}
+      {%- if job.month is defined %}
     - month: '{{ job.month }}'
-    {%- endif %}
-    {%- if job.dayweek is defined %}
+      {%- endif %}
+      {%- if job.dayweek is defined %}
     - dayweek: '{{ job.dayweek }}'
-    {%- endif %}
-    {%- if job.user|default("root") in system.get('user', {}).keys() %}
+      {%- endif %}
     - require:
-      - user: system_user_{{ job.user|default("root") }}
-    {%- endif %}
-  {%- else %}
+      - sls: linux.system.cron
+      {%- if job_user in system.get('user', {}).keys() %}
+      - user: system_user_{{ job_user }}
+      {%- endif %}
+    {%- else %}
   cron.absent:
     - name: {{ job.command }}
-    {%- if job.get('identifier', True) %}
+      {%- if job.get('identifier', True) %}
     - identifier: {{ job.get('identifier', job.get('name', name)) }}
+      {%- endif %}
     {%- endif %}
-  {%- endif %}
-
-{%- endfor %}
-
+  {%- endfor %}
 {%- endif %}
diff --git a/linux/system/kernel.sls b/linux/system/kernel.sls
index e6111c5..3dc3046 100644
--- a/linux/system/kernel.sls
+++ b/linux/system/kernel.sls
@@ -8,10 +8,10 @@
 {%- do kernel_boot_opts.append('elevator=' ~ system.kernel.elevator) if system.kernel.elevator is defined %}
 {%- do kernel_boot_opts.extend(system.kernel.boot_options) if system.kernel.boot_options is defined %}
 
-{%- if kernel_boot_opts %}
 include:
   - linux.system.grub
 
+{%- if kernel_boot_opts %}
 /etc/default/grub.d/99-custom-settings.cfg:
   file.managed:
     - contents: 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT {{ kernel_boot_opts|join(' ') }}"'
diff --git a/linux/system/login_defs.sls b/linux/system/login_defs.sls
new file mode 100644
index 0000000..f94348a
--- /dev/null
+++ b/linux/system/login_defs.sls
@@ -0,0 +1,13 @@
+{%- from "linux/map.jinja" import system with context %}
+{%- if system.enabled %}
+  {%- if system.login_defs is defined %}
+login_defs:
+  file.managed:
+    - name: /etc/login.defs
+    - source: salt://linux/files/login.defs.jinja
+    - template: jinja
+    - user: root
+    - group: root
+    - mode: 644
+  {%- endif %}
+{%- endif %}
diff --git a/linux/system/repo.sls b/linux/system/repo.sls
index 0c0b026..dd41afe 100644
--- a/linux/system/repo.sls
+++ b/linux/system/repo.sls
@@ -57,7 +57,7 @@
   file.absent
       {%- endif %}
 
-      {%- if repo.pin is defined %}
+      {%- if repo.pin is defined or repo.pinning is defined %}
 linux_repo_{{ name }}_pin:
   file.managed:
     - name: /etc/apt/preferences.d/{{ name }}
diff --git a/linux/system/shell.sls b/linux/system/shell.sls
new file mode 100644
index 0000000..29fc1dc
--- /dev/null
+++ b/linux/system/shell.sls
@@ -0,0 +1,45 @@
+{%- from "linux/map.jinja" import system with context %}
+{%- if system.enabled %}
+  {%- if system.shell is defined %}
+
+    {%- if system.shell.umask is defined %}
+etc_bash_bashrc_umask:
+  file.blockreplace:
+    - name: /etc/bash.bashrc
+    - marker_start: "# BEGIN CIS 5.4.4 default user umask"
+    - marker_end: "# END CIS 5.4.4 default user umask"
+    - content: "umask {{ system.shell.umask }}"
+    - append_if_not_found: True
+    - onlyif: test -f /etc/bash.bashrc
+
+etc_profile_umask:
+  file.blockreplace:
+    - name: /etc/profile
+    - marker_start: "# BEGIN CIS 5.4.4 default user umask"
+    - marker_end: "# END CIS 5.4.4 default user umask"
+    - content: "umask {{ system.shell.umask }}"
+    - append_if_not_found: True
+    - onlyif: test -f /etc/profile
+    {%- endif %}
+
+    {%- if system.shell.timeout is defined %}
+etc_bash_bashrc_timeout:
+  file.blockreplace:
+    - name: /etc/bash.bashrc
+    - marker_start: "# BEGIN CIS 5.4.5 default user shell timeout"
+    - marker_end: "# END CIS 5.4.5 default user shell timeout"
+    - content: "TMOUT={{ system.shell.timeout }}"
+    - append_if_not_found: True
+    - onlyif: test -f /etc/bash.bashrc
+
+etc_profile_timeout:
+  file.blockreplace:
+    - name: /etc/profile
+    - marker_start: "# BEGIN CIS 5.4.5 default user shell timeout"
+    - marker_end: "# END CIS 5.4.5 default user shell timeout"
+    - content: "TMOUT={{ system.shell.timeout }}"
+    - append_if_not_found: True
+    - onlyif: test -f /etc/profile
+    {%- endif %}
+  {%- endif %}
+{%- endif %}
diff --git a/linux/system/sysfs.sls b/linux/system/sysfs.sls
index 8440384..a4e28bf 100644
--- a/linux/system/sysfs.sls
+++ b/linux/system/sysfs.sls
@@ -11,6 +11,8 @@
     - require:
       - pkg: linux_sysfs_package
 
+{% set apply = system.get('sysfs', {}).pop('enable_apply', True) %}
+
 {%- for name, sysfs in system.get('sysfs', {}).items() %}
 
 /etc/sysfs.d/{{ name }}.conf:
@@ -32,6 +34,8 @@
 {%- set sysfs_list = sysfs %}
 {%- endif %}
 
+{%- if apply %}
+
 {%- for item in sysfs_list %}
 {%- set list_idx = loop.index %}
 {%- for key, value in item.items() %}
@@ -48,4 +52,7 @@
   {%- endfor %}
 
 {%- endfor %}
+
+{%- endif %}
+
 {%- endfor %}
diff --git a/linux/system/user.sls b/linux/system/user.sls
index 7ffdae6..89d2cbb 100644
--- a/linux/system/user.sls
+++ b/linux/system/user.sls
@@ -43,12 +43,25 @@
   {%- endif %}
   {%- if user.system is defined and user.system %}
   - system: True
+  - shell: {{ user.get('shell', '/bin/false') }}
   {%- else %}
   - shell: {{ user.get('shell', '/bin/bash') }}
   {%- endif %}
   {%- if user.uid is defined and user.uid %}
   - uid: {{ user.uid }}
   {%- endif %}
+  {%- if user.maxdays is defined %}
+  - maxdays: {{ user.maxdays }}
+  {%- endif %}
+  {%- if user.mindays is defined %}
+  - mindays: {{ user.mindays }}
+  {%- endif %}
+  {%- if user.warndays is defined %}
+  - warndays: {{ user.warndays }}
+  {%- endif %}
+  {%- if user.inactdays is defined %}
+  - inactdays: {{ user.inactdays }}
+  {%- endif %}
   - require: {{ requires|yaml }}
 
 system_user_home_{{ user.home }}:
diff --git a/metadata/service/system/cis/cis-1-1-1-1.yml b/metadata/service/system/cis/cis-1-1-1-1.yml
new file mode 100644
index 0000000..2331a54
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-1-1.yml
@@ -0,0 +1,37 @@
+# 1.1.1.1 Ensure mounting of cramfs filesystems is disabled
+#
+# Description
+# ===========
+# The cramfs filesystem type is a compressed read-only Linux filesystem
+# embedded in small footprint systems. A cramfs image can be used without
+# having to first decompress the image.
+#
+# Rationale
+# =========
+# Removing support for unneeded filesystem types reduces the local attack
+# surface of the server. If this filesystem type is not needed, disable it.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v cramfs
+#   install /bin/true
+#   # lsmod | grep cramfs
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install cramfs /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          cramfs:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-1-1-1-2.yml b/metadata/service/system/cis/cis-1-1-1-2.yml
new file mode 100644
index 0000000..f84b56f
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-1-2.yml
@@ -0,0 +1,36 @@
+# 1.1.1.2 Ensure mounting of freevxfs filesystems is disabled
+#
+# Description
+# ===========
+# The freevxfs filesystem type is a free version of the Veritas type
+# filesystem. This is the primary filesystem type for HP-UX operating systems.
+#
+# Rationale
+# =========
+# Removing support for unneeded filesystem types reduces the local attack
+# surface of the system. If this filesystem type is not needed, disable it.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v freevxfs
+#   install /bin/true
+#   # lsmod | grep freevxfs
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install freevxfs /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          freevxfs:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-1-1-1-3.yml b/metadata/service/system/cis/cis-1-1-1-3.yml
new file mode 100644
index 0000000..91390b5
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-1-3.yml
@@ -0,0 +1,36 @@
+# 1.1.1.3 Ensure mounting of jffs2 filesystems is disabled
+#
+# Description
+# ===========
+# The jffs2 (journaling flash filesystem 2) filesystem type is a
+# log-structured filesystem used in flash memory devices.
+#
+# Rationale
+# =========
+# Removing support for unneeded filesystem types reduces the local attack
+# surface of the system. If this filesystem type is not needed, disable it.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v jffs2
+#   install /bin/true
+#   # lsmod | grep jffs2
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install jffs2 /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          jffs2:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-1-1-1-4.yml b/metadata/service/system/cis/cis-1-1-1-4.yml
new file mode 100644
index 0000000..c246ad2
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-1-4.yml
@@ -0,0 +1,36 @@
+# 1.1.1.4 Ensure mounting of hfs filesystems is disabled
+#
+# Description
+# ===========
+# The hfs filesystem type is a hierarchical filesystem that allows
+# you to mount Mac OS filesystems.
+#
+# Rationale
+# =========
+# Removing support for unneeded filesystem types reduces the local attack
+# surface of the system. If this filesystem type is not needed, disable it.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v hfs
+#   install /bin/true
+#   # lsmod | grep hfs
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install hfs /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          hfs:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-1-1-1-5.yml b/metadata/service/system/cis/cis-1-1-1-5.yml
new file mode 100644
index 0000000..e258052
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-1-5.yml
@@ -0,0 +1,36 @@
+# 1.1.1.5 Ensure mounting of hfsplus filesystems is disabled
+#
+# Description
+# ===========
+# The hfsplus filesystem type is a hierarchical filesystem designed to
+# replace hfs that allows you to mount Mac OS filesystems.
+#
+# Rationale
+# =========
+# Removing support for unneeded filesystem types reduces the local attack
+# surface of the system. If this filesystem type is not needed, disable it.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v hfsplus
+#   install /bin/true
+#   # lsmod | grep hfsplus
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install hfsplus /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          hfsplus:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-1-1-1-6.yml b/metadata/service/system/cis/cis-1-1-1-6.yml
new file mode 100644
index 0000000..59da5db
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-1-6.yml
@@ -0,0 +1,43 @@
+# 1.1.1.6 Ensure mounting of squashfs filesystems is disabled
+#
+# Description
+# ===========
+# The squashfs filesystem type is a compressed read-only Linux filesystem
+# embedded in small footprint systems (similar to cramfs). A squashfs image
+# can be used without having to first decompress the image.
+#
+# Rationale
+# =========
+# Removing support for unneeded filesystem types reduces the local attack
+# surface of the server. If this filesystem type is not needed, disable it.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v squashfs
+#   install /bin/true
+#   # lsmod | grep squashfs
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install squashfs /bin/true
+#
+# NOTE
+# ====
+# In Ubuntu 16.04 squashfs is built into kernel, and 'install' command
+# from modprobe.d dir has no effect. However, this is still checked by
+# CIS-CAT in Ubuntu 16.04 benchmark v.1.0.0. This was removed in v.1.1.0.
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          squashfs:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-1-1-1-7.yml b/metadata/service/system/cis/cis-1-1-1-7.yml
new file mode 100644
index 0000000..0102220
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-1-7.yml
@@ -0,0 +1,38 @@
+# 1.1.1.7 Ensure mounting of udf filesystems is disabled
+#
+# Description
+# ===========
+# The udf filesystem type is the universal disk format used to implement
+# ISO/IEC 13346 and ECMA-167 specifications. This is an open vendor filesystem
+# type for data storage on a broad range of media. This filesystem type is
+# necessary to support writing DVDs and newer optical disc formats.
+#
+# Rationale
+# =========
+# Removing support for unneeded filesystem types reduces the local attack
+# surface of the server. If this filesystem type is not needed, disable it.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v udf
+#   install /bin/true
+#   # lsmod | grep udf
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install udf /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          udf:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-1-1-1-8.yml b/metadata/service/system/cis/cis-1-1-1-8.yml
new file mode 100644
index 0000000..7c06c8e
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-1-8.yml
@@ -0,0 +1,50 @@
+# 1.1.1.8 Ensure mounting of FAT filesystems is disabled
+#
+# Description
+# ===========
+# The FAT filesystem format is primarily used on older windows systems and
+# portable USB drives or flash modules. It comes in three types FAT12, FAT16,
+# and FAT32 all of which are supported by the vfat kernel module.
+#
+# Rationale
+# =========
+# Removing support for unneeded filesystem types reduces the local attack
+# surface of the server. If this filesystem type is not needed, disable it.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v vfat
+#   install /bin/true
+#   # lsmod | grep vfat
+#   <No output>
+#
+# Remediation
+# ===========
+#
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install vfat /bin/true
+#
+# Impact
+# ======
+# FAT filesystems are often used on portable USB sticks and other flash
+# media are commonly used to transfer files between workstations, removing
+# VFAT support may prevent the ability to transfer files in this way.
+#
+# NOTE
+# ====
+# In Ubuntu 16.04 vfat is built into kernel, and 'install' command
+# from modprobe.d dir has no effect. However, this is still checked by
+# CIS-CAT in Ubuntu 16.04 benchmark v.1.0.0. This was removed in v.1.1.0.
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          vfat:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-1-1-14_15_16.yml b/metadata/service/system/cis/cis-1-1-14_15_16.yml
new file mode 100644
index 0000000..d9c7e72
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-14_15_16.yml
@@ -0,0 +1,95 @@
+# CIS 1.1.14 Ensure nodev option set on /dev/shm partition (Scored)
+#
+# Description
+# ===========
+# The nodev mount option specifies that the filesystem cannot contain special
+# devices.
+#
+# Rationale
+# =========
+# Since the /run/shm filesystem is not intended to support devices, set this
+# option to ensure that users cannot attempt to create special devices in
+# /dev/shm partitions.
+#
+# Audit
+# =====
+# Run the following command and verify that the nodev option is set on /dev/shm .
+#
+#   # mount | grep /dev/shm
+#   shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime)
+#
+# Remediation
+# ===========
+#
+# Edit the /etc/fstab file and add nodev to the fourth field (mounting options)
+# for the /dev/shm partition. See the fstab(5) manual page for more information.
+# Run the following command to remount /dev/shm :
+#
+#   # mount -o remount,nodev /dev/shm
+#
+# CIS 1.1.15 Ensure nosuid option set on /dev/shm partition (Scored)
+#
+# Description
+# ===========
+# The nosuid mount option specifies that the filesystem cannot contain setuid
+# files.
+#
+# Rationale
+# =========
+# Setting this option on a file system prevents users from introducing
+# privileged programs onto the system and allowing non-root users to execute them.
+#
+# Audit
+# =====
+# Run the following command and verify that the no suid option is set on /dev/shm .
+#
+#   # mount | grep /dev/shm
+#   shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime)
+#
+# Remediation
+# ===========
+# Edit the /etc/fstab file and add nosuid to the fourth field (mounting options)
+# for the /dev/shm partition. See the fstab(5) manual page for more information.
+# Run the following command to remount /dev/shm :
+#
+#   # mount -o remount,nosuid /dev/shm
+#
+# 1.1.16 Ensure noexec option set on /dev/shm partition (Scored)
+#
+# Description
+# ===========
+# The noexec mount option specifies that the filesystem cannot contain
+# executable binaries.
+#
+# Rationale
+# =========
+# Setting this option on a file system prevents users from executing programs
+# from shared memory. This deters users from introducing potentially malicious
+# software on the system.
+#
+# Audit
+# =====
+# Run the following command and verify that the noexec option is set on /run/shm .
+#
+#   # mount | grep /dev/shm
+#   shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime)
+#
+# Remediation
+# ===========
+# Edit the /etc/fstab file and add noexec to the fourth field (mounting options)
+# for the /dev/shm partition. See the fstab(5) manual page for more information.
+# Run the following command to remount /dev/shm :
+#
+#   # mount -o remount,noexec /dev/shm
+#
+parameters:
+  linux:
+    storage:
+      mount:
+        ensure_dev_shm_mount_options:
+          enabled: true
+          file_system: tmpfs
+          device: shm
+          path: /dev/shm
+          opts: rw,nosuid,nodev,noexec,relatime
+
diff --git a/metadata/service/system/cis/cis-1-1-21.yml b/metadata/service/system/cis/cis-1-1-21.yml
new file mode 100644
index 0000000..da84f49
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-1-21.yml
@@ -0,0 +1,53 @@
+# CIS 1.1.21 Disable Automounting
+#
+# Description
+# ===========
+# autofs allows automatic mounting of devices, typically including CD/DVDs
+# and USB drives.
+#
+# Rationale
+# =========
+# With automounting enabled anyone with physical access could attach a USB
+# drive or disc and have its contents available in system even if they lacked
+# permissions to mount it themselves.
+#
+# Audit
+# =====
+# Run the following command to verify autofs is not enabled:
+#
+#   # systemctl is-enabled autofs
+#   disabled
+#
+# Verify result is not "enabled".
+#
+# Remediation
+# ===========
+#
+# Run the following command to disable autofs :
+#
+#   # systemctl disable autofs
+#
+# Impact
+# ======
+# The use portable hard drives is very common for workstation users. If your
+# organization allows the use of portable storage or media on workstations
+# and physical access controls to workstations is considered adequate there
+# is little value add in turning off automounting.
+#
+# Notes
+# =====
+# This control should align with the tolerance of the use of portable drives
+# and optical media in the organization. On a server requiring an admin to
+# manually mount media can be part of defense-in-depth to reduce the risk of
+# unapproved software or information being introduced or proprietary software
+# or information being exfiltrated. If admins commonly use flash drives and
+# Server access has sufficient physical controls, requiring manual mounting
+# may not increase security.
+#
+parameters:
+  linux:
+    system:
+      service:
+        autofs:
+          status: disabled
+
diff --git a/metadata/service/system/cis/cis-1-5-4.yml b/metadata/service/system/cis/cis-1-5-4.yml
new file mode 100644
index 0000000..5583d80
--- /dev/null
+++ b/metadata/service/system/cis/cis-1-5-4.yml
@@ -0,0 +1,37 @@
+# CIS 1.5.4 Ensure prelink is disabled
+#
+# Description
+# ===========
+# prelink is a program that modifies ELF shared libraries and ELF dynamically
+# linked binaries in such a way that the time needed for the dynamic linker to
+# perform relocations at startup significantly decreases.
+#
+# Rationale
+# =========
+# The prelinking feature can interfere with the operation of AIDE, because it
+# changes binaries. Prelinking can also increase the vulnerability of the system
+# if a malicious user is able to compromise a common library such as libc.
+#
+# Audit
+# =====
+# Run the following command and verify prelink is not installed:
+#
+#   # dpkg -s prelink
+#
+# Remediation
+# ===========
+# Run the following command to restore binaries to normal:
+#
+#   # prelink -ua
+#
+# Run the following command to uninstall prelink :
+#
+#   # apt-get remove prelink
+#
+parameters:
+  linux:
+    system:
+      package:
+        prelink:
+          version: removed
+
diff --git a/metadata/service/system/cis/cis-2-3-1.yml b/metadata/service/system/cis/cis-2-3-1.yml
new file mode 100644
index 0000000..6116f36
--- /dev/null
+++ b/metadata/service/system/cis/cis-2-3-1.yml
@@ -0,0 +1,43 @@
+# 2.3.1 Ensure NIS Client is not installed
+#
+# Description
+# ===========
+# The Network Information Service (NIS), formerly known as Yellow Pages,
+# is a client-server directory service protocol used to distribute system
+# configuration files. The NIS client ( ypbind ) was used to bind a machine
+# to an NIS server and receive the distributed configuration files.
+#
+# Rationale
+# =========
+# The NIS service is inherently an insecure system that has been vulnerable
+# to DOS attacks, buffer overflows and has poor authentication for querying
+# NIS maps. NIS generally has been replaced by such protocols as Lightweight
+# Directory Access Protocol (LDAP). It is recommended that the service be
+# removed.
+#
+# Audit
+# =====
+# Run the following command and verify nis is not installed:
+#
+#   dpkg -s nis
+#
+# Remediation
+# ===========
+# Run the following command to uninstall nis:
+#
+#   apt-get remove nis
+#
+# Impact
+# ======
+# Many insecure service clients are used as troubleshooting tools and in
+# testing environments. Uninstalling them can inhibit capability to test
+# and troubleshoot. If they are required it is advisable to remove the clients
+# after use to prevent accidental or intentional misuse.
+#
+parameters:
+  linux:
+    system:
+      package:
+        nis:
+          version: removed
+
diff --git a/metadata/service/system/cis/cis-2-3-2.yml b/metadata/service/system/cis/cis-2-3-2.yml
new file mode 100644
index 0000000..ecbfa6a
--- /dev/null
+++ b/metadata/service/system/cis/cis-2-3-2.yml
@@ -0,0 +1,55 @@
+# 2.3.2 Ensure rsh client is not installed
+#
+# Description
+# ===========
+# The rsh package contains the client commands for the rsh services.
+#
+# Rationale
+# =========
+# These legacy clients contain numerous security exposures and have been
+# replaced with the more secure SSH package. Even if the server is removed,
+# it is best to ensure the clients are also removed to prevent users from
+# inadvertently attempting to use these commands and therefore exposing
+# their credentials. Note that removing the rsh package removes the
+# clients for rsh , rcp and rlogin .
+#
+# Audit
+# =====
+# Run the following commands and verify rsh is not installed:
+#
+#   dpkg -s rsh-client
+#   dpkg -s rsh-redone-client
+#
+# Remediation
+# ===========
+# Run the following command to uninstall rsh :
+#
+#   apt-get remove rsh-client rsh-redone-client
+#
+# Impact
+# ======
+# Many insecure service clients are used as troubleshooting tools and in
+# testing environments. Uninstalling them can inhibit capability to test
+# and troubleshoot. If they are required it is advisable to remove the
+# clients after use to prevent accidental or intentional misuse.
+#
+# NOTE
+# ====
+# It is not possible to remove rsh-client by means of SaltStack because
+# of the way SaltStack checks that package was really removed. 'rsh-client'
+# is "provided" by openssh-client package, and SaltStack thinks that
+# it is the same as 'rsh-client is installed'. So each time we try to
+# remove 'rsh-client' on a system where 'openssh-client' is installed
+# (that's almost every system), we got state failure.
+# This was fixed in upstream SaltStack in 2018, not sure where we start using
+# this version. Until that moment 'rsh-client' should remain unmanaged.
+#
+parameters:
+  linux:
+    system:
+      package:
+#        rsh-client:
+#          version: removed
+        rsh-redone-client:
+          version: removed
+
diff --git a/metadata/service/system/cis/cis-2-3-3.yml b/metadata/service/system/cis/cis-2-3-3.yml
new file mode 100644
index 0000000..859754b
--- /dev/null
+++ b/metadata/service/system/cis/cis-2-3-3.yml
@@ -0,0 +1,39 @@
+# 2.3.3 Ensure talk client is not installed
+#
+# Description
+# ===========
+# The talk software makes it possible for users to send and receive messages
+# across systems through a terminal session. The talk client, which allows
+# initialization of talk sessions, is installed by default.
+#
+# Rationale
+# =========
+# The software presents a security risk as it uses unencrypted protocols
+# for communication.
+#
+# Audit
+# =====
+# Run the following command and verify talk is not installed:
+#
+#   dpkg -s talk
+#
+# Remediation
+# ===========
+# Run the following command to uninstall talk :
+#
+#   apt-get remove talk
+#
+# Impact
+# ======
+# Many insecure service clients are used as troubleshooting tools and in
+# testing environments. Uninstalling them can inhibit capability to test
+# and troubleshoot. If they are required it is advisable to remove the clients
+# after use to prevent accidental or intentional misuse.
+#
+parameters:
+  linux:
+    system:
+      package:
+        talk:
+          version: removed
+
diff --git a/metadata/service/system/cis/cis-2-3-4.yml b/metadata/service/system/cis/cis-2-3-4.yml
new file mode 100644
index 0000000..34c8eb2
--- /dev/null
+++ b/metadata/service/system/cis/cis-2-3-4.yml
@@ -0,0 +1,40 @@
+# 2.3.4 Ensure telnet client is not installed
+#
+# Description
+# ===========
+# The telnet package contains the telnet client, which allows users to start
+# connections to other systems via the telnet protocol.
+#
+# Rationale
+# =========
+# The telnet protocol is insecure and unencrypted. The use of an unencrypted
+# transmission medium could allow an unauthorized user to steal credentials.
+# The ssh package provides an encrypted session and stronger security and is
+# included in most Linux distributions.
+#
+# Audit
+# =====
+# Run the following command and verify telnet is not installed:
+#
+#   # dpkg -s telnet
+#
+# Remediation
+# ===========
+# Run the following command to uninstall telnet :
+#
+#   # apt-get remove telnet
+#
+# Impact
+# ======
+# Many insecure service clients are used as troubleshooting tools and in
+# testing environments. Uninstalling them can inhibit capability to test and
+# troubleshoot. If they are required it is advisable to remove the clients
+# after use to prevent accidental or intentional misuse.
+#
+parameters:
+  linux:
+    system:
+      package:
+        telnet:
+          version: removed
+
diff --git a/metadata/service/system/cis/cis-3-5-1.yml b/metadata/service/system/cis/cis-3-5-1.yml
new file mode 100644
index 0000000..b232990
--- /dev/null
+++ b/metadata/service/system/cis/cis-3-5-1.yml
@@ -0,0 +1,38 @@
+# 3.5.2 Ensure DCCP is disabled
+#
+# Description
+# ===========
+# The Datagram Congestion Control Protocol (DCCP) is a transport layer protocol
+# that supports streaming media and telephony. DCCP provides a way to gain
+# access to congestion control, without having to do it at the application
+# layer, but does not provide in-sequence delivery.
+#
+# Rationale
+# =========
+# If the protocol is not required, it is recommended that the drivers not be
+# installed to reduce the potential attack surface.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v dccp
+#   install /bin/true
+#   # lsmod | grep dccp
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install dccp /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          dccp:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-3-5-2.yml b/metadata/service/system/cis/cis-3-5-2.yml
new file mode 100644
index 0000000..0207eb9
--- /dev/null
+++ b/metadata/service/system/cis/cis-3-5-2.yml
@@ -0,0 +1,41 @@
+# 3.5.2 Ensure SCTP is disabled
+#
+# Description
+# ===========
+# The Stream Control Transmission Protocol (SCTP) is a transport layer
+# protocol used to support message oriented communication, with several
+# streams of messages in one connection. It serves a similar function as
+# TCP and UDP, incorporating features of both. It is message-oriented
+# like UDP, and ensures reliable in-sequence transport of messages with
+# congestion control like TCP.
+#
+# Rationale
+# =========
+# If the protocol is not being used, it is recommended that kernel module
+# not be loaded, disabling the service to reduce the potential attack surface.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v sctp
+#   install /bin/true
+#   # lsmod | grep sctp
+#   <No output>
+#
+# Remediation
+# ===========
+#
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install sctp /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          sctp:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-3-5-3.yml b/metadata/service/system/cis/cis-3-5-3.yml
new file mode 100644
index 0000000..723de8b
--- /dev/null
+++ b/metadata/service/system/cis/cis-3-5-3.yml
@@ -0,0 +1,37 @@
+# 3.5.3 Ensure RDS is disabled
+#
+# Description
+# ===========
+# The Reliable Datagram Sockets (RDS) protocol is a transport layer protocol
+# designed to provide low-latency, high-bandwidth communications between
+# cluster nodes. It was developed by the Oracle Corporation.
+#
+# Rationale
+# =========
+# If the protocol is not being used, it is recommended that kernel module
+# not be loaded, disabling the service to reduce the potential attack surface.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v rds
+#   install /bin/true
+#   # lsmod | grep rds
+#   <No output>
+#
+# Remediation
+# ===========
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install rds /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          rds:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-3-5-4.yml b/metadata/service/system/cis/cis-3-5-4.yml
new file mode 100644
index 0000000..6a4920c
--- /dev/null
+++ b/metadata/service/system/cis/cis-3-5-4.yml
@@ -0,0 +1,37 @@
+# 3.5.4 Ensure TIPC is disabled
+#
+# Description
+# ===========
+# The Transparent Inter-Process Communication (TIPC) protocol is designed
+# to provide communication between cluster nodes.
+#
+# Rationale
+# =========
+# If the protocol is not being used, it is recommended that kernel module
+# not be loaded, disabling the service to reduce the potential attack surface.
+#
+# Audit
+# =====
+# Run the following commands and verify the output is as indicated:
+#
+#   # modprobe -n -v tipc
+#   install /bin/true
+#   # lsmod | grep tipc
+#   <No output>
+#
+# Remediation
+# ===========
+#
+# Edit or create the file /etc/modprobe.d/CIS.conf and add the following line:
+#
+#   install tipc /bin/true
+#
+parameters:
+  linux:
+    system:
+      kernel:
+        module:
+          tipc:
+            install:
+              command: /bin/true
+
diff --git a/metadata/service/system/cis/cis-5-4-1-1.yml b/metadata/service/system/cis/cis-5-4-1-1.yml
new file mode 100644
index 0000000..8b82466
--- /dev/null
+++ b/metadata/service/system/cis/cis-5-4-1-1.yml
@@ -0,0 +1,52 @@
+# CIS 5.4.1.1 Ensure password expiration is 90 days or less (Scored)
+#
+# Description
+# ===========
+# The PASS_MAX_DAYS parameter in /etc/login.defs allows an administrator to
+# force passwords to expire once they reach a defined age. It is recommended
+# that the PASS_MAX_DAYS parameter be set to less than or equal to 90 days.
+#
+# Rationale
+# =========
+# The window of opportunity for an attacker to leverage compromised credentials
+# or successfully compromise credentials via an online brute force attack is
+# limited by the age of the password. Therefore, reducing the maximum age of a
+# password also reduces an attacker's window of opportunity.
+#
+# Audit
+# =====
+# Run the following command and verify PASS_MAX_DAYS is 90 or less:
+#
+#   # grep PASS_MAX_DAYS /etc/login.defs
+#   PASS_MAX_DAYS 90
+#
+# Verify all users with a password have their maximum days between password
+# change set to 90 or less:
+#
+#   # egrep ^[^:]+:[^\!*] /etc/shadow | cut -d: -f1
+#   <list of users>
+#   # chage --list <user>
+#   Maximum number of days between password change: 90
+#
+# Remediation
+# ===========
+# Set the PASS_MAX_DAYS parameter to 90 in /etc/login.defs :
+#
+#   PASS_MAX_DAYS 90
+#
+# Modify user parameters for all users with a password set to match:
+#
+#   # chage --maxdays 90 <user>
+#
+# Notes
+# =====
+# You can also check this setting in /etc/shadow directly. The 5th field
+# should be 90 or less for all users with a password.
+#
+parameters:
+  linux:
+    system:
+      login_defs:
+        PASS_MAX_DAYS:
+          value: 90
+
diff --git a/metadata/service/system/cis/cis-5-4-1-2.yml b/metadata/service/system/cis/cis-5-4-1-2.yml
new file mode 100644
index 0000000..50543ca
--- /dev/null
+++ b/metadata/service/system/cis/cis-5-4-1-2.yml
@@ -0,0 +1,52 @@
+# CIS 5.4.1.2 Ensure minimum days between password changes is 7 or more (Scored)
+#
+# Description
+# ===========
+# The PASS_MIN_DAYS parameter in /etc/login.defs allows an administrator to
+# prevent users from changing their password until a minimum number of days
+# have passed since the last time the user changed their password. It is
+# recommended that PASS_MIN_DAYS parameter be set to 7 or more days.
+#
+# Rationale
+# =========
+# By restricting the frequency of password changes, an administrator can
+# prevent users from repeatedly changing their password in an attempt to
+# circumvent password reuse controls.
+#
+# Audit
+# =====
+# Run the following command and verify PASS_MIN_DAYS is 7 or more:
+#
+#   # grep PASS_MIN_DAYS /etc/login.defs
+#   PASS_MIN_DAYS 7
+#
+# Verify all users with a password have their minimum days between password
+# change set to 7 or more:
+#
+#   # egrep ^[^:]+:[^\!*] /etc/shadow | cut -d: -f1
+#   <list of users>
+#   # chage --list <user>
+#   Minimum number of days between password change: 7
+#
+# Remediation
+# ===========
+# Set the PASS_MIN_DAYS parameter to 7 in /etc/login.defs :
+#
+#   PASS_MIN_DAYS 7
+#
+# Modify user parameters for all users with a password set to match:
+#
+#   # chage --mindays 7 <user>
+#
+# Notes
+# =====
+# You can also check this setting in /etc/shadow directly. The 5th field
+# should be 7 or more for all users with a password.
+#
+parameters:
+  linux:
+    system:
+      login_defs:
+        PASS_MIN_DAYS:
+          value: 7
+
diff --git a/metadata/service/system/cis/cis-5-4-1-3.yml b/metadata/service/system/cis/cis-5-4-1-3.yml
new file mode 100644
index 0000000..3567f2a
--- /dev/null
+++ b/metadata/service/system/cis/cis-5-4-1-3.yml
@@ -0,0 +1,52 @@
+# CIS 5.4.1.3 Ensure password expiration warning days is 7 or more (Scored)
+#
+# Description
+# ===========
+# The PASS_WARN_AGE parameter in /etc/login.defs allows an administrator to
+# notify users that their password will expire in a defined number of days.
+# It is recommended that the PASS_WARN_AGE parameter be set to 7 or more days.
+#
+# Rationale
+# =========
+# Providing an advance warning that a password will be expiring gives users
+# time to think of a secure password. Users caught unaware may choose a simple
+# password or write it down where it may be discovered.
+#
+# Audit
+# =====
+# Run the following command and verify PASS_WARN_AGE is 7 or more:
+#
+#   # grep PASS_WARN_AGE /etc/login.defs
+#   PASS_WARN_AGE 7
+#
+# Verify all users with a password have their number of days of warning before
+# password expires set to 7 or more:
+#
+#   # egrep ^[^:]+:[^\!*] /etc/shadow | cut -d: -f1
+#   <list of users>
+#   # chage --list <user>
+#   Number of days of warning before password expires: 7
+#
+# Remediation
+# ===========
+#
+# Set the PASS_WARN_AGE parameter to 7 in /etc/login.defs :
+#
+#   PASS_WARN_AGE 7
+#
+# Modify user parameters for all users with a password set to match:
+#
+#   # chage --warndays 7 <user>
+#
+# Notes
+# =====
+# You can also check this setting in /etc/shadow directly. The 6th field
+# should be 7 or more for all users with a password.
+#
+parameters:
+  linux:
+    system:
+      login_defs:
+        PASS_WARN_AGE:
+          value: 7
+
diff --git a/metadata/service/system/cis/cis-5-4-4.yml b/metadata/service/system/cis/cis-5-4-4.yml
new file mode 100644
index 0000000..639babc
--- /dev/null
+++ b/metadata/service/system/cis/cis-5-4-4.yml
@@ -0,0 +1,57 @@
+# CIS 5.4.4 Ensure default user umask is 027 or more restrictive (Scored)
+#
+# Description
+# ===========
+# The default umask determines the permissions of files created by users.
+# The user creating the file has the discretion of making their files and
+# directories readable by others via the chmod command. Users who wish to
+# allow their files and directories to be readable by others by default may
+# choose a different default umask by inserting the umask command into the
+# standard shell configuration files ( .profile , .bashrc , etc.) in their
+# home directories.
+#
+# Rationale
+# =========
+# Setting a very secure default value for umask ensures that users make a
+# conscious choice about their file permissions. A default umask setting of
+# 077 causes files and directories created by users to not be readable by
+# any other user on the system. A umask of 027 would make files and
+# directories readable by users in the same Unix group, while a umask of 022
+# would make files readable by every user on the system.
+#
+# Audit
+# =====
+# Run the following commands and verify all umask lines returned are 027 or
+# more restrictive.
+#
+#   # grep "^umask" /etc/bash.bashrc
+#   umask 027
+#   # grep "^umask" /etc/profile
+#   umask 027
+#
+# Remediation
+# ===========
+# Edit the /etc/bash.bashrc and /etc/profile files (and the appropriate files
+# for any other shell supported on your system) and add or edit any umask
+# parameters as follows:
+#
+#   umask 027
+#
+# Notes
+# =====
+# The audit and remediation in this recommendation apply to bash and shell.
+# If other shells are supported on the system, it is recommended that their
+# configuration files also are checked.
+#
+# Other methods of setting a default user umask exist however the shell
+# configuration files are the last run and will override other settings if
+# they exist therefore our recommendation is to configure in the shell
+# configuration files. If other methods are in use in your environment they
+# should be audited and the shell configs should be verified to not override.
+#
+parameters:
+  linux:
+    system:
+      shell:
+        umask: "027"
+
diff --git a/metadata/service/system/cis/cis-6-1-2.yml b/metadata/service/system/cis/cis-6-1-2.yml
new file mode 100644
index 0000000..481c2df
--- /dev/null
+++ b/metadata/service/system/cis/cis-6-1-2.yml
@@ -0,0 +1,38 @@
+# CIS 6.1.2 Ensure permissions on /etc/passwd are configured
+#
+# Description
+# ===========
+# The /etc/passwd file contains user account information that is used by
+# many system utilities and therefore must be readable for these utilities
+# to operate.
+#
+# Rationale
+# =========
+# It is critical to ensure that the /etc/passwd file is protected from
+# unauthorized write access. Although it is protected by default, the file
+# permissions could be changed either inadvertently or through malicious actions.
+#
+# Audit
+# =====
+# Run the following command and verify Uid and Gid are both 0/root and
+# Access is 644 :
+#
+#   # stat /etc/passwd
+#   Access: (0644/-rw-r--r--) Uid: (0/root) Gid: (0/root)
+#
+# Remediation
+# ===========
+# Run the following command to set permissions on /etc/passwd :
+#
+#   # chown root:root /etc/passwd
+#   # chmod 644 /etc/passwd
+#
+parameters:
+  linux:
+    system:
+      file:
+        /etc/passwd:
+          user: 'root'
+          group: 'root'
+          mode: '0644'
+
diff --git a/metadata/service/system/cis/cis-6-1-3.yml b/metadata/service/system/cis/cis-6-1-3.yml
new file mode 100644
index 0000000..7bcd373
--- /dev/null
+++ b/metadata/service/system/cis/cis-6-1-3.yml
@@ -0,0 +1,39 @@
+# CIS 6.1.3 Ensure permissions on /etc/shadow are configured
+#
+# Description
+# ===========
+# The /etc/shadow file is used to store the information about user accounts
+# that is critical to the security of those accounts, such as the hashed
+# password and other security information.
+#
+# Rationale
+# =========
+# If attackers can gain read access to the /etc/shadow file, they can easily
+# run a password cracking program against the hashed password to break it.
+# Other security information that is stored in the /etc/shadow file (such
+# as expiration) could also be useful to subvert the user accounts.
+#
+# Audit
+# =====
+# Run the following command and verify Uid is 0/root , Gid is <gid>/shadow ,
+# and Access is 640 or more restrictive:
+#
+#   # stat /etc/shadow
+#   Access: (0640/-rw-r-----) Uid: (0/root) Gid: (42/shadow)
+#
+# Remediation
+# ===========
+# Run the one following commands to set permissions on /etc/shadow :
+#
+#   # chown root:shadow /etc/shadow
+#   # chmod o-rwx,g-wx /etc/shadow
+#
+parameters:
+  linux:
+    system:
+      file:
+        /etc/shadow:
+          user: 'root'
+          group: 'shadow'
+          mode: '0640'
+
diff --git a/metadata/service/system/cis/cis-6-1-4.yml b/metadata/service/system/cis/cis-6-1-4.yml
new file mode 100644
index 0000000..d5b2ffd
--- /dev/null
+++ b/metadata/service/system/cis/cis-6-1-4.yml
@@ -0,0 +1,38 @@
+# CIS 6.1.4 Ensure permissions on /etc/group are configured
+#
+# Description
+# ===========
+# The /etc/group file contains a list of all the valid groups defined in the
+# system. The command below allows read/write access for root and read access
+# for everyone else.
+#
+# Rationale
+# =========
+# The /etc/group file needs to be protected from unauthorized changes by
+# non-privileged users, but needs to be readable as this information is used
+# with many non-privileged programs.
+#
+# Audit
+# =====
+# Run the following command and verify Uid and Gid are both 0/root and
+# Access is 644 :
+#
+#   # stat /etc/group
+#   Access: (0644/-rw-r--r--) Uid: (0/root) Gid: (0/root)
+#
+# Remediation
+# ===========
+# Run the following command to set permissions on /etc/group :
+#
+#   # chown root:root /etc/group
+#   # chmod 644 /etc/group
+#
+parameters:
+  linux:
+    system:
+      file:
+        /etc/group:
+          user: 'root'
+          group: 'root'
+          mode: '0644'
+
diff --git a/metadata/service/system/cis/cis-6-1-5.yml b/metadata/service/system/cis/cis-6-1-5.yml
new file mode 100644
index 0000000..87ef05a
--- /dev/null
+++ b/metadata/service/system/cis/cis-6-1-5.yml
@@ -0,0 +1,39 @@
+# CIS 6.1.5 Ensure permissions on /etc/gshadow are configured
+#
+# Description
+# ===========
+# The /etc/gshadow file is used to store the information about groups that
+# is critical to the security of those accounts, such as the hashed password
+# and other security information.
+#
+# Rationale
+# =========
+# If attackers can gain read access to the /etc/gshadow file, they can easily
+# run a password cracking program against the hashed password to break it.
+# Other security information that is stored in the /etc/gshadow file (such as
+# group administrators) could also be useful to subvert the group.
+#
+# Audit
+# =====
+# Run the following command and verify verify Uid is 0/root ,
+# Gid is <gid>/shadow , and Access is 640 or more restrictive:
+#
+#   # stat /etc/gshadow
+#   Access: (0640/-rw-r-----) Uid: (0/root) Gid: (42/shadow)
+#
+# Remediation
+# ===========
+# Run the following commands to set permissions on /etc/gshadow :
+#
+#   # chown root:shadow /etc/gshadow
+#   # chmod o-rwx,g-rw /etc/gshadow
+#
+parameters:
+  linux:
+    system:
+      file:
+        /etc/gshadow:
+          user: 'root'
+          group: 'shadow'
+          mode: '0640'
+
diff --git a/metadata/service/system/cis/cis-6-1-6.yml b/metadata/service/system/cis/cis-6-1-6.yml
new file mode 100644
index 0000000..0cd4b9f
--- /dev/null
+++ b/metadata/service/system/cis/cis-6-1-6.yml
@@ -0,0 +1,36 @@
+# CIS 6.1.6 Ensure permissions on /etc/passwd- are configured
+#
+# Description
+# ===========
+# The /etc/passwd- file contains backup user account information.
+#
+# Rationale
+# =========
+# It is critical to ensure that the /etc/passwd- file is protected from
+# unauthorized access. Although it is protected by default, the file
+# permissions could be changed either inadvertently or through malicious actions.
+#
+# Audit
+# =====
+# Run the following command and verify Uid and Gid are both 0/root and
+# Access is 600 or more restrictive:
+#
+#   # stat /etc/passwd-
+#   Access: (0600/-rw-------) Uid: (0/root) Gid: (0/root)
+#
+# Remediation
+# ===========
+# Run the following command to set permissions on /etc/passwd- :
+#
+#   # chown root:root /etc/passwd-
+#   # chmod 600 /etc/passwd-
+#
+parameters:
+  linux:
+    system:
+      file:
+        /etc/passwd-:
+          user: 'root'
+          group: 'root'
+          mode: '0600'
+
diff --git a/metadata/service/system/cis/cis-6-1-7.yml b/metadata/service/system/cis/cis-6-1-7.yml
new file mode 100644
index 0000000..4918e6b
--- /dev/null
+++ b/metadata/service/system/cis/cis-6-1-7.yml
@@ -0,0 +1,38 @@
+# CIS 6.1.7 Ensure permissions on /etc/shadow- are configured
+#
+# Description
+# ===========
+# The /etc/shadow- file is used to store backup information about user
+# accounts that is critical to the security of those accounts, such as the
+# hashed password and other security information.
+#
+# Rationale
+# =========
+# It is critical to ensure that the /etc/shadow- file is protected from
+# unauthorized access. Although it is protected by default, the file
+# permissions could be changed either inadvertently or through malicious actions.
+#
+# Audit
+# =====
+# Run the following command and verify Uid and Gid are both 0/root and
+# Access is 600 or more restrictive:
+#
+#   # stat /etc/shadow-
+#   Access: (0600/-rw-------) Uid: (0/root) Gid: (0/root)
+#
+# Remediation
+# ===========
+# Run the following command to set permissions on /etc/shadow- :
+#
+#   # chown root:root /etc/shadow-
+#   # chmod 600 /etc/shadow-
+#
+parameters:
+  linux:
+    system:
+      file:
+        /etc/shadow-:
+          user: 'root'
+          group: 'root'
+          mode: '0600'
+
diff --git a/metadata/service/system/cis/cis-6-1-8.yml b/metadata/service/system/cis/cis-6-1-8.yml
new file mode 100644
index 0000000..eb7bb16
--- /dev/null
+++ b/metadata/service/system/cis/cis-6-1-8.yml
@@ -0,0 +1,37 @@
+# CIS 6.1.8 Ensure permissions on /etc/group- are configured
+#
+# Description
+# ===========
+# The /etc/group- file contains a backup list of all the valid groups defined
+# in the system.
+#
+# Rationale
+# =========
+# It is critical to ensure that the /etc/group- file is protected from
+# unauthorized access. Although it is protected by default, the file
+# permissions could be changed either inadvertently or through malicious actions.
+#
+# Audit
+# =====
+# Run the following command and verify Uid and Gid are both 0/root and
+# Access is 600 or more restrictive:
+#
+#   # stat /etc/group-
+#   Access: (0600/-rw-------) Uid: (0/root) Gid: (0/root)
+#
+# Remediation
+# ===========
+# Run the following command to set permissions on /etc/group- :
+#
+#   # chown root:root /etc/group-
+#   # chmod 600 /etc/group-
+#
+parameters:
+  linux:
+    system:
+      file:
+        /etc/group-:
+          user: 'root'
+          group: 'root'
+          mode: '0600'
+
diff --git a/metadata/service/system/cis/cis-6-1-9.yml b/metadata/service/system/cis/cis-6-1-9.yml
new file mode 100644
index 0000000..7acba2f
--- /dev/null
+++ b/metadata/service/system/cis/cis-6-1-9.yml
@@ -0,0 +1,38 @@
+# CIS 6.1.9 Ensure permissions on /etc/gshadow- are configured
+#
+# Description
+# ===========
+# The /etc/gshadow- file is used to store backup information about groups
+# that is critical to the security of those accounts, such as the hashed
+# password and other security information.
+#
+# Rationale
+# =========
+# It is critical to ensure that the /etc/gshadow- file is protected from
+# unauthorized access. Although it is protected by default, the file
+# permissions could be changed either inadvertently or through malicious actions.
+#
+# Audit
+# =====
+# Run the following command and verify Uid and Gid are both 0/root and
+# Access is 600 or more restrictive:
+#
+#   # stat /etc/gshadow-
+#   Access: (0600/-rw-------) Uid: (0/root) Gid: (0/root)
+#
+# Remediation
+# ===========
+# Run the following command to set permissions on /etc/gshadow- :
+#
+#   # chown root:root /etc/gshadow-
+#   # chmod 600 /etc/gshadow-
+#
+parameters:
+  linux:
+    system:
+      file:
+        /etc/gshadow-:
+          user: 'root'
+          group: 'root'
+          mode: '0600'
+
diff --git a/metadata/service/system/cis/init.yml b/metadata/service/system/cis/init.yml
index a6664f1..0c2626d 100644
--- a/metadata/service/system/cis/init.yml
+++ b/metadata/service/system/cis/init.yml
@@ -1,6 +1,21 @@
 classes:
+- service.linux.system.cis.cis-1-1-1-1
+- service.linux.system.cis.cis-1-1-1-2
+- service.linux.system.cis.cis-1-1-1-3
+- service.linux.system.cis.cis-1-1-1-4
+- service.linux.system.cis.cis-1-1-1-5
+- service.linux.system.cis.cis-1-1-1-6
+- service.linux.system.cis.cis-1-1-1-7
+- service.linux.system.cis.cis-1-1-1-8
+- service.linux.system.cis.cis-1-1-14_15_16
+- service.linux.system.cis.cis-1-1-21
 - service.linux.system.cis.cis-1-5-1
 - service.linux.system.cis.cis-1-5-3
+- service.linux.system.cis.cis-1-5-4
+- service.linux.system.cis.cis-2-3-1
+- service.linux.system.cis.cis-2-3-2
+- service.linux.system.cis.cis-2-3-3
+- service.linux.system.cis.cis-2-3-4
 - service.linux.system.cis.cis-3-1-2
 - service.linux.system.cis.cis-3-2-1
 - service.linux.system.cis.cis-3-2-2
@@ -12,3 +27,19 @@
 - service.linux.system.cis.cis-3-2-8
 # Temp. disable PROD-22520
 #- service.linux.system.cis.cis-3-3-3
+- service.linux.system.cis.cis-3-5-1
+- service.linux.system.cis.cis-3-5-2
+- service.linux.system.cis.cis-3-5-3
+- service.linux.system.cis.cis-3-5-4
+- service.linux.system.cis.cis-5-4-1-1
+- service.linux.system.cis.cis-5-4-1-2
+- service.linux.system.cis.cis-5-4-1-3
+- service.linux.system.cis.cis-5-4-4
+- service.linux.system.cis.cis-6-1-2
+- service.linux.system.cis.cis-6-1-3
+- service.linux.system.cis.cis-6-1-4
+- service.linux.system.cis.cis-6-1-5
+- service.linux.system.cis.cis-6-1-6
+- service.linux.system.cis.cis-6-1-7
+- service.linux.system.cis.cis-6-1-8
+- service.linux.system.cis.cis-6-1-9
diff --git a/tests/pillar/system.sls b/tests/pillar/system.sls
index 555a0cd..43fc65c 100644
--- a/tests/pillar/system.sls
+++ b/tests/pillar/system.sls
@@ -5,6 +5,20 @@
     fqdn: linux.ci.local
   system:
     enabled: true
+    at:
+      enabled: true
+      user:
+        root:
+          enabled: true
+        testuser:
+          enabled: true
+    cron:
+      enabled: true
+      user:
+        root:
+          enabled: true
+        testuser:
+          enabled: true
     cluster: default
     name: linux
     domain: ci.local
@@ -85,6 +99,7 @@
             subjects:
             - '@group1'
     sysfs:
+      enable_apply: true
       scheduler:
         block/sda/queue/scheduler: deadline
       power:
@@ -113,6 +128,7 @@
         enabled: true
         home: /root
         name: root
+        maxdays: 365
       testuser:
         enabled: true
         name: testuser
@@ -187,6 +203,22 @@
         proxy:
           enabled: true
           https: https://127.0.5.1:443
+      saltstack:
+        source: "deb [arch=amd64] http://repo.saltstack.com/apt/ubuntu/16.04/amd64/2017.7/ xenial main"
+        key_url: "http://repo.saltstack.com/apt/ubuntu/16.04/amd64/2017.7/SALTSTACK-GPG-KEY.pub"
+        architectures: amd64
+        clean_file: true
+        pinning:
+          10:
+            enabled: true
+            pin: 'release o=SaltStack'
+            priority: 50
+            package: 'libsodium18'
+          20:
+            enabled: true
+            pin: 'release o=SaltStack'
+            priority: 1100
+            package: '*'
       opencontrail:
         source: "deb http://ppa.launchpad.net/tcpcloud/contrail-3.0/ubuntu xenial main"
         keyid: E79EE90C
@@ -393,6 +425,12 @@
         - .local
       LANG: C
       LC_ALL: C
+    login_defs:
+      PASS_MAX_DAYS:
+        value: 99
+    shell:
+      umask: '027'
+      timeout: 900
     profile:
       vi_flavors.sh: |
         export PAGER=view