├── VERSION ├── debian ├── compat ├── source │ └── format ├── docs ├── rules ├── control ├── copyright └── changelog ├── linux ├── files │ ├── etc_profile │ ├── hostname │ ├── etc_profile_vi_flavors.sh │ ├── multipath │ │ ├── _hitachi_vsp1000.conf │ │ ├── _ibm_storwize.conf │ │ └── _fujitsu_eternus_dxl.conf │ ├── 60-net-txqueue.rules │ ├── sources.list │ ├── logstash-forwarder.conf │ ├── collectd_disk.conf │ ├── collectd_swap.conf │ ├── governor.conf.jinja │ ├── cron_users.jinja │ ├── collectd_bond_status.conf │ ├── journal.conf │ ├── login_duo.conf │ ├── atop.conf │ ├── systemd.conf │ ├── sudoer │ ├── policy-rc.d │ ├── limits.conf │ ├── motd.sh │ ├── dpdk_interfaces │ ├── mkhomedir │ ├── setup-loopback-device.upstart │ ├── systemd-network.conf │ ├── 95proxies │ ├── collectd_netlink.conf │ ├── wireless │ ├── grub_hugepages │ ├── telegraf.conf │ ├── apt.conf │ ├── collectd_df.conf │ ├── sudoer-groups │ ├── sudoer-users │ ├── apt.conf.d_proxies │ ├── atop.service │ ├── openvswitch-switch.systemd │ ├── bash_history.sh │ ├── logstash-filter.conf │ ├── cgconfig.conf │ ├── ovs_bridge │ ├── cgrules.conf │ ├── tty.upstart │ ├── ovs_port │ ├── prompt.sh │ ├── setup-loopback-device.systemd │ ├── preferences_repo │ ├── sysfs.conf │ ├── proxy.sh │ ├── sudoer-aliases │ ├── resolv.conf │ ├── nsswitch.conf │ ├── multipath.conf │ ├── etc_environment │ ├── netconsole.conf │ ├── pam-add-profile │ ├── 90autoupdates │ ├── hosts │ ├── login.defs.jinja │ ├── nslcd.conf │ ├── openvswitch-switch.default │ ├── modprobe.conf.jinja │ └── pam-sshd ├── system │ ├── dpdk.sls │ ├── doc.sls │ ├── banner.sls │ ├── rc.sls │ ├── bash.sls │ ├── timezone.sls │ ├── login_defs.sls │ ├── iommu.sls │ ├── haveged.sls │ ├── directory.sls │ ├── service.sls │ ├── sriov.sls │ ├── policyrcd.sls │ ├── ld.sls │ ├── locale.sls │ ├── apparmor.sls │ ├── limit.sls │ ├── autoupdates.sls │ ├── apt.sls │ ├── prompt.sls │ ├── mcelog.sls │ ├── journal.sls │ ├── console.sls │ ├── profile.sls │ ├── auth │ │ └── duo.sls │ ├── selinux.sls │ ├── grub.sls │ ├── certificate.sls │ ├── systemd.sls │ ├── env.sls │ ├── group.sls │ ├── motd.sls │ ├── cpu.sls │ ├── netconsole.sls │ ├── hugepages.sls │ ├── shell.sls │ ├── at.sls │ ├── sysfs.sls │ ├── config.sls │ ├── atop.sls │ ├── job.sls │ ├── package.sls │ ├── sudo.sls │ ├── file.sls │ └── cron.sls ├── init.sls ├── network │ ├── resolv.sls │ ├── dhclient.sls │ ├── proxy.sls │ ├── init.sls │ ├── hostname.sls │ ├── openvswitch.sls │ ├── systemd.sls │ └── host.sls ├── meta │ ├── meta.yml │ ├── logrotate.yml │ ├── grafana.yml │ ├── sphinx.yml │ ├── telegraf.yml │ ├── salt.yml │ ├── sensu.yml │ ├── collectd.yml │ └── graphite.yml └── storage │ ├── multipath.sls │ ├── init.sls │ ├── loopback.sls │ ├── mount.sls │ ├── lvm.sls │ └── swap.sls ├── tests ├── test-requirements.txt ├── example │ └── file_template.jinja ├── pillar │ ├── system_banner.sls │ ├── system_file.sls │ ├── network_extended.sls │ ├── system_extra.sls │ ├── network_openvswitch.sls │ ├── storage.sls │ └── network_openvswitch_dpdk.sls └── integration │ └── system │ ├── sudoer_spec.rb │ ├── env_spec.rb │ ├── netconsole_spec.rb │ ├── profile_spec.rb │ └── repo_spec.rb ├── .gitignore ├── metadata.yml ├── metadata └── service │ ├── system │ ├── container.yml │ ├── init.yml │ └── cis │ │ ├── cis-3-3-3.yml │ │ ├── cis-1-1-1-4.yml │ │ ├── cis-3-5-4.yml │ │ ├── cis-1-1-1-3.yml │ │ ├── cis-1-1-1-5.yml │ │ ├── cis-1-1-1-2.yml │ │ ├── cis-3-5-3.yml │ │ ├── cis-1-1-1-1.yml │ │ ├── cis-6-1-6.yml │ │ ├── cis-1-5-4.yml │ │ ├── cis-6-1-8.yml │ │ ├── cis-1-5-3.yml │ │ ├── cis-3-5-1.yml │ │ ├── cis-6-1-4.yml │ │ ├── cis-6-1-2.yml │ │ ├── cis-1-1-1-7.yml │ │ ├── cis-2-3-3.yml │ │ ├── cis-6-1-7.yml │ │ ├── cis-6-1-9.yml │ │ ├── cis-3-2-6.yml │ │ ├── cis-3-5-2.yml │ │ ├── cis-2-3-4.yml │ │ ├── cis-6-1-3.yml │ │ ├── cis-6-1-5.yml │ │ ├── cis-3-2-4.yml │ │ ├── cis-1-1-1-6.yml │ │ ├── cis-3-1-2.yml │ │ ├── cis-2-3-1.yml │ │ ├── cis-3-2-3.yml │ │ ├── cis-1-1-1-8.yml │ │ ├── cis-3-2-5.yml │ │ ├── cis-5-4-1-3.yml │ │ ├── cis-5-4-1-2.yml │ │ ├── cis-5-4-1-1.yml │ │ ├── cis-3-2-2.yml │ │ ├── cis-1-1-21.yml │ │ ├── cis-1-5-1.yml │ │ ├── init.yml │ │ ├── cis-3-2-7.yml │ │ ├── cis-3-2-8.yml │ │ ├── cis-2-3-2.yml │ │ ├── cis-5-4-4.yml │ │ └── cis-3-2-1.yml │ └── support.yml ├── _modules ├── linux_netlink.py ├── linux_hosts.py └── ovs_config.py ├── LICENSE ├── .kitchen.yml ├── .travis.yml └── _states └── ovs_config.py /VERSION: -------------------------------------------------------------------------------- 1 | 2017.4.1 2 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /linux/files/etc_profile: -------------------------------------------------------------------------------- 1 | {{ script }} 2 | -------------------------------------------------------------------------------- /tests/test-requirements.txt: -------------------------------------------------------------------------------- 1 | jsonschema 2 | reno 3 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.rst 2 | CHANGELOG.rst 3 | VERSION 4 | -------------------------------------------------------------------------------- /linux/files/hostname: -------------------------------------------------------------------------------- 1 | {{ pillar.linux.system.name }} 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | %: 4 | dh $@ 5 | 6 | -------------------------------------------------------------------------------- /tests/example/file_template.jinja: -------------------------------------------------------------------------------- 1 | foo{{ pillar["test"]["example"] }} 2 | -------------------------------------------------------------------------------- /linux/files/etc_profile_vi_flavors.sh: -------------------------------------------------------------------------------- 1 | set -o vi 2 | export EDITOR=vim 3 | {{ script }} 4 | -------------------------------------------------------------------------------- /linux/files/multipath/_hitachi_vsp1000.conf: -------------------------------------------------------------------------------- 1 | 2 | # vsp1000 does not have special device config -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .kitchen 2 | .bundle 3 | bundle/ 4 | tests/build/ 5 | *.swp 6 | *.pyc 7 | .ropeproject 8 | Gemfile* 9 | -------------------------------------------------------------------------------- /metadata.yml: -------------------------------------------------------------------------------- 1 | name: "linux" 2 | version: "2017.4.1" 3 | source: "https://github.com/salt-formulas/salt-formula-linux" 4 | -------------------------------------------------------------------------------- /linux/files/60-net-txqueue.rules: -------------------------------------------------------------------------------- 1 | KERNEL=="tap[0-9a-z\-]*", RUN+="/sbin/ip link set %k txqueuelen {{ tap_custom_txqueuelen }}" 2 | -------------------------------------------------------------------------------- /linux/files/sources.list: -------------------------------------------------------------------------------- 1 | {%- for name, repo in default_repos.items() | sort %} 2 | # Repository {{ name }} 3 | {{ repo.source }} 4 | {%- endfor %} 5 | -------------------------------------------------------------------------------- /linux/files/logstash-forwarder.conf: -------------------------------------------------------------------------------- 1 | { 2 | "paths": [ 3 | "/var/log/syslog", 4 | "/var/log/auth.log" 5 | ], 6 | "fields": { "type": "syslog" } 7 | } 8 | -------------------------------------------------------------------------------- /linux/files/collectd_disk.conf: -------------------------------------------------------------------------------- 1 | 2 | Globals false 3 | 4 | 5 | 6 | IgnoreSelected {{ plugin.get('ignore_selected', True)|lower }} 7 | 8 | -------------------------------------------------------------------------------- /linux/files/collectd_swap.conf: -------------------------------------------------------------------------------- 1 | 2 | Globals false 3 | 4 | 5 | 6 | ReportBytes {{ plugin.get('report_bytes', False)|lower }} 7 | 8 | 9 | -------------------------------------------------------------------------------- /linux/files/governor.conf.jinja: -------------------------------------------------------------------------------- 1 | {% for cpu_core in range(salt['grains.get']('num_cpus', 1)) %} 2 | devices/system/cpu/cpu{{ cpu_core }}/cpufreq/scaling_governor = {{ governor }} 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /linux/files/cron_users.jinja: -------------------------------------------------------------------------------- 1 | # This file is managed by Salt, do not edit 2 | {%- for user_name in users %} 3 | {{ user_name }} 4 | {%- endfor %} 5 | {# IMPORTANT: This file SHOULD ends with a newline #} -------------------------------------------------------------------------------- /linux/system/dpdk.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | 3 | {%- if network.dpdk.enabled and network.dpdk.driver == "vfio" %} 4 | include: 5 | - linux.system.iommu 6 | {%- endif %} -------------------------------------------------------------------------------- /linux/files/collectd_bond_status.conf: -------------------------------------------------------------------------------- 1 | Import "bond_status" 2 | 3 | 4 | {%- for interface in plugin.get('interfaces', []) %} 5 | Bond "{{ interface }}" 6 | {%- endfor %} 7 | 8 | -------------------------------------------------------------------------------- /linux/files/journal.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | #This file is managed by salt 3 | [Journal] 4 | {%- for option, value in settings.items() %} 5 | {{ option }}={{ value }} 6 | {%- endfor -%} 7 | -------------------------------------------------------------------------------- /linux/files/login_duo.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import auth with context %} 2 | [duo] 3 | ikey = {{ auth.duo.duo_ikey }} 4 | skey = {{ auth.duo.duo_skey }} 5 | host = {{ auth.duo.duo_host }} 6 | pushinfo = yes 7 | failmode = secure 8 | 9 | -------------------------------------------------------------------------------- /linux/files/atop.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | # This file /etc/default/atop is managed by Salt linux formula 3 | INTERVAL={{ system.atop.interval }} 4 | LOGPATH={{ system.atop.logpath }} 5 | OUTFILE={{ system.atop.outfile }} 6 | -------------------------------------------------------------------------------- /linux/files/systemd.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | {%- for section, options in settings.items() %} 3 | [{{ section }}] 4 | {%- for option, value in options.items() %} 5 | {{ option }}={{ value }} 6 | {%- endfor -%} 7 | {%- endfor -%} 8 | -------------------------------------------------------------------------------- /linux/files/sudoer: -------------------------------------------------------------------------------- 1 | # sudoer file managed by salt-minion 2 | # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN 3 | # 4 | # user {{ user_name }} is system administrator. 5 | # It has passwordless sudo functionality. 6 | {{ user_name }} ALL=(ALL) NOPASSWD:ALL 7 | -------------------------------------------------------------------------------- /linux/files/policy-rc.d: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | #!/bin/sh 3 | 4 | while true; do 5 | case $1 in 6 | {%- for policy in system.policyrcd %} 7 | {{ policy.package }}) {{ policy.action }};; 8 | {%- endfor %} 9 | esac 10 | done 11 | -------------------------------------------------------------------------------- /linux/files/limits.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %}{%- set limit = system.limit.get(limit_name) %}# Limits for {{ limit.domain }} 2 | {%- for entry in limit.limits %} 3 | {{ limit.domain }} {{ entry.type }} {{ entry.item }} {{ entry.value }} 4 | 5 | {%- endfor %} 6 | 7 | -------------------------------------------------------------------------------- /linux/system/doc.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | # This state is obsolete, grains are now managed from salt.minion.grains so we 5 | # will just include it 6 | 7 | include: 8 | - salt.minion.grains 9 | 10 | {%- endif %} 11 | -------------------------------------------------------------------------------- /metadata/service/system/container.yml: -------------------------------------------------------------------------------- 1 | applications: 2 | - linux 3 | classes: 4 | - service.linux.support 5 | parameters: 6 | linux: 7 | system: 8 | enabled: true 9 | cluster: default 10 | network: 11 | enabled: false 12 | storage: 13 | enabled: false 14 | -------------------------------------------------------------------------------- /linux/files/motd.sh: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | {%- for motd in system.motd -%} 3 | {%- if loop.index == index -%} 4 | {%- for name, value in motd.items() -%} 5 | {%- if name == motd_name -%}{{ value }}{%- endif %} 6 | {%- endfor -%} 7 | {%- endif -%} 8 | {%- endfor -%} 9 | -------------------------------------------------------------------------------- /linux/system/banner.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import banner with context %} 2 | 3 | {%- if banner.get('enabled', False) %} 4 | /etc/issue: 5 | file.managed: 6 | - user: root 7 | - group: root 8 | - mode: 644 9 | - contents_pillar: linux:system:banner:contents 10 | {%- endif %} 11 | -------------------------------------------------------------------------------- /linux/files/dpdk_interfaces: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | {%- for interface_name, interface in network.interface.items() %} 3 | {%- if 'dpdk' in interface.type and interface.pci is defined %} 4 | pci {{ interface.pci }} {{ interface.driver }} 5 | {%- endif %} 6 | {%- endfor %} 7 | -------------------------------------------------------------------------------- /linux/files/mkhomedir: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import auth with context %} 2 | Name: Create home directory during login 3 | Default: yes 4 | Priority: 0 5 | Session-Type: Additional 6 | Session-Final: 7 | required pam_mkhomedir.so skel=/etc/skel umask={{ auth.mkhomedir.get('umask', '0022') }} silent 8 | -------------------------------------------------------------------------------- /linux/files/setup-loopback-device.upstart: -------------------------------------------------------------------------------- 1 | description "Setup {{ device_name }} device" 2 | 3 | start on filesystem 4 | 5 | pre-start exec losetup {{ device_name }} {{ file }} 6 | post-stop exec losetup -d {{ device_name }} 7 | 8 | script 9 | while losetup {{ device_name }} ; do sleep 60 ; done 10 | end script 11 | -------------------------------------------------------------------------------- /linux/files/systemd-network.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | {%- for section, options in settings.items() %} 3 | 4 | [{{ section[0].upper() + section[1:] }}] 5 | {%- for option, value in options.items() %} 6 | {{ option[0].upper() + option[1:] }}={{ value }} 7 | {%- endfor %} 8 | {%- endfor %} 9 | -------------------------------------------------------------------------------- /linux/init.sls: -------------------------------------------------------------------------------- 1 | {%- if pillar.linux is defined %} 2 | include: 3 | {%- if pillar.linux.system is defined %} 4 | - linux.system 5 | {%- endif %} 6 | {%- if pillar.linux.network is defined %} 7 | - linux.network 8 | {%- endif %} 9 | {%- if pillar.linux.storage is defined %} 10 | - linux.storage 11 | {%- endif %} 12 | {%- endif %} -------------------------------------------------------------------------------- /linux/files/95proxies: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | Acquire::http::proxy "http://{{ network.proxy.host }}:{{ network.proxy.port }}/"; 3 | Acquire::ftp::proxy "ftp://{{ network.proxy.host }}:{{ network.proxy.port }}/"; 4 | Acquire::https::proxy "http://{{ network.proxy.host }}:{{ network.proxy.port }}/"; 5 | -------------------------------------------------------------------------------- /linux/files/collectd_netlink.conf: -------------------------------------------------------------------------------- 1 | 2 | Globals false 3 | 4 | 5 | 6 | {%- for interface_name in plugin.get('interfaces', []) %} 7 | VerboseInterface "{{ interface_name }}" 8 | {%- endfor %} 9 | IgnoreSelected {{ plugin.get('ignore_selected', False)|lower }} 10 | 11 | -------------------------------------------------------------------------------- /linux/system/rc.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.rc.local is defined %} 5 | 6 | /etc/rc.local: 7 | file.managed: 8 | - user: root 9 | - group: root 10 | - mode: 755 11 | - contents_pillar: linux:system:rc:local 12 | 13 | {%- endif %} 14 | 15 | {%- endif %} -------------------------------------------------------------------------------- /linux/system/bash.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.bash.get('preserve_history', False) %} 5 | /etc/profile.d/bash_history.sh: 6 | file.managed: 7 | - source: salt://linux/files/bash_history.sh 8 | - template: jinja 9 | {%- endif %} 10 | 11 | {%- endif %} 12 | -------------------------------------------------------------------------------- /linux/files/wireless: -------------------------------------------------------------------------------- 1 | {%- set interface = salt['pillar.get']('linux:network:interface:'+interface_name) %} 2 | Interface={{ interface_name }} 3 | Connection=wireless 4 | Security={{ interface.wireless.security }} 5 | ESSID={{ interface.wireless.essid }} 6 | IP=dhcp 7 | Key={{ interface.wireless.key }} 8 | Priority={{ interface.wireless.get('priority', '1') }} 9 | -------------------------------------------------------------------------------- /linux/files/grub_hugepages: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT {%- for hugepages_type, hugepages in system.kernel.hugepages.items() %}{%- if hugepages.get('default', False) %} default_hugepagesz={{ hugepages.size }} {%- endif %} hugepagesz={{ hugepages.size }} hugepages={{ hugepages.count }} {%- endfor %}" 3 | -------------------------------------------------------------------------------- /linux/system/timezone.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.timezone is defined %} 5 | 6 | {{ system.timezone }}: 7 | timezone.system: 8 | {%- if grains.get('noservices') %} 9 | - onlyif: /bin/false 10 | {%- endif %} 11 | - utc: {{ system.utc }} 12 | 13 | {%- endif %} 14 | 15 | {%- endif %} 16 | -------------------------------------------------------------------------------- /linux/files/telegraf.conf: -------------------------------------------------------------------------------- 1 | [[inputs.bond]] 2 | {%- include 'telegraf/files/input/_common.conf' %} 3 | {%- if values.bond_interfaces is defined %} 4 | bond_interfaces = {{ values.bond_interfaces | json }} 5 | {%- endif %} 6 | {%- if values.host_proc is defined %} 7 | host_proc = "{{ values.host_proc | json }}" 8 | {%- endif %} 9 | {%- include 'telegraf/files/input/_filters.conf' %} 10 | -------------------------------------------------------------------------------- /linux/files/apt.conf: -------------------------------------------------------------------------------- 1 | // apt.conf file managed by salt-minion 2 | // DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN 3 | 4 | {%- for key, value in config.items() %} 5 | {{ key }} {% if value is iterable and value is not string %}{ {% for entry in value %}"{{ entry }}";{% endfor %} } {% else %}{{ value }};{% endif %} 6 | {%- endfor %} 7 | {#- 8 | vim: syntax=jinja 9 | -#} 10 | -------------------------------------------------------------------------------- /linux/files/collectd_df.conf: -------------------------------------------------------------------------------- 1 | 2 | Globals false 3 | 4 | 5 | 6 | {%- for fs_type in plugin.get('fs_types', []) %} 7 | FSType {{ fs_type }} 8 | {%- endfor %} 9 | IgnoreSelected {{ plugin.get('ignore_selected', False)|lower }} 10 | ReportByDevice false 11 | ReportInodes true 12 | ValuesAbsolute true 13 | ValuesPercentage true 14 | 15 | -------------------------------------------------------------------------------- /linux/system/login_defs.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | {%- if system.login_defs is defined %} 4 | login_defs: 5 | file.managed: 6 | - name: /etc/login.defs 7 | - source: salt://linux/files/login.defs.jinja 8 | - template: jinja 9 | - user: root 10 | - group: root 11 | - mode: 644 12 | {%- endif %} 13 | {%- endif %} 14 | -------------------------------------------------------------------------------- /linux/system/iommu.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - linux.system.grub 3 | 4 | /etc/default/grub.d/90-iommu.cfg: 5 | file.managed: 6 | - contents: 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT intel_iommu=on iommu=pt"' 7 | - require: 8 | - file: grub_d_directory 9 | {%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %} 10 | - watch_in: 11 | - cmd: grub_update 12 | {%- endif %} 13 | -------------------------------------------------------------------------------- /linux/files/sudoer-groups: -------------------------------------------------------------------------------- 1 | # sudoer groups, file managed by salt-minion 2 | # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN 3 | 4 | {%- for group,spec in groups.items() %} 5 | %{{ spec.name|default(group) }} {{ spec.get('hosts', ['ALL'])|join(',') }}=({{ spec.get('runas', ['ALL'])|join(', ') }}) {% if spec.get('nopasswd', True) %}NOPASSWD: {% endif %}{{ spec.get('commands', ['ALL'])|join(', ') }} 6 | {%- endfor %} 7 | 8 | -------------------------------------------------------------------------------- /linux/system/haveged.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | {%- if system.haveged.enabled %} 4 | 5 | linux_haveged_pkgs: 6 | pkg.installed: 7 | - name: haveged 8 | - watch_in: 9 | - service: linux_haveged_service 10 | 11 | linux_haveged_service: 12 | service.running: 13 | - name: haveged 14 | - enable: true 15 | - require: 16 | - pkg: linux_haveged_pkgs 17 | 18 | {%- endif %} 19 | -------------------------------------------------------------------------------- /metadata/service/support.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | linux: 3 | _support: 4 | prometheus: 5 | enabled: true 6 | telegraf: 7 | enabled: true 8 | collectd: 9 | enabled: true 10 | heka: 11 | enabled: true 12 | sensu: 13 | enabled: false 14 | sphinx: 15 | enabled: true 16 | grafana: 17 | enabled: true 18 | fluentd: 19 | enabled: true 20 | -------------------------------------------------------------------------------- /tests/pillar/system_banner.sls: -------------------------------------------------------------------------------- 1 | linux: 2 | network: 3 | enabled: true 4 | hostname: linux 5 | fqdn: linux.ci.local 6 | system: 7 | enabled: true 8 | name: linux 9 | banner: 10 | enabled: true 11 | contents: | 12 | ================= WARNING ================= 13 | This is tcpcloud network. 14 | Unauthorized access is strictly prohibited. 15 | =========================================== 16 | -------------------------------------------------------------------------------- /linux/network/resolv.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | {%- if network.enabled %} 3 | 4 | /etc/resolv.conf: 5 | file.managed: 6 | - source: salt://linux/files/resolv.conf 7 | - mode: 644 8 | - template: jinja 9 | - follow_symlinks: false 10 | - require: 11 | - service: resolvconf_service 12 | 13 | resolvconf_service: 14 | service.dead: 15 | - name: resolvconf 16 | - enable: false 17 | 18 | {%- endif %} 19 | -------------------------------------------------------------------------------- /tests/integration/system/sudoer_spec.rb: -------------------------------------------------------------------------------- 1 | describe command('grep "" /etc/sudoers.d/*') do 2 | its('stdout') { should_not match /sudogroup0/ } 3 | its('stdout') { should match /salt-ops ALL=\(DBA\) NOPASSWD/ } 4 | its('stdout') { should match /sudogroup2.*localhost=/ } 5 | its('stdout') { should match /db-ops.*less/ } 6 | its('stdout') { should_not match /sudogroup0/ } 7 | its('stdout') { should_not match /sudogroup1 .* !SUDO_RESTRICTED_SU/ } 8 | end 9 | -------------------------------------------------------------------------------- /linux/files/sudoer-users: -------------------------------------------------------------------------------- 1 | # sudoer users, file managed by salt-minion 2 | # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN 3 | 4 | {%- for user,spec in users.items() %} 5 | {{ spec.name|default(user) }} {{ spec.get('hosts', ['ALL'])|join(',') }}=({{ spec.get('runas', ['ALL'])|join(', ') }}) {% if spec.get('nopasswd', True) %}NOPASSWD:{% endif %}{% if spec.get('setenv', False) %}SETENV:{% endif %} {{ spec.get('commands', ['ALL'])|join(', ') }} 6 | {%- endfor %} 7 | 8 | -------------------------------------------------------------------------------- /linux/system/directory.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | {%- if system.directory is defined %} 4 | {%- for name, dir in system.directory.items() %} 5 | 6 | {{ dir.name|default(name) }}: 7 | file.directory: 8 | {%- if dir %} 9 | {%- for key, value in dir.items() %} 10 | - {{ key }}: {{ value }} 11 | {%- endfor %} 12 | {%- else %} 13 | - name: {{ name }} 14 | {%- endif %} 15 | 16 | {%- endfor %} 17 | {% endif %} 18 | -------------------------------------------------------------------------------- /linux/files/apt.conf.d_proxies: -------------------------------------------------------------------------------- 1 | {%- if ftp and ftp.lower() != 'none' %} 2 | Acquire::ftp::proxy{%- if external_host %}::{{ external_host }}{% endif %} "{{ ftp }}"; 3 | {%- endif %} 4 | {%- if http and http.lower() != 'none' %} 5 | Acquire::http::proxy{%- if external_host %}::{{ external_host }}{% endif %} "{{ http }}"; 6 | {%- endif %} 7 | {%- if https and https.lower() != 'none' %} 8 | Acquire::https::proxy{%- if external_host %}::{{ external_host }}{% endif %} "{{ https }}"; 9 | {%- endif -%} 10 | -------------------------------------------------------------------------------- /linux/files/atop.service: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | [Unit] 3 | Description=atop - advanced interactive monitor 4 | After=syslog.target 5 | ConditionPathExists={{ config_file }} 6 | Documentation=man:atop(1) 7 | Documentation=https://atoptool.nl 8 | 9 | [Service] 10 | EnvironmentFile=-{{ config_file }} 11 | ExecStart=/usr/bin/atop -a -w ${LOGPATH}/daily.log ${INTERVAL} 12 | Restart=always 13 | RestartSec=10 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /linux/files/openvswitch-switch.systemd: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Open vSwitch 3 | Before=network.target 4 | After=network-pre.target ovsdb-server.service ovs-vswitchd.service 5 | PartOf=network.target 6 | Requires=ovsdb-server.service 7 | Requires=ovs-vswitchd.service 8 | 9 | [Service] 10 | Type=oneshot 11 | ExecStart=/usr/bin/ovs-vsctl set open . external-ids:hostname=%H 12 | ExecReload=/bin/true 13 | ExecStop=/bin/true 14 | RemainAfterExit=yes 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | 19 | -------------------------------------------------------------------------------- /_modules/linux_netlink.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | 5 | def __virtual__(): 6 | return 'linux_netlink' 7 | 8 | def ls(regex): 9 | """ 10 | Provide a list of network interfaces. 11 | """ 12 | _lo_re = re.compile(r'^lo$') 13 | _alphanum_re = re.compile(regex) 14 | 15 | def _filter(interface): 16 | return _alphanum_re.match(interface) and not _lo_re.match(interface) 17 | 18 | return filter(_filter, __salt__['grains.get']('ip_interfaces', {}).keys()) 19 | -------------------------------------------------------------------------------- /tests/integration/system/env_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | ## PROXIES 3 | # 4 | describe file('/etc/environment') do 5 | it('should exist') 6 | its('content') { should_not match /HTTPS_PROXY"/ } 7 | its('content') { should match /HTTP_PROXY="http:\/\/127.0.4.2:80"/ } 8 | its('content') { should match /BOB_PATH=/} 9 | its('content') { should match /LC_ALL="C"/ } 10 | its('content') { should match /ftp_proxy=.*127.0.4.3:2121/ } 11 | its('content') { should match /NO_PROXY=.*dummy.net,.local/ } 12 | end 13 | -------------------------------------------------------------------------------- /linux/system/service.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- for name, service in system.service.items() %} 5 | 6 | linux_service_{{ name }}: 7 | service.{{ service.status }}: 8 | {%- if service.status == 'dead' %} 9 | - enable: {{ service.get('enabled', False) }} 10 | {%- elif service.status == 'running' %} 11 | - enable: {{ service.get('enabled', True) }} 12 | {%- endif %} 13 | - name: {{ name }} 14 | 15 | {%- endfor %} 16 | {%- endif %} 17 | -------------------------------------------------------------------------------- /linux/files/bash_history.sh: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | # History across sessions for Bash 4 | if [ -n "$BASH_VERSION" ]; then 5 | # Avoid duplicates 6 | export HISTCONTROL=ignoredups:erasedups 7 | # When the shell exits, append to the history file instead of overwriting it 8 | shopt -s histappend 9 | 10 | # After each command, append to the history file and reread it 11 | export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND$'\n'}history -a; history -c; history -r" 12 | fi 13 | -------------------------------------------------------------------------------- /linux/system/sriov.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | include: 4 | - linux.system.iommu 5 | 6 | /etc/modprobe.d/sriov.conf: 7 | file.managed: 8 | - contents: | 9 | blacklist ixgbevf 10 | blacklist igbvf 11 | blacklist i40evf 12 | 13 | {%- if system.kernel.get('unsafe_interrupts', false) %} 14 | 15 | /etc/modprobe.d/iommu_unsafe_interrupts.conf: 16 | file.managed: 17 | - contents: options vfio_iommu_type1 allow_unsafe_interrupts=1 18 | 19 | {%- endif %} 20 | -------------------------------------------------------------------------------- /tests/integration/system/netconsole_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | ## NETCONSOLE 3 | # 4 | describe file('/etc/default/netconsole.conf') do 5 | it('should exist') 6 | its('content') { should match /^PORT="514"/} 7 | its('content') { should match /^netconsole "bond0" "192.168.0.1" "ff:ff:ff:ff:ff:ff"/} 8 | its('content') { should match /^dmesg -n "debug"/} 9 | end 10 | 11 | describe file('/etc/dhcp/dhclient-exit-hooks.d/netconsole') do 12 | it('should exist') 13 | its('content') { should match /netconsole_setup/} 14 | end 15 | -------------------------------------------------------------------------------- /linux/files/logstash-filter.conf: -------------------------------------------------------------------------------- 1 | filter { 2 | if [type] == "syslog" { 3 | grok { 4 | match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" } 5 | add_field => [ "received_at", "%{@timestamp}" ] 6 | add_field => [ "received_from", "%{host}" ] 7 | } 8 | syslog_pri { } 9 | date { 10 | match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /linux/network/dhclient.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | 3 | {%- if network.dhclient.enabled|default(False) %} 4 | 5 | dhclient_conf: 6 | file.managed: 7 | - name: {{ network.dhclient_config }} 8 | - source: salt://linux/files/dhclient.conf 9 | - template: jinja 10 | 11 | {%- elif network.dhclient.enabled is defined and network.dhclient.enabled == False %} 12 | 13 | kill_dhcp_client: 14 | cmd.run: 15 | - name: "pkill dhclient" 16 | - onlyif: "pgrep dhclient" 17 | 18 | {%- endif %} 19 | -------------------------------------------------------------------------------- /linux/network/proxy.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | {%- if network.enabled %} 3 | 4 | {%- if grains.os_family == 'Debian' %} 5 | 6 | {%- if network.proxy.host == 'none' %} 7 | 8 | /etc/profile.d/proxy.sh: 9 | file.absent 10 | 11 | /etc/apt/apt.conf.d/95proxies: 12 | file.absent 13 | 14 | {%- else %} 15 | 16 | /etc/apt/apt.conf.d/95proxies: 17 | file.managed: 18 | - template: jinja 19 | - source: salt://linux/files/95proxies 20 | 21 | {%- endif %} 22 | 23 | {%- endif %} 24 | 25 | {%- endif %} 26 | -------------------------------------------------------------------------------- /linux/system/policyrcd.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | policyrcd_file: 5 | file.managed: 6 | - name: /usr/local/sbin/policy-rc.d 7 | - source: salt://linux/files/policy-rc.d 8 | - mode: 655 9 | - template: jinja 10 | 11 | policyrcd_alternatives: 12 | alternatives.install: 13 | - name: policy-rc.d 14 | - link: /usr/sbin/policy-rc.d 15 | - path: /usr/local/sbin/policy-rc.d 16 | - require: 17 | - file: policyrcd_file 18 | 19 | {%- endif %} 20 | -------------------------------------------------------------------------------- /tests/integration/system/profile_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | describe file('/etc/profile.d/salt_profile_vi_flavors.sh') do 3 | it('should exist') 4 | its('content') { should match /EDITOR=vim/ } 5 | its('content') { should match /PAGER=view/ } 6 | its('content') { should match /alias vi=vim/ } 7 | end 8 | 9 | describe file('/etc/profile.d/salt_profile_locales.sh') do 10 | it('should exist') 11 | its('content') { should match /LANG=en_US/ } 12 | end 13 | 14 | describe file('/etc/profile.d/prompt.sh') do 15 | it('should exist') 16 | end 17 | 18 | -------------------------------------------------------------------------------- /tests/integration/system/repo_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | # PROXIES 3 | # 4 | # globally 5 | describe file('/etc/apt/apt.conf.d/99proxies-salt') do 6 | it('should exist') 7 | its('content') { should_not match /ftp/ } 8 | its('content') { should match /proxy "https.*127.0.2.1:4443"/ } 9 | end 10 | 11 | # per repo 12 | describe file('/etc/apt/apt.conf.d/99proxies-salt-opencontrail') do 13 | it('should exist') 14 | its('content') { should_not match /ftp/ } 15 | its('content') { should match /Acquire::https::proxy::ppa.launchpad.net/ } 16 | end 17 | 18 | -------------------------------------------------------------------------------- /metadata/service/system/init.yml: -------------------------------------------------------------------------------- 1 | applications: 2 | - linux 3 | classes: 4 | - service.linux.support 5 | parameters: 6 | linux: 7 | system: 8 | enabled: true 9 | user: 10 | root: 11 | enabled: true 12 | name: root 13 | home: /root 14 | timezone: Europe/Prague 15 | cluster: default 16 | purge_repos: false 17 | network: 18 | enabled: true 19 | hostname: ${linux:system:name} 20 | fqdn: ${linux:system:name}.${linux:system:domain} 21 | storage: 22 | enabled: true 23 | -------------------------------------------------------------------------------- /linux/files/cgconfig.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | ## 3 | ## This is cgconfig configuration file is managed by Salt 4 | ## 5 | {%- for cgroup_name, cg in system.cgroup.group.items() %} 6 | group {{ cgroup_name }} { 7 | {%- for controller_name, controller in cg.controller.items() %} 8 | {{ controller_name }} { 9 | {%- for v_name, v in controller.items() %} 10 | {{ controller_name }}.{{ v_name }}="{{ v.value }}"; 11 | {%- endfor %} 12 | 13 | } 14 | {%- endfor %} 15 | } 16 | {%- endfor %} 17 | -------------------------------------------------------------------------------- /linux/system/ld.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | {%- if system.enabled %} 4 | 5 | {%- for key in system.ld.library %} 6 | /etc/ld.so.conf.d/{{ key }}.conf: 7 | file.managed: 8 | - user: root 9 | - group: root 10 | - mode: 644 11 | - contents: | 12 | {% for val in system.ld.library[key] -%} 13 | {{ val }} 14 | {% endfor %} 15 | - watch_in: 16 | - cmd: ldconfig_update 17 | {% endfor %} 18 | 19 | ldconfig_update: 20 | cmd.wait: 21 | - name: ldconfig 22 | 23 | {% endif %} 24 | -------------------------------------------------------------------------------- /linux/files/ovs_bridge: -------------------------------------------------------------------------------- 1 | auto {{ bridge_name }} 2 | iface {{ bridge_name }} inet {{ bridge.get('proto', 'static' if bridge.address is defined else 'manual') }} 3 | ovs_type {{ bridge.get('ovs_bridge_type', 'OVSBridge') }} 4 | mtu {{ bridge.get('mtu', '1500') }} 5 | {%- if bridge.address is defined %} 6 | address {{ bridge.address }} 7 | netmask {{ bridge.netmask }} 8 | {%- endif %} 9 | {%- if bridge.gateway is defined %} 10 | gateway {{ bridge.gateway }} 11 | {%- endif %} 12 | {%- if bridge.ovs_options is defined %} 13 | ovs_options {{ bridge.ovs_options }} 14 | {%- endif %} 15 | -------------------------------------------------------------------------------- /linux/meta/meta.yml: -------------------------------------------------------------------------------- 1 | graph: 2 | {%- if pillar.get('linux', {}).system is defined %} 3 | {%- from "linux/map.jinja" import system with context %} 4 | - host: {{ grains.id }} 5 | service: linux.system 6 | type: software-system 7 | relations: 8 | {%- if system.repo is defined %} 9 | {%- for repo_name, repo in system.repo.items() %} 10 | {%- if repo.get('enabled', True) %} 11 | - service: apt.repo 12 | host_external: {{ repo.source }} 13 | direction: source 14 | type: tcp-http 15 | {%- endif %} 16 | {%- endfor %} 17 | {%- endif %} 18 | {%- endif %} 19 | -------------------------------------------------------------------------------- /_modules/linux_hosts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Module for defining new filter for sorting 4 | host names/alias by FQDN first and alphabetically 5 | ''' 6 | 7 | from jinja2 import Undefined 8 | 9 | def __virtual__(): 10 | return 'linux_hosts' 11 | 12 | def fqdn_sort_fn(n1): 13 | length = len(n1) 14 | return length 15 | 16 | def fqdn_sort_filter(iterable): 17 | if iterable is None or isinstance(iterable, Undefined): 18 | return iterable 19 | # Do effective custom sorting of iterable here 20 | return sorted(set(iterable), key=fqdn_sort_fn) 21 | -------------------------------------------------------------------------------- /linux/files/cgrules.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | ## 3 | ## This is cgrules configuration file is managed by Salt 4 | ## 5 | # 6 | {%- for cgroup_name, cg in system.cgroup.group.items() %} 7 | {%- for subject in cg.mapping.subjects %} 8 | {{ subject }}{% raw %} {% endraw %}{%- for controller_name, controller in cg.controller.items() -%}{{ controller_name }}{%- if not loop.last -%},{%- endif -%}{%- endfor -%}{% raw %} {% endraw %}{{ cgroup_name }} 9 | {%- endfor %} 10 | {%- endfor %} 11 | -------------------------------------------------------------------------------- /linux/files/multipath/_ibm_storwize.conf: -------------------------------------------------------------------------------- 1 | 2 | device { 3 | vendor "IBM" 4 | product "2145" 5 | path_grouping_policy group_by_prio 6 | getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" 7 | features "1 queue_if_no_path" 8 | prio alua 9 | path_checker tur 10 | failback immediate 11 | no_path_retry "5" 12 | rr_min_io 1 13 | polling_interval 30 14 | dev_loss_tmo 120 15 | } 16 | -------------------------------------------------------------------------------- /linux/files/tty.upstart: -------------------------------------------------------------------------------- 1 | # {{ name }} - getty 2 | # 3 | # This service maintains a getty on tty1 from the point the system is 4 | # started until it is shut down again. 5 | 6 | start on stopped rc RUNLEVEL=[2345] and ( 7 | not-container or 8 | container CONTAINER=lxc or 9 | container CONTAINER=lxc-libvirt) 10 | 11 | stop on runlevel [!2345] 12 | 13 | respawn 14 | exec /sbin/getty{% if tty.get('autologin', False) %} --autologin {{ tty.autologin }}{% endif %} -8 {{ tty.get('rate', 38400) }} {{ name }}{% if tty.term is defined %} {{ tty.term }}{% endif %} 15 | -------------------------------------------------------------------------------- /linux/files/ovs_port: -------------------------------------------------------------------------------- 1 | auto {{ port_name }} 2 | allow-{{ port.bridge }} {{ port_name }} 3 | iface {{ port_name }} inet {{ port.get('proto', 'manual') }} 4 | ovs_type {{ port.get('ovs_port_type', 'OVSIntPort') }} 5 | mtu {{ port.get('mtu', '1500') }} 6 | ovs_bridge {{ port.bridge }} 7 | {%- if port.get('proto', 'manual') == 'static' %} 8 | address {{ port.address }} 9 | netmask {{ port.netmask }} 10 | {%- endif %} 11 | {%- if port.gateway is defined %} 12 | gateway {{ port.gateway }} 13 | {%- endif %} 14 | {%- if port.ovs_options is defined %} 15 | ovs_options {{ port.ovs_options }} 16 | {%- endif %} 17 | -------------------------------------------------------------------------------- /linux/system/locale.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- for locale_name, locale in system.locale.items() %} 5 | {%- if locale.get('enabled', True) %} 6 | 7 | linux_locale_{{ locale_name }}: 8 | locale.present: 9 | - name: {{ locale_name }} 10 | 11 | {%- if locale.get('default', False) %} 12 | linux_locale_default: 13 | locale.system: 14 | - name: {{ locale_name }} 15 | - require: 16 | - locale: linux_locale_{{ locale_name }} 17 | {%- endif %} 18 | 19 | {%- endif %} 20 | {%- endfor %} 21 | 22 | {%- endif %} 23 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: salt-formula-linux 2 | Maintainer: Ales Komarek 3 | Section: admin 4 | Priority: optional 5 | Build-Depends: salt-master, python, python-yaml, debhelper (>= 9), salt-master, python, python-yaml 6 | Standards-Version: 3.9.6 7 | Homepage: http://www.tcpcloud.eu 8 | Vcs-Browser: https://github.com/tcpcloud/salt-formula-linux 9 | Vcs-Git: https://github.com/tcpcloud/salt-formula-linux.git 10 | 11 | Package: salt-formula-linux 12 | Architecture: all 13 | Depends: ${misc:Depends} 14 | Description: Linux salt formula 15 | Configure Linux operating system. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015 tcp cloud a. s. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /linux/files/multipath/_fujitsu_eternus_dxl.conf: -------------------------------------------------------------------------------- 1 | 2 | device { 3 | vendor "FUJITSU" 4 | product "ETERNUS_DXL" 5 | prio alua 6 | path_grouping_policy group_by_prio 7 | path_selector "round-robin 0" 8 | failback immediate 9 | no_path_retry 0 (*1) 10 | path_checker tur 11 | dev_loss_tmo 2097151 (*2) 12 | fast_io_fail_tmo 1 13 | } 14 | -------------------------------------------------------------------------------- /linux/system/apparmor.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | include: 4 | - linux.system.package 5 | 6 | {%- if system.apparmor.enabled %} 7 | 8 | apparmor_service: 9 | service.running: 10 | - name: apparmor 11 | - enable: true 12 | - require: 13 | - pkg: linux_repo_prereq_pkgs 14 | 15 | {%- else %} 16 | 17 | apparmor_service_disable: 18 | service.dead: 19 | - name: apparmor 20 | - enable: false 21 | 22 | apparmor_teardown: 23 | cmd.wait: 24 | - name: /etc/init.d/apparmor teardown 25 | - watch: 26 | - service: apparmor_service_disable 27 | 28 | {%- endif %} 29 | -------------------------------------------------------------------------------- /linux/system/limit.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- for name, limit in system.limit.items() %} 5 | 6 | linux_limit_{{ name }}: 7 | {%- if limit.get('enabled', True) %} 8 | file.managed: 9 | - name: /etc/security/limits.d/90-salt-{{ name }}.conf 10 | - source: salt://linux/files/limits.conf 11 | - template: jinja 12 | - defaults: 13 | limit_name: {{ name }} 14 | {%- else %} 15 | file.absent: 16 | - name: /etc/security/limits.d/90-salt-{{ name }}.conf 17 | {%- endif %} 18 | 19 | {%- endfor %} 20 | 21 | {%- endif %} 22 | -------------------------------------------------------------------------------- /linux/files/prompt.sh: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | # Don't set special prompt when not using Bash or ZSH 4 | [ -n "$BASH_VERSION" -o -n "$ZSH_VERSION" ] || return 0 5 | 6 | # Don't set prompt on non-interactive shell 7 | [[ $- == *i* ]] || return 0 8 | 9 | {%- for user, prompt in system.prompt.items() %} 10 | {% if user != "default" %} 11 | if [ "$USERNAME" == "{{ user }}" ]; then 12 | export PS1="{{ prompt }} " 13 | return 0 14 | fi 15 | {% endif %} 16 | {%- endfor %} 17 | 18 | {% if system.prompt.default is defined %} 19 | export PS1="{{ system.prompt.default }} " 20 | {%- endif %} 21 | -------------------------------------------------------------------------------- /linux/files/setup-loopback-device.systemd: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Setup {{ device_name }} device 3 | DefaultDependencies=no 4 | After=systemd-udev-settle.service 5 | Before=lvm2-activation-early.service 6 | Wants=systemd-udev-settle.service 7 | 8 | [Service] 9 | {# The command is prefixed with '-' to consider it a success if the loopback device is already setup #} 10 | ExecStart=-/sbin/losetup {{ device_name }} {{ file }} 11 | {# In order to Salt can recognize oneshot service as running, we need it to remain active after it exited #} 12 | RemainAfterExit=true 13 | Type=oneshot 14 | 15 | [Install] 16 | WantedBy=local-fs.target 17 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: salt-formula-linux 3 | Upstream-Contact: Ales Komarek 4 | Source: https://github.com/tcpcloud/salt-formula-linux 5 | 6 | Files: * 7 | Copyright: 2014-2015 tcp cloud a.s. 8 | License: Apache-2.0 9 | Copyright (C) 2014-2015 tcp cloud a.s. 10 | . 11 | Licensed under the Apache License, Version 2.0 (the "License"); 12 | you may not use this file except in compliance with the License. 13 | . 14 | On a Debian system you can find a copy of this license in 15 | /usr/share/common-licenses/Apache-2.0. 16 | -------------------------------------------------------------------------------- /linux/system/autoupdates.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.autoupdates.get('enabled', True) %} 5 | 6 | {%- if system.autoupdates.pkgs %} 7 | linux_autoupdates_packages: 8 | pkg.installed: 9 | - pkgs: {{ system.autoupdates.pkgs | json }} 10 | {%- endif %} 11 | 12 | {%- if grains.os_family == 'Debian' %} 13 | /etc/apt/apt.conf.d/90autoupdates: 14 | file.managed: 15 | - source: salt://linux/files/90autoupdates 16 | - template: jinja 17 | - user: root 18 | - group: root 19 | - mode: 644 20 | {%- endif %} 21 | 22 | {%- endif %} 23 | 24 | {%- endif %} 25 | -------------------------------------------------------------------------------- /linux/files/preferences_repo: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | {%- set repo = system.repo[repo_name] -%} 3 | {%- if repo.pinning is defined -%} 4 | {%- for id,pin in repo.pinning|dictsort -%} 5 | {% if pin.get('enabled', False) %} 6 | 7 | Package: {{ pin.get('package','*') }} 8 | Pin: {{ pin.pin }} 9 | Pin-Priority: {{ pin.priority }} 10 | {%- endif %} 11 | {%- endfor -%} 12 | {%- elif repo.pin is defined -%} 13 | {%- for pin in repo.pin -%} 14 | {%- set package = pin.get('package', '*') %} 15 | Package: {{ package }} 16 | Pin: {{ pin.pin }} 17 | Pin-Priority: {{ pin.priority }} 18 | {%- endfor %} 19 | {%- endif -%} 20 | -------------------------------------------------------------------------------- /linux/files/sysfs.conf: -------------------------------------------------------------------------------- 1 | # Sysfs file for {{ name }} managed by salt-minion(1) 2 | # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN 3 | 4 | {%- if sysfs is mapping %} 5 | {%- set sysfs_list = [sysfs] %} 6 | {%- else %} 7 | {%- set sysfs_list = sysfs %} 8 | {%- endif %} 9 | 10 | 11 | {%- for item in sysfs_list %} 12 | {%- for key, value in item.items() %} 13 | {%- if key in ["mode", "owner"] %} 14 | {%- for attr, val in value.items() %} 15 | mode {{ attr }} = {{ val }} 16 | {%- endfor %} 17 | {%- else %} 18 | {{ key }} = {{ value }} 19 | {%- endif %} 20 | {%- endfor %} 21 | {%- endfor %} 22 | 23 | {#- 24 | vim: syntax=jinja 25 | -#} 26 | -------------------------------------------------------------------------------- /linux/meta/logrotate.yml: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | 3 | {%- if system.atop.enabled %} 4 | job: 5 | atop: 6 | - files: 7 | - {{ system.atop.logpath }}/atop* 8 | - {{ system.atop.logpath }}/{{ system.atop.outfile }} 9 | options: 10 | - olddir {{ system.atop.logpath }}/old 11 | - compress 12 | - delaycompress 13 | - missingok 14 | - notifempty 15 | - rotate: 10 16 | - daily 17 | - minsize: 20M 18 | - maxsize: 500M 19 | - postrotate: "if ! service atop status > /dev/null; then service atop restart > /dev/null; fi" 20 | {%- endif %} 21 | -------------------------------------------------------------------------------- /linux/files/proxy.sh: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | export http_proxy="http://{{ network.proxy.host }}:{{ network.proxy.port }}/" 3 | export https_proxy="http://{{ network.proxy.host }}:{{ network.proxy.port }}/" 4 | export ftp_proxy="http://{{ network.proxy.host }}:{{ network.proxy.port }}/" 5 | export no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com" 6 | export HTTP_PROXY="http://{{ network.proxy.host }}:{{ network.proxy.port }}/" 7 | export HTTPS_PROXY="http://{{ network.proxy.host }}:{{ network.proxy.port }}/" 8 | export FTP_PROXY="http://{{ network.proxy.host }}:{{ network.proxy.port }}/" 9 | export NO_PROXY="localhost,127.0.0.1,localaddress,.localdomain.com" -------------------------------------------------------------------------------- /linux/files/sudoer-aliases: -------------------------------------------------------------------------------- 1 | # sudoer aliases, file managed by salt-minion 2 | # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN 3 | 4 | {%- for alias,commands in aliases.get('command',{}).items() %} 5 | Cmnd_Alias {{ alias }}={{ commands|join(', ') }} 6 | {%- endfor %} 7 | 8 | {%- for alias,users in aliases.get('user',{}).items() %} 9 | User_Alias {{ alias }}={{ users|join(', ') }} 10 | {%- endfor %} 11 | 12 | {%- for alias,users in aliases.get('runas',{}).items() %} 13 | Runas_Alias {{ alias }}={{ users|join(', ') }} 14 | {%- endfor %} 15 | 16 | {%- for alias,hosts in aliases.get('host',{}).items() %} 17 | Host_Alias {{ alias }}={{ hosts|join(', ') }} 18 | {%- endfor %} 19 | 20 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | salt-formula-linux (2017.4.1) trusty; urgency=medium 2 | 3 | * New version 4 | 5 | -- Filip Pytloun Tue, 25 Apr 2017 16:05:44 +0200 6 | 7 | salt-formula-linux (2017.4) trusty; urgency=medium 8 | 9 | * New version 10 | 11 | -- Filip Pytloun Tue, 25 Apr 2017 16:03:41 +0200 12 | 13 | salt-formula-linux (0.2) trusty; urgency=medium 14 | 15 | * First public release 16 | 17 | -- Filip Pytloun Tue, 06 Oct 2015 16:38:46 +0200 18 | 19 | salt-formula-linux (0.1) trusty; urgency=medium 20 | 21 | * Initial release 22 | 23 | -- Ales Komarek Thu, 13 Aug 2015 23:23:41 +0200 24 | -------------------------------------------------------------------------------- /linux/files/resolv.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %}# Dynamic resolv.conf(5) file for glibc resolver(3) generated by salt-minion(1) 2 | # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN 3 | {% if network.resolv.get('search', False) -%} 4 | search {{ network.resolv.search|join(' ') }} 5 | {%- endif %} 6 | {% if network.resolv.get('domain', False) -%} 7 | domain {{ network.resolv.domain }} 8 | {%- endif %} 9 | {%- for nameserver in network.resolv.dns %} 10 | nameserver {{ nameserver }} 11 | {%- endfor %} 12 | {%- if network.resolv.get('options', False) %} 13 | {%- for option in network.resolv.options %} 14 | options {{ option }} 15 | {%- endfor %} 16 | {%- endif %} 17 | -------------------------------------------------------------------------------- /linux/system/apt.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | {%- if grains.os_family == 'Debian' %} 4 | 5 | {%- if system.repo|length > 0 %} 6 | include: 7 | - linux.system.repo 8 | {%- endif %} 9 | 10 | {%- for key, config in system.apt.get('config', {}).items() %} 11 | 12 | linux_apt_conf_{{ key }}: 13 | file.managed: 14 | - name: /etc/apt/apt.conf.d/99{{ key }}-salt 15 | - template: jinja 16 | - source: salt://linux/files/apt.conf 17 | - defaults: 18 | config: {{ config|yaml }} 19 | {%- if system.repo|length > 0 %} 20 | - require_in: 21 | - pkg: linux_repo_prereq_pkgs 22 | {%- endif %} 23 | 24 | {%- endfor %} 25 | 26 | {%- endif %} 27 | {%- endif %} 28 | -------------------------------------------------------------------------------- /linux/storage/multipath.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import storage with context %} 2 | {%- if storage.enabled and storage.multipath.enabled %} 3 | 4 | linux_storage_multipath_packages: 5 | pkg.installed: 6 | - pkgs: {{ storage.multipath.pkgs | json }} 7 | 8 | linux_storage_multipath_config: 9 | file.managed: 10 | - name: /etc/multipath.conf 11 | - source: salt://linux/files/multipath.conf 12 | - template: jinja 13 | - require: 14 | - pkg: linux_storage_multipath_packages 15 | 16 | linux_storage_multipath_service: 17 | service.running: 18 | - enable: true 19 | - name: {{ storage.multipath.service }} 20 | - watch: 21 | - file: linux_storage_multipath_config 22 | - sig: multipathd 23 | 24 | {%- endif %} 25 | -------------------------------------------------------------------------------- /linux/system/prompt.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | /etc/profile.d/prompt.sh: 5 | file.managed: 6 | - source: salt://linux/files/prompt.sh 7 | - template: jinja 8 | 9 | {%- if grains.os_family == 'Debian' %} 10 | /etc/bash.bashrc: 11 | file.replace: 12 | - pattern: ".*PS1=.*" 13 | - repl: ": # Prompt is set by /etc/profile.d/prompt.sh" 14 | 15 | /etc/skel/.bashrc: 16 | file.replace: 17 | - pattern: ".*PS1=.*" 18 | - repl: ": # Prompt is set by /etc/profile.d/prompt.sh" 19 | 20 | /root/.bashrc: 21 | file.replace: 22 | - pattern: ".*PS1=.*" 23 | - repl: ": # Prompt is set by /etc/profile.d/prompt.sh" 24 | {%- endif %} 25 | 26 | {%- endif %} 27 | -------------------------------------------------------------------------------- /linux/system/mcelog.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.get('mcelog',{}).get('enabled', False) %} 5 | 6 | mcelog_packages: 7 | pkg.installed: 8 | - name: mcelog 9 | 10 | mcelog_conf: 11 | file.managed: 12 | - name: /etc/mcelog/mcelog.conf 13 | - source: salt://linux/files/mcelog.conf 14 | - template: jinja 15 | - user: root 16 | - group: root 17 | - mode: 644 18 | - require: 19 | - pkg: mcelog_packages 20 | 21 | mce_service: 22 | service.running: 23 | - name: mcelog 24 | - enable: true 25 | - require: 26 | - pkg: mcelog_packages 27 | - watch: 28 | - file: mcelog_conf 29 | 30 | {%- endif %} 31 | 32 | {%- endif %} 33 | -------------------------------------------------------------------------------- /linux/files/nsswitch.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import ldap with context -%} 2 | # /etc/nsswitch.conf 3 | # 4 | # Example configuration of GNU Name Service Switch functionality. 5 | # If you have the `glibc-doc-reference' and `info' packages installed, try: 6 | # `info libc "Name Service Switch"' for information about this file. 7 | 8 | passwd: compat{%- if ldap.enabled %} ldap{%- endif %} 9 | group: compat{%- if ldap.enabled %} ldap{%- endif %} 10 | shadow: compat{%- if ldap.enabled %} ldap{%- endif %} 11 | gshadow: files 12 | 13 | hosts: files dns 14 | networks: files 15 | 16 | protocols: db files 17 | services: db files 18 | ethers: db files 19 | rpc: db files 20 | 21 | netgroup: nis 22 | -------------------------------------------------------------------------------- /linux/storage/init.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import storage with context %} 2 | {%- if storage.mount|length > 0 or storage.swap|length > 0 or storage.multipath.enabled or storage.disk|length > 0 or storage.lvm|length > 0 or storage.loopback|length > 0 %} 3 | include: 4 | {%- if storage.loopback|length > 0 %} 5 | - linux.storage.loopback 6 | {%- endif %} 7 | {%- if storage.disk|length > 0 %} 8 | - linux.storage.disk 9 | {%- endif %} 10 | {%- if storage.lvm|length > 0 %} 11 | - linux.storage.lvm 12 | {%- endif %} 13 | {%- if storage.mount|length > 0 %} 14 | - linux.storage.mount 15 | {%- endif %} 16 | {%- if storage.swap|length > 0 %} 17 | - linux.storage.swap 18 | {%- endif %} 19 | {%- if storage.multipath.enabled %} 20 | - linux.storage.multipath 21 | {%- endif %} 22 | {%- endif %} 23 | -------------------------------------------------------------------------------- /linux/system/journal.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled and grains.get('init', None) == 'systemd' %} 3 | 4 | {%- if system.systemd.journal is defined %} 5 | 6 | linux_systemd_journal_config: 7 | file.managed: 8 | - name: /etc/systemd/journald.conf.d/90-salt.conf 9 | - source: salt://linux/files/journal.conf 10 | - template: jinja 11 | - makedirs: True 12 | - defaults: 13 | settings: {{ system.systemd.journal|tojson }} 14 | - watch_in: 15 | - module: linux_journal_systemd_reload 16 | 17 | linux_journal_systemd_reload: 18 | module.wait: 19 | - name: service.restart 20 | - m_name: systemd-journald 21 | - require: 22 | - module: service.systemctl_reload 23 | 24 | {%- endif %} 25 | {%- endif %} -------------------------------------------------------------------------------- /linux/system/console.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.console is defined %} 5 | 6 | {%- for tty_name, console in system.console.items() %} 7 | 8 | {%- if grains.get('init', None) == 'upstart' %} 9 | {{ tty_name }}_service_file: 10 | file.managed: 11 | - name: /etc/init/{{ tty_name }}.conf 12 | - source: salt://linux/files/tty.upstart 13 | - template: jinja 14 | - defaults: 15 | name: {{ tty_name }} 16 | tty: {{ console }} 17 | {%- endif %} 18 | 19 | {{ tty_name }}_service: 20 | service.running: 21 | - enable: true 22 | - name: {{ tty_name }} 23 | - watch: 24 | - file: {{ tty_name }}_service_file 25 | 26 | {%- endfor %} 27 | 28 | {%- endif %} 29 | 30 | {%- endif %} 31 | -------------------------------------------------------------------------------- /linux/files/multipath.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import storage with context %} 2 | ## 3 | ## This is multipath-tools configuration file managed by Salt 4 | ## 5 | 6 | defaults { 7 | # getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n" 8 | user_friendly_names no 9 | } 10 | 11 | blacklist { 12 | {%- for device in storage.multipath.get('blacklist_devices', []) %} 13 | wwid {{ salt['cmd.shell']('/lib/udev/scsi_id -g -u '+device) }} 14 | {%- endfor %} 15 | devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st|nbd)[0-9]*" 16 | } 17 | 18 | devices { 19 | {%- for backend in storage.multipath.get('backends', []) %} 20 | {%- include "linux/files/multipath/_" + backend + ".conf" %} 21 | {%- endfor %} 22 | } 23 | -------------------------------------------------------------------------------- /linux/system/profile.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | profile.d_clean: 5 | file.directory: 6 | - name: /etc/profile.d 7 | - clean: true 8 | - exclude_pat: 'E@^((?!salt_profile*).)*$' 9 | 10 | {%- if system.profile|length > 0 %} 11 | 12 | {%- for name, script in system.profile.items() %} 13 | profile.d_script_{{ name }}: 14 | file.managed: 15 | - name: /etc/profile.d/salt_profile_{{ name }}{%if name.split('.')|length == 1 %}.sh{% endif %} 16 | - source: 17 | - salt://linux/files/etc_profile_{{ name }} 18 | - salt://linux/files/etc_profile 19 | - template: jinja 20 | - defaults: 21 | script: {{ script|yaml }} 22 | - require_in: 23 | - file: profile.d_clean 24 | {% endfor %} 25 | 26 | {%- endif %} 27 | {%- endif %} 28 | 29 | -------------------------------------------------------------------------------- /linux/network/init.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | include: 3 | {%- if network.hostname is defined %} 4 | - linux.network.hostname 5 | {%- endif %} 6 | {%- if network.host|length > 0 or network.get('purge_hosts', True) %} 7 | - linux.network.host 8 | {%- endif %} 9 | {%- if network.dpdk is defined %} 10 | - linux.network.dpdk 11 | {%- endif %} 12 | {%- if network.dhclient is defined %} 13 | - linux.network.dhclient 14 | {%- endif %} 15 | {%- if network.systemd|length > 0 %} 16 | - linux.network.systemd 17 | {%- endif %} 18 | {%- if network.openvswitch is defined %} 19 | - linux.network.openvswitch 20 | {%- endif %} 21 | {%- if network.interface|length > 0 %} 22 | - linux.network.interface 23 | {%- endif %} 24 | {%- if network.resolv is defined %} 25 | - linux.network.resolv 26 | {%- endif %} 27 | - linux.network.proxy 28 | -------------------------------------------------------------------------------- /linux/system/auth/duo.sls: -------------------------------------------------------------------------------- 1 | {%- if grains['os'] == 'Ubuntu' %} 2 | 3 | package_duo: 4 | pkg.installed: 5 | - name: duo-unix 6 | - skip_verify: True 7 | 8 | 9 | login_duo: 10 | file.managed: 11 | - name: /etc/duo/login_duo.conf 12 | - source: salt://linux/files/login_duo.conf 13 | - template: jinja 14 | - user: 'root' 15 | - group: 'root' 16 | - mode: '0600' 17 | 18 | 19 | pam_duo: 20 | file.managed: 21 | - name: /etc/duo/pam_duo.conf 22 | - source: salt://linux/files/login_duo.conf 23 | - template: jinja 24 | - user: 'root' 25 | - group: 'root' 26 | - mode: '0600' 27 | 28 | pam-sshd_config: 29 | file.managed: 30 | - name: /etc/pam.d/sshd 31 | - user: root 32 | - group: root 33 | - source: salt://linux/files/pam-sshd 34 | - mode: 600 35 | - template: jinja 36 | 37 | {%- endif %} 38 | 39 | -------------------------------------------------------------------------------- /linux/files/etc_environment: -------------------------------------------------------------------------------- 1 | 2 | {%- for name,value in variables.items() if not name.lower().endswith('_proxy') %} 3 | 4 | {%- if value is sequence and value is not string %} 5 | {{ name }}="{{ value|join(':') }}" 6 | 7 | {%- else %} 8 | {{ name }}="{{ value }}" 9 | 10 | {%- endif %} 11 | {%- endfor %} 12 | 13 | {%- if ftp_proxy and ftp_proxy.lower() != 'none' %} 14 | ftp_proxy="{{ ftp_proxy }}" 15 | FTP_PROXY="{{ ftp_proxy }}" 16 | {%- endif %} 17 | 18 | {%- if http_proxy and http_proxy.lower() != 'none' %} 19 | http_proxy="{{ http_proxy }}" 20 | HTTP_PROXY="{{ http_proxy }}" 21 | {%- endif %} 22 | 23 | {%- if https_proxy and https_proxy.lower() != 'none' %} 24 | https_proxy="{{ https_proxy }}" 25 | HTTPS_PROXY="{{ https_proxy }}" 26 | {%- endif %} 27 | 28 | {%- if no_proxy %} 29 | no_proxy="{{ no_proxy|join(',') }}" 30 | NO_PROXY="{{ no_proxy|join(',') }}" 31 | {%- endif %} 32 | 33 | -------------------------------------------------------------------------------- /linux/files/netconsole.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | # default port is 514 3 | #PORT=6666 4 | {%- if system.netconsole is mapping and system.netconsole.port is defined %} 5 | PORT="{{ system.netconsole.port }}" 6 | {%- endif %} 7 | 8 | # unicast, could be multiline 9 | #netconsole ens3 192.168.1.32 fa:16:3e:8d:f6:d0 10 | {%- if system.netconsole is mapping and system.netconsole.target is mapping %} 11 | {%- for target, data in system.netconsole.target.items() %} 12 | {%- if data is mapping %} 13 | netconsole "{{ data.get('interface', '${interface}') }}" "{{ target }}" "{{ data.get('mac', '') }}" 14 | {%- endif %} 15 | {%- endfor %} 16 | {%- endif %} 17 | 18 | # set up dmesg log level 19 | # dmesg -n info 20 | {%- if system.netconsole is mapping and system.netconsole.loglevel is defined %} 21 | dmesg -n "{{ system.netconsole.loglevel }}" 22 | {%- endif %} 23 | -------------------------------------------------------------------------------- /tests/pillar/system_file.sls: -------------------------------------------------------------------------------- 1 | linux: 2 | network: 3 | enabled: true 4 | hostname: linux 5 | fqdn: linux.ci.local 6 | system: 7 | name: linux 8 | enabled: true 9 | file: 10 | /tmp/sample.txt: 11 | source: http://techslides.com/demos/samples/sample.txt 12 | source_hash: 5452459724e85b4e12277d5f8aab8fc9 13 | sample2.txt: 14 | name: /tmp/sample2.txt 15 | source: http://techslides.com/demos/samples/sample.txt 16 | test2: 17 | name: /tmp/test2.txt 18 | contents: | 19 | line1 20 | line2 21 | user: root 22 | group: root 23 | mode: 700 24 | dir_mode: 700 25 | encoding: utf-8 26 | makedirs: true 27 | test3: 28 | name: /tmp/test3.txt 29 | source: salt://linux/files/test/file_template.jinja 30 | template: jinja 31 | test: 32 | example: "bar" 33 | -------------------------------------------------------------------------------- /linux/files/pam-add-profile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$(basename $EDITOR 2>/dev/null)" == "$(basename $0)" ]; then 4 | PROFILES=$(debconf-get-selections | grep libpam-runtime/profiles | awk '{$1=$2=$3=""; print $0}') 5 | 6 | for profile in ${PROFILE[@]}; do 7 | if [[ $PROFILES =~ $profile ]]; then 8 | continue 9 | else 10 | PROFILES="${PROFILES}, ${profile}" 11 | fi 12 | done 13 | 14 | for profile in /usr/share/pam-configs/*; do 15 | profile_name=$(grep Name: $profile | cut -d ' ' -f 2-) 16 | PROFILES=$(echo $PROFILES | sed s,$(basename $profile),"${profile_name}",g) 17 | done 18 | 19 | cat > $1 < 21 | # 22 | # Remediation 23 | # =========== 24 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 25 | # 26 | # install hfs /bin/true 27 | # 28 | parameters: 29 | linux: 30 | system: 31 | kernel: 32 | module: 33 | hfs: 34 | install: 35 | command: /bin/true 36 | 37 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-5-4.yml: -------------------------------------------------------------------------------- 1 | # 3.5.4 Ensure TIPC is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The Transparent Inter-Process Communication (TIPC) protocol is designed 6 | # to provide communication between cluster nodes. 7 | # 8 | # Rationale 9 | # ========= 10 | # If the protocol is not being used, it is recommended that kernel module 11 | # not be loaded, disabling the service to reduce the potential attack surface. 12 | # 13 | # Audit 14 | # ===== 15 | # Run the following commands and verify the output is as indicated: 16 | # 17 | # # modprobe -n -v tipc 18 | # install /bin/true 19 | # # lsmod | grep tipc 20 | # 21 | # 22 | # Remediation 23 | # =========== 24 | # 25 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 26 | # 27 | # install tipc /bin/true 28 | # 29 | parameters: 30 | linux: 31 | system: 32 | kernel: 33 | module: 34 | tipc: 35 | install: 36 | command: /bin/true 37 | 38 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-1-1-3.yml: -------------------------------------------------------------------------------- 1 | # 1.1.1.3 Ensure mounting of jffs2 filesystems is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The jffs2 (journaling flash filesystem 2) filesystem type is a 6 | # log-structured filesystem used in flash memory devices. 7 | # 8 | # Rationale 9 | # ========= 10 | # Removing support for unneeded filesystem types reduces the local attack 11 | # surface of the system. If this filesystem type is not needed, disable it. 12 | # 13 | # Audit 14 | # ===== 15 | # Run the following commands and verify the output is as indicated: 16 | # 17 | # # modprobe -n -v jffs2 18 | # install /bin/true 19 | # # lsmod | grep jffs2 20 | # 21 | # 22 | # Remediation 23 | # =========== 24 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 25 | # 26 | # install jffs2 /bin/true 27 | # 28 | parameters: 29 | linux: 30 | system: 31 | kernel: 32 | module: 33 | jffs2: 34 | install: 35 | command: /bin/true 36 | 37 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-1-1-5.yml: -------------------------------------------------------------------------------- 1 | # 1.1.1.5 Ensure mounting of hfsplus filesystems is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The hfsplus filesystem type is a hierarchical filesystem designed to 6 | # replace hfs that allows you to mount Mac OS filesystems. 7 | # 8 | # Rationale 9 | # ========= 10 | # Removing support for unneeded filesystem types reduces the local attack 11 | # surface of the system. If this filesystem type is not needed, disable it. 12 | # 13 | # Audit 14 | # ===== 15 | # Run the following commands and verify the output is as indicated: 16 | # 17 | # # modprobe -n -v hfsplus 18 | # install /bin/true 19 | # # lsmod | grep hfsplus 20 | # 21 | # 22 | # Remediation 23 | # =========== 24 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 25 | # 26 | # install hfsplus /bin/true 27 | # 28 | parameters: 29 | linux: 30 | system: 31 | kernel: 32 | module: 33 | hfsplus: 34 | install: 35 | command: /bin/true 36 | 37 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-1-1-2.yml: -------------------------------------------------------------------------------- 1 | # 1.1.1.2 Ensure mounting of freevxfs filesystems is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The freevxfs filesystem type is a free version of the Veritas type 6 | # filesystem. This is the primary filesystem type for HP-UX operating systems. 7 | # 8 | # Rationale 9 | # ========= 10 | # Removing support for unneeded filesystem types reduces the local attack 11 | # surface of the system. If this filesystem type is not needed, disable it. 12 | # 13 | # Audit 14 | # ===== 15 | # Run the following commands and verify the output is as indicated: 16 | # 17 | # # modprobe -n -v freevxfs 18 | # install /bin/true 19 | # # lsmod | grep freevxfs 20 | # 21 | # 22 | # Remediation 23 | # =========== 24 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 25 | # 26 | # install freevxfs /bin/true 27 | # 28 | parameters: 29 | linux: 30 | system: 31 | kernel: 32 | module: 33 | freevxfs: 34 | install: 35 | command: /bin/true 36 | 37 | -------------------------------------------------------------------------------- /linux/network/hostname.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | {%- if network.enabled %} 3 | 4 | {%- if grains.os_family in ['Arch', 'Debian'] %} 5 | 6 | linux_hostname_file: 7 | file.managed: 8 | - name: {{ network.hostname_file }} 9 | - source: salt://linux/files/hostname 10 | - template: jinja 11 | - user: root 12 | - group: root 13 | - mode: 644 14 | - watch_in: 15 | - cmd: linux_enforce_hostname 16 | 17 | {%- endif %} 18 | 19 | {# Change state to proper one, after releasing patch: 20 | https://github.com/saltstack/salt/pull/45748/files/74599bbdfcf99f45d3a31296887097fade31cbf1 21 | linux_enforce_hostname: 22 | network.system: 23 | - enabled: True 24 | - hostname: {{ network.hostname }} 25 | - apply_hostname: True 26 | - retain_settings: True 27 | #} 28 | linux_enforce_hostname: 29 | cmd.run: 30 | - name: hostname {{ network.hostname }} 31 | - unless: test "$(hostname)" = "{{ network.hostname }}" 32 | {%- if grains.get('noservices') %} 33 | - onlyif: /bin/false 34 | {%- endif %} 35 | 36 | {%- endif %} 37 | -------------------------------------------------------------------------------- /linux/system/grub.sls: -------------------------------------------------------------------------------- 1 | grub_d_directory: 2 | file.directory: 3 | - name: /etc/default/grub.d 4 | - user: root 5 | - group: root 6 | - mode: 755 7 | - makedirs: True 8 | 9 | {%- if grains['os_family'] == 'RedHat' %} 10 | {%- set boot_grub_cfg = '/boot/grub2/grub.cfg' %} 11 | /etc/default/grub: 12 | file.append: 13 | - text: 14 | - for i in $(ls /etc/default/grub.d);do source /etc/default/grub.d/$i ;done 15 | 16 | grub_update: 17 | cmd.wait: 18 | - name: grub2-mkconfig -o {{ boot_grub_cfg }} 19 | 20 | {%- else %} 21 | {%- set boot_grub_cfg = '/boot/grub/grub.cfg' %} 22 | 23 | grub_update: 24 | cmd.wait: 25 | - name: update-grub 26 | {%- if grains.get('virtual_subtype') in ['Docker', 'LXC'] %} 27 | - onlyif: /bin/false 28 | {%- endif %} 29 | 30 | {%- endif %} 31 | 32 | grub_cfg_permissions: 33 | file.managed: 34 | - name: {{ boot_grub_cfg }} 35 | - user: 'root' 36 | - owner: 'root' 37 | - mode: '400' 38 | - replace: false 39 | - onlyif: test -f {{ boot_grub_cfg }} 40 | - require: 41 | - cmd: grub_update 42 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-5-3.yml: -------------------------------------------------------------------------------- 1 | # 3.5.3 Ensure RDS is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The Reliable Datagram Sockets (RDS) protocol is a transport layer protocol 6 | # designed to provide low-latency, high-bandwidth communications between 7 | # cluster nodes. It was developed by the Oracle Corporation. 8 | # 9 | # Rationale 10 | # ========= 11 | # If the protocol is not being used, it is recommended that kernel module 12 | # not be loaded, disabling the service to reduce the potential attack surface. 13 | # 14 | # Audit 15 | # ===== 16 | # Run the following commands and verify the output is as indicated: 17 | # 18 | # # modprobe -n -v rds 19 | # install /bin/true 20 | # # lsmod | grep rds 21 | # 22 | # 23 | # Remediation 24 | # =========== 25 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 26 | # 27 | # install rds /bin/true 28 | # 29 | parameters: 30 | linux: 31 | system: 32 | kernel: 33 | module: 34 | rds: 35 | install: 36 | command: /bin/true 37 | 38 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-1-1-1.yml: -------------------------------------------------------------------------------- 1 | # 1.1.1.1 Ensure mounting of cramfs filesystems is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The cramfs filesystem type is a compressed read-only Linux filesystem 6 | # embedded in small footprint systems. A cramfs image can be used without 7 | # having to first decompress the image. 8 | # 9 | # Rationale 10 | # ========= 11 | # Removing support for unneeded filesystem types reduces the local attack 12 | # surface of the server. If this filesystem type is not needed, disable it. 13 | # 14 | # Audit 15 | # ===== 16 | # Run the following commands and verify the output is as indicated: 17 | # 18 | # # modprobe -n -v cramfs 19 | # install /bin/true 20 | # # lsmod | grep cramfs 21 | # 22 | # 23 | # Remediation 24 | # =========== 25 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 26 | # 27 | # install cramfs /bin/true 28 | # 29 | parameters: 30 | linux: 31 | system: 32 | kernel: 33 | module: 34 | cramfs: 35 | install: 36 | command: /bin/true 37 | 38 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-6-1-6.yml: -------------------------------------------------------------------------------- 1 | # CIS 6.1.6 Ensure permissions on /etc/passwd- are configured 2 | # 3 | # Description 4 | # =========== 5 | # The /etc/passwd- file contains backup user account information. 6 | # 7 | # Rationale 8 | # ========= 9 | # It is critical to ensure that the /etc/passwd- file is protected from 10 | # unauthorized access. Although it is protected by default, the file 11 | # permissions could be changed either inadvertently or through malicious actions. 12 | # 13 | # Audit 14 | # ===== 15 | # Run the following command and verify Uid and Gid are both 0/root and 16 | # Access is 600 or more restrictive: 17 | # 18 | # # stat /etc/passwd- 19 | # Access: (0600/-rw-------) Uid: (0/root) Gid: (0/root) 20 | # 21 | # Remediation 22 | # =========== 23 | # Run the following command to set permissions on /etc/passwd- : 24 | # 25 | # # chown root:root /etc/passwd- 26 | # # chmod 600 /etc/passwd- 27 | # 28 | parameters: 29 | linux: 30 | system: 31 | file: 32 | /etc/passwd-: 33 | user: 'root' 34 | group: 'root' 35 | mode: '0600' 36 | 37 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-5-4.yml: -------------------------------------------------------------------------------- 1 | # CIS 1.5.4 Ensure prelink is disabled 2 | # 3 | # Description 4 | # =========== 5 | # prelink is a program that modifies ELF shared libraries and ELF dynamically 6 | # linked binaries in such a way that the time needed for the dynamic linker to 7 | # perform relocations at startup significantly decreases. 8 | # 9 | # Rationale 10 | # ========= 11 | # The prelinking feature can interfere with the operation of AIDE, because it 12 | # changes binaries. Prelinking can also increase the vulnerability of the system 13 | # if a malicious user is able to compromise a common library such as libc. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following command and verify prelink is not installed: 18 | # 19 | # # dpkg -s prelink 20 | # 21 | # Remediation 22 | # =========== 23 | # Run the following command to restore binaries to normal: 24 | # 25 | # # prelink -ua 26 | # 27 | # Run the following command to uninstall prelink : 28 | # 29 | # # apt-get remove prelink 30 | # 31 | parameters: 32 | linux: 33 | system: 34 | package: 35 | prelink: 36 | version: removed 37 | 38 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-6-1-8.yml: -------------------------------------------------------------------------------- 1 | # CIS 6.1.8 Ensure permissions on /etc/group- are configured 2 | # 3 | # Description 4 | # =========== 5 | # The /etc/group- file contains a backup list of all the valid groups defined 6 | # in the system. 7 | # 8 | # Rationale 9 | # ========= 10 | # It is critical to ensure that the /etc/group- file is protected from 11 | # unauthorized access. Although it is protected by default, the file 12 | # permissions could be changed either inadvertently or through malicious actions. 13 | # 14 | # Audit 15 | # ===== 16 | # Run the following command and verify Uid and Gid are both 0/root and 17 | # Access is 600 or more restrictive: 18 | # 19 | # # stat /etc/group- 20 | # Access: (0600/-rw-------) Uid: (0/root) Gid: (0/root) 21 | # 22 | # Remediation 23 | # =========== 24 | # Run the following command to set permissions on /etc/group- : 25 | # 26 | # # chown root:root /etc/group- 27 | # # chmod 600 /etc/group- 28 | # 29 | parameters: 30 | linux: 31 | system: 32 | file: 33 | /etc/group-: 34 | user: 'root' 35 | group: 'root' 36 | mode: '0600' 37 | 38 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-5-3.yml: -------------------------------------------------------------------------------- 1 | # 1.5.3 Ensure address space layout randomization (ASLR) is enabled 2 | # 3 | # Description 4 | # =========== 5 | # 6 | # Address space layout randomization (ASLR) is an exploit mitigation technique which 7 | # randomly arranges the address space of key data areas of a process. 8 | # 9 | # Rationale 10 | # ========= 11 | # 12 | # Randomly placing virtual memory regions will make it difficult to write memory page 13 | # exploits as the memory placement will be consistently shifting. 14 | # 15 | # Audit 16 | # ===== 17 | # 18 | # Run the following command and verify output matches: 19 | # 20 | # # sysctl kernel.randomize_va_space 21 | # kernel.randomize_va_space = 2 22 | # 23 | # Remediation 24 | # =========== 25 | # 26 | # Set the following parameter in the /etc/sysctl.conf file: 27 | # 28 | # kernel.randomize_va_space = 2 29 | # 30 | # Run the following command to set the active kernel parameter: 31 | # 32 | # # sysctl -w kernel.randomize_va_space=2 33 | 34 | parameters: 35 | linux: 36 | system: 37 | kernel: 38 | sysctl: 39 | kernel.randomize_va_space: 2 40 | 41 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-5-1.yml: -------------------------------------------------------------------------------- 1 | # 3.5.2 Ensure DCCP is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The Datagram Congestion Control Protocol (DCCP) is a transport layer protocol 6 | # that supports streaming media and telephony. DCCP provides a way to gain 7 | # access to congestion control, without having to do it at the application 8 | # layer, but does not provide in-sequence delivery. 9 | # 10 | # Rationale 11 | # ========= 12 | # If the protocol is not required, it is recommended that the drivers not be 13 | # installed to reduce the potential attack surface. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following commands and verify the output is as indicated: 18 | # 19 | # # modprobe -n -v dccp 20 | # install /bin/true 21 | # # lsmod | grep dccp 22 | # 23 | # 24 | # Remediation 25 | # =========== 26 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 27 | # 28 | # install dccp /bin/true 29 | # 30 | parameters: 31 | linux: 32 | system: 33 | kernel: 34 | module: 35 | dccp: 36 | install: 37 | command: /bin/true 38 | 39 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-6-1-4.yml: -------------------------------------------------------------------------------- 1 | # CIS 6.1.4 Ensure permissions on /etc/group are configured 2 | # 3 | # Description 4 | # =========== 5 | # The /etc/group file contains a list of all the valid groups defined in the 6 | # system. The command below allows read/write access for root and read access 7 | # for everyone else. 8 | # 9 | # Rationale 10 | # ========= 11 | # The /etc/group file needs to be protected from unauthorized changes by 12 | # non-privileged users, but needs to be readable as this information is used 13 | # with many non-privileged programs. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following command and verify Uid and Gid are both 0/root and 18 | # Access is 644 : 19 | # 20 | # # stat /etc/group 21 | # Access: (0644/-rw-r--r--) Uid: (0/root) Gid: (0/root) 22 | # 23 | # Remediation 24 | # =========== 25 | # Run the following command to set permissions on /etc/group : 26 | # 27 | # # chown root:root /etc/group 28 | # # chmod 644 /etc/group 29 | # 30 | parameters: 31 | linux: 32 | system: 33 | file: 34 | /etc/group: 35 | user: 'root' 36 | group: 'root' 37 | mode: '0644' 38 | 39 | -------------------------------------------------------------------------------- /linux/system/certificate.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.ca_certificates is defined %} 5 | 6 | linux_system_ca_certificates: 7 | pkg.installed: 8 | - name: ca-certificates 9 | {%- if system.ca_certificates is mapping %} 10 | 11 | {%- for name, cert in system.ca_certificates.items() %} 12 | {{ system.ca_certs_dir }}/{{ name }}.crt: 13 | file.managed: 14 | - contents_pillar: "linux:system:ca_certificates:{{ name }}" 15 | - watch_in: 16 | - cmd: update_certificates 17 | - require: 18 | - pkg: linux_system_ca_certificates 19 | {%- endfor %} 20 | 21 | {%- else %} 22 | {#- salt-pki way #} 23 | 24 | {%- for certificate in system.ca_certificates %} 25 | {{ system.ca_certs_dir }}/{{ certificate }}.crt: 26 | file.managed: 27 | - source: salt://pki/{{ certificate }}/{{ certificate }}-chain.cert.pem 28 | - watch_in: 29 | - cmd: update_certificates 30 | - require: 31 | - pkg: linux_system_ca_certificates 32 | {%- endfor %} 33 | 34 | {%- endif %} 35 | 36 | update_certificates: 37 | cmd.wait: 38 | - name: {{ system.ca_certs_bin }} 39 | 40 | {%- endif %} 41 | 42 | {%- endif %} 43 | -------------------------------------------------------------------------------- /linux/system/systemd.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled and grains.get('init', None) == 'systemd' %} 3 | {%- if system.systemd.journal is defined %} 4 | include: 5 | - linux.system.journal 6 | {%- endif %} 7 | 8 | {%- if system.systemd.system is defined %} 9 | linux_systemd_system_config: 10 | file.managed: 11 | - name: /etc/systemd/system.conf.d/90-salt.conf 12 | - source: salt://linux/files/systemd.conf 13 | - template: jinja 14 | - makedirs: True 15 | - defaults: 16 | settings: {{ system.systemd.system }} 17 | - watch_in: 18 | - module: linux_systemd_reload 19 | {%- endif %} 20 | 21 | {%- if system.systemd.user is defined %} 22 | linux_systemd_user_config: 23 | file.managed: 24 | - name: /etc/systemd/user.conf.d/90-salt.conf 25 | - source: salt://linux/files/systemd.conf 26 | - template: jinja 27 | - makedirs: True 28 | - defaults: 29 | settings: {{ system.systemd.user }} 30 | - watch_in: 31 | - module: linux_systemd_reload 32 | {%- endif %} 33 | 34 | linux_systemd_reload: 35 | module.wait: 36 | - name: service.systemctl_reload 37 | 38 | {%- endif %} -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-6-1-2.yml: -------------------------------------------------------------------------------- 1 | # CIS 6.1.2 Ensure permissions on /etc/passwd are configured 2 | # 3 | # Description 4 | # =========== 5 | # The /etc/passwd file contains user account information that is used by 6 | # many system utilities and therefore must be readable for these utilities 7 | # to operate. 8 | # 9 | # Rationale 10 | # ========= 11 | # It is critical to ensure that the /etc/passwd file is protected from 12 | # unauthorized write access. Although it is protected by default, the file 13 | # permissions could be changed either inadvertently or through malicious actions. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following command and verify Uid and Gid are both 0/root and 18 | # Access is 644 : 19 | # 20 | # # stat /etc/passwd 21 | # Access: (0644/-rw-r--r--) Uid: (0/root) Gid: (0/root) 22 | # 23 | # Remediation 24 | # =========== 25 | # Run the following command to set permissions on /etc/passwd : 26 | # 27 | # # chown root:root /etc/passwd 28 | # # chmod 644 /etc/passwd 29 | # 30 | parameters: 31 | linux: 32 | system: 33 | file: 34 | /etc/passwd: 35 | user: 'root' 36 | group: 'root' 37 | mode: '0644' 38 | 39 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-1-1-7.yml: -------------------------------------------------------------------------------- 1 | # 1.1.1.7 Ensure mounting of udf filesystems is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The udf filesystem type is the universal disk format used to implement 6 | # ISO/IEC 13346 and ECMA-167 specifications. This is an open vendor filesystem 7 | # type for data storage on a broad range of media. This filesystem type is 8 | # necessary to support writing DVDs and newer optical disc formats. 9 | # 10 | # Rationale 11 | # ========= 12 | # Removing support for unneeded filesystem types reduces the local attack 13 | # surface of the server. If this filesystem type is not needed, disable it. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following commands and verify the output is as indicated: 18 | # 19 | # # modprobe -n -v udf 20 | # install /bin/true 21 | # # lsmod | grep udf 22 | # 23 | # 24 | # Remediation 25 | # =========== 26 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 27 | # 28 | # install udf /bin/true 29 | # 30 | parameters: 31 | linux: 32 | system: 33 | kernel: 34 | module: 35 | udf: 36 | install: 37 | command: /bin/true 38 | 39 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-2-3-3.yml: -------------------------------------------------------------------------------- 1 | # 2.3.3 Ensure talk client is not installed 2 | # 3 | # Description 4 | # =========== 5 | # The talk software makes it possible for users to send and receive messages 6 | # across systems through a terminal session. The talk client, which allows 7 | # initialization of talk sessions, is installed by default. 8 | # 9 | # Rationale 10 | # ========= 11 | # The software presents a security risk as it uses unencrypted protocols 12 | # for communication. 13 | # 14 | # Audit 15 | # ===== 16 | # Run the following command and verify talk is not installed: 17 | # 18 | # dpkg -s talk 19 | # 20 | # Remediation 21 | # =========== 22 | # Run the following command to uninstall talk : 23 | # 24 | # apt-get remove talk 25 | # 26 | # Impact 27 | # ====== 28 | # Many insecure service clients are used as troubleshooting tools and in 29 | # testing environments. Uninstalling them can inhibit capability to test 30 | # and troubleshoot. If they are required it is advisable to remove the clients 31 | # after use to prevent accidental or intentional misuse. 32 | # 33 | parameters: 34 | linux: 35 | system: 36 | package: 37 | talk: 38 | version: removed 39 | 40 | -------------------------------------------------------------------------------- /linux/meta/telegraf.yml: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import monitoring with context %} 2 | agent: 3 | input: 4 | cpu: 5 | percpu: false 6 | totalcpu: true 7 | disk: 8 | ignore_fs: 9 | - aufs 10 | - rootfs 11 | - sysfs 12 | - proc 13 | - devtmpfs 14 | - devpts 15 | - tmpfs 16 | - fusectl 17 | - cgroup 18 | - overlay 19 | diskio: 20 | kernel: 21 | net: 22 | mem: 23 | nstat: 24 | fieldpass: 25 | - packet_drop 26 | - time_squeeze 27 | processes: 28 | swap: 29 | system: 30 | procstat: 31 | process: 32 | sshd: 33 | exe: sshd 34 | cron: 35 | exe: cron 36 | linux_sysctl_fs: 37 | {%- if monitoring.bond_status.interfaces is defined and monitoring.bond_status.interfaces %} 38 | bond: 39 | template: linux/files/telegraf.conf 40 | {%- if monitoring.bond_status.interfaces is list %} 41 | bond_interfaces: {{ monitoring.bond_status.interfaces }} 42 | {%- endif %} 43 | {%- if monitoring.bond_status.host_proc is defined %} 44 | host_proc: {{ monitoring.bond_status.host_proc }} 45 | {%- endif %} 46 | {%- endif %} 47 | -------------------------------------------------------------------------------- /linux/system/env.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.env|length > 0 %} 5 | 6 | linux_system_environment_proxies: 7 | file.blockreplace: 8 | - name: /etc/environment 9 | - marker_start: '# START - SALT MANAGED VARIABLES, DO NOT EDIT' 10 | - marker_end: '# END - SALT MANAGED VARIABLES' 11 | - template: jinja 12 | - source: salt://linux/files/etc_environment 13 | - append_if_not_found: True 14 | - backup: '.bak' 15 | - show_changes: True 16 | - defaults: 17 | variables: {{ system.env | yaml }} 18 | no_proxy: {{ system.env.get('no_proxy', None) }} 19 | https_proxy: {{ system.env.get('https_proxy', None) }} 20 | http_proxy: {{ system.env.get('http_proxy', None) }} 21 | ftp_proxy: {{ system.env.get('ftp_proxy', None) }} 22 | 23 | {%- else %} 24 | 25 | linux_system_environment_proxies: 26 | file.blockreplace: 27 | - name: /etc/environment 28 | - marker_start: '# SALT MANAGED VARIABLES - DO NOT EDIT - START' 29 | - content: '# ' 30 | - marker_end: '# SALT MANAGED VARIABLES - END' 31 | - append_if_not_found: True 32 | - backup: '.bak' 33 | - show_changes: True 34 | 35 | {%- endif %} 36 | {%- endif %} 37 | -------------------------------------------------------------------------------- /linux/files/90autoupdates: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | 3 | {%- if system.autoupdates.enabled %} 4 | APT::Periodic::Enable "1"; 5 | APT::Periodic::Update-Package-Lists "1"; 6 | APT::Periodic::Unattended-Upgrade "1"; 7 | {%- else %} 8 | APT::Periodic::Unattended-Upgrade "0"; 9 | {%- endif %} 10 | 11 | {%- if system.autoupdates.mail is defined %} 12 | Unattended-Upgrade::Mail "{{ system.autoupdates.mail }}"; 13 | {%- endif %} 14 | 15 | {%- if system.autoupdates.mail_only_on_error is defined %} 16 | Unattended-Upgrade::MailOnlyOnError "{{ "true" if system.autoupdates.mail_only_on_error else "false"}}"; 17 | {%- endif %} 18 | 19 | {%- if system.autoupdates.remove_unused_dependencies is defined %} 20 | Unattended-Upgrade::Remove-Unused-Dependencies "{{ "true" if system.autoupdates.remove_unused_dependencies else "false"}}"; 21 | {%- endif %} 22 | 23 | {%- if system.autoupdates.automatic_reboot is defined %} 24 | Unattended-Upgrade::Automatic-Reboot "{{ "true" if system.autoupdates.automatic_reboot else "false"}}"; 25 | {%- endif %} 26 | 27 | {%- if system.autoupdates.automatic_reboot_time is defined %} 28 | Unattended-Upgrade::Automatic-Reboot-Time "{{ system.autoupdates.automatic_reboot_time }}"; 29 | {%- endif %} 30 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-6-1-7.yml: -------------------------------------------------------------------------------- 1 | # CIS 6.1.7 Ensure permissions on /etc/shadow- are configured 2 | # 3 | # Description 4 | # =========== 5 | # The /etc/shadow- file is used to store backup information about user 6 | # accounts that is critical to the security of those accounts, such as the 7 | # hashed password and other security information. 8 | # 9 | # Rationale 10 | # ========= 11 | # It is critical to ensure that the /etc/shadow- file is protected from 12 | # unauthorized access. Although it is protected by default, the file 13 | # permissions could be changed either inadvertently or through malicious actions. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following command and verify Uid and Gid are both 0/root and 18 | # Access is 600 or more restrictive: 19 | # 20 | # # stat /etc/shadow- 21 | # Access: (0600/-rw-------) Uid: (0/root) Gid: (0/root) 22 | # 23 | # Remediation 24 | # =========== 25 | # Run the following command to set permissions on /etc/shadow- : 26 | # 27 | # # chown root:root /etc/shadow- 28 | # # chmod 600 /etc/shadow- 29 | # 30 | parameters: 31 | linux: 32 | system: 33 | file: 34 | /etc/shadow-: 35 | user: 'root' 36 | group: 'root' 37 | mode: '0600' 38 | 39 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-6-1-9.yml: -------------------------------------------------------------------------------- 1 | # CIS 6.1.9 Ensure permissions on /etc/gshadow- are configured 2 | # 3 | # Description 4 | # =========== 5 | # The /etc/gshadow- file is used to store backup information about groups 6 | # that is critical to the security of those accounts, such as the hashed 7 | # password and other security information. 8 | # 9 | # Rationale 10 | # ========= 11 | # It is critical to ensure that the /etc/gshadow- file is protected from 12 | # unauthorized access. Although it is protected by default, the file 13 | # permissions could be changed either inadvertently or through malicious actions. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following command and verify Uid and Gid are both 0/root and 18 | # Access is 600 or more restrictive: 19 | # 20 | # # stat /etc/gshadow- 21 | # Access: (0600/-rw-------) Uid: (0/root) Gid: (0/root) 22 | # 23 | # Remediation 24 | # =========== 25 | # Run the following command to set permissions on /etc/gshadow- : 26 | # 27 | # # chown root:root /etc/gshadow- 28 | # # chmod 600 /etc/gshadow- 29 | # 30 | parameters: 31 | linux: 32 | system: 33 | file: 34 | /etc/gshadow-: 35 | user: 'root' 36 | group: 'root' 37 | mode: '0600' 38 | 39 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-2-6.yml: -------------------------------------------------------------------------------- 1 | # 3.2.6 Ensure bogus ICMP responses are ignored 2 | # 3 | # Description 4 | # =========== 5 | # Setting icmp_ignore_bogus_error_responses to 1 prevents the kernel from 6 | # logging bogus responses (RFC-1122 non-compliant) from broadcast reframes, 7 | # keeping file systems from filling up with useless log messages. 8 | # 9 | # Rationale 10 | # ========= 11 | # Some routers (and some attackers) will send responses that violate RFC-1122 12 | # and attempt to fill up a log file system with many useless error messages. 13 | # 14 | # Audit 15 | # ===== 16 | # 17 | # Run the following commands and verify output matches: 18 | # 19 | # # sysctl net.ipv4.icmp_ignore_bogus_error_responses 20 | # net.ipv4.icmp_ignore_bogus_error_responses = 1 21 | # 22 | # Remediation 23 | # =========== 24 | # 25 | # Set the following parameter in the /etc/sysctl.conf file: 26 | # 27 | # net.ipv4.icmp_ignore_bogus_error_responses = 1 28 | # 29 | # Run the following commands to set the active kernel parameters: 30 | # 31 | # # sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1 32 | # # sysctl -w net.ipv4.route.flush=1 33 | 34 | parameters: 35 | linux: 36 | system: 37 | kernel: 38 | sysctl: 39 | net.ipv4.icmp_ignore_bogus_error_responses: 1 40 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-5-2.yml: -------------------------------------------------------------------------------- 1 | # 3.5.2 Ensure SCTP is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The Stream Control Transmission Protocol (SCTP) is a transport layer 6 | # protocol used to support message oriented communication, with several 7 | # streams of messages in one connection. It serves a similar function as 8 | # TCP and UDP, incorporating features of both. It is message-oriented 9 | # like UDP, and ensures reliable in-sequence transport of messages with 10 | # congestion control like TCP. 11 | # 12 | # Rationale 13 | # ========= 14 | # If the protocol is not being used, it is recommended that kernel module 15 | # not be loaded, disabling the service to reduce the potential attack surface. 16 | # 17 | # Audit 18 | # ===== 19 | # Run the following commands and verify the output is as indicated: 20 | # 21 | # # modprobe -n -v sctp 22 | # install /bin/true 23 | # # lsmod | grep sctp 24 | # 25 | # 26 | # Remediation 27 | # =========== 28 | # 29 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 30 | # 31 | # install sctp /bin/true 32 | # 33 | parameters: 34 | linux: 35 | system: 36 | kernel: 37 | module: 38 | sctp: 39 | install: 40 | command: /bin/true 41 | 42 | -------------------------------------------------------------------------------- /linux/meta/salt.yml: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system,network with context -%} 2 | orchestrate: 3 | system: 4 | priority: 30 5 | network: 6 | priority: 40 7 | storage: 8 | priority: 50 9 | grain: 10 | sphinx: 11 | {%- set service_grains = {'sphinx': {'doc': {}}} %} 12 | {%- for service_name, service in pillar.items() %} 13 | {%- if service.get('_support', {}).get('sphinx', {}).get('enabled', False) %} 14 | {%- set grains_fragment_file = service_name+'/meta/sphinx.yml' %} 15 | {%- macro load_grains_file() %}{% include grains_fragment_file ignore missing %}{% endmacro %} 16 | {%- set grains = load_grains_file()|load_yaml %} 17 | {%- if grains %} 18 | {%- set grains_yaml = load_grains_file()|load_yaml %} 19 | {%- do service_grains.sphinx.doc.update({ service_name: grains_yaml.doc }) %} 20 | {%- endif %} 21 | {%- endif %} 22 | {%- endfor %} 23 | {{ service_grains|yaml(False)|indent(4) }} 24 | {%- set dns_records = [] %} 25 | {%- for host_name, host in network.host.items() %} 26 | {%- if host.get('grain', False) %} 27 | {%- do host.pop('grain') %} 28 | {%- do dns_records.append(host) %} 29 | {%- endif %} 30 | {%- endfor %} 31 | dns_records: 32 | dns_records: {{ dns_records|yaml }} 33 | -------------------------------------------------------------------------------- /linux/network/openvswitch.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | 3 | {%- if network.get('openvswitch', {}).get('enabled', False) %} 4 | 5 | openvswitch_pkgs: 6 | pkg.installed: 7 | - pkgs: {{ network.ovs_pkgs | json }} 8 | 9 | /etc/default/openvswitch-switch: 10 | file.managed: 11 | - source: salt://linux/files/openvswitch-switch.default 12 | - template: jinja 13 | - require: 14 | - pkg: openvswitch_pkgs 15 | 16 | /etc/systemd/system/openvswitch-switch.service: 17 | file.managed: 18 | - source: salt://linux/files/openvswitch-switch.systemd 19 | - template: jinja 20 | - require: 21 | - pkg: openvswitch_pkgs 22 | 23 | openvswitch_sytemctl_reload: 24 | module.run: 25 | {%- if 'module.run' in salt['config.get']('use_superseded', default=[]) %} 26 | - service.systemctl_reload: [] 27 | {%- else %} 28 | - name: service.systemctl_reload 29 | {%- endif %} 30 | - onchanges: 31 | - file: /etc/systemd/system/openvswitch-switch.service 32 | 33 | openvswitch_switch_service: 34 | service.running: 35 | - name: openvswitch-switch 36 | - enable: true 37 | {%- if grains.get('noservices') %} 38 | - onlyif: /bin/false 39 | {%- endif %} 40 | - watch: 41 | - file: /etc/default/openvswitch-switch 42 | 43 | {%- endif %} 44 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-2-3-4.yml: -------------------------------------------------------------------------------- 1 | # 2.3.4 Ensure telnet client is not installed 2 | # 3 | # Description 4 | # =========== 5 | # The telnet package contains the telnet client, which allows users to start 6 | # connections to other systems via the telnet protocol. 7 | # 8 | # Rationale 9 | # ========= 10 | # The telnet protocol is insecure and unencrypted. The use of an unencrypted 11 | # transmission medium could allow an unauthorized user to steal credentials. 12 | # The ssh package provides an encrypted session and stronger security and is 13 | # included in most Linux distributions. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following command and verify telnet is not installed: 18 | # 19 | # # dpkg -s telnet 20 | # 21 | # Remediation 22 | # =========== 23 | # Run the following command to uninstall telnet : 24 | # 25 | # # apt-get remove telnet 26 | # 27 | # Impact 28 | # ====== 29 | # Many insecure service clients are used as troubleshooting tools and in 30 | # testing environments. Uninstalling them can inhibit capability to test and 31 | # troubleshoot. If they are required it is advisable to remove the clients 32 | # after use to prevent accidental or intentional misuse. 33 | # 34 | parameters: 35 | linux: 36 | system: 37 | package: 38 | telnet: 39 | version: removed 40 | 41 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-6-1-3.yml: -------------------------------------------------------------------------------- 1 | # CIS 6.1.3 Ensure permissions on /etc/shadow are configured 2 | # 3 | # Description 4 | # =========== 5 | # The /etc/shadow file is used to store the information about user accounts 6 | # that is critical to the security of those accounts, such as the hashed 7 | # password and other security information. 8 | # 9 | # Rationale 10 | # ========= 11 | # If attackers can gain read access to the /etc/shadow file, they can easily 12 | # run a password cracking program against the hashed password to break it. 13 | # Other security information that is stored in the /etc/shadow file (such 14 | # as expiration) could also be useful to subvert the user accounts. 15 | # 16 | # Audit 17 | # ===== 18 | # Run the following command and verify Uid is 0/root , Gid is /shadow , 19 | # and Access is 640 or more restrictive: 20 | # 21 | # # stat /etc/shadow 22 | # Access: (0640/-rw-r-----) Uid: (0/root) Gid: (42/shadow) 23 | # 24 | # Remediation 25 | # =========== 26 | # Run the one following commands to set permissions on /etc/shadow : 27 | # 28 | # # chown root:shadow /etc/shadow 29 | # # chmod o-rwx,g-wx /etc/shadow 30 | # 31 | parameters: 32 | linux: 33 | system: 34 | file: 35 | /etc/shadow: 36 | user: 'root' 37 | group: 'shadow' 38 | mode: '0640' 39 | 40 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-6-1-5.yml: -------------------------------------------------------------------------------- 1 | # CIS 6.1.5 Ensure permissions on /etc/gshadow are configured 2 | # 3 | # Description 4 | # =========== 5 | # The /etc/gshadow file is used to store the information about groups that 6 | # is critical to the security of those accounts, such as the hashed password 7 | # and other security information. 8 | # 9 | # Rationale 10 | # ========= 11 | # If attackers can gain read access to the /etc/gshadow file, they can easily 12 | # run a password cracking program against the hashed password to break it. 13 | # Other security information that is stored in the /etc/gshadow file (such as 14 | # group administrators) could also be useful to subvert the group. 15 | # 16 | # Audit 17 | # ===== 18 | # Run the following command and verify verify Uid is 0/root , 19 | # Gid is /shadow , and Access is 640 or more restrictive: 20 | # 21 | # # stat /etc/gshadow 22 | # Access: (0640/-rw-r-----) Uid: (0/root) Gid: (42/shadow) 23 | # 24 | # Remediation 25 | # =========== 26 | # Run the following commands to set permissions on /etc/gshadow : 27 | # 28 | # # chown root:shadow /etc/gshadow 29 | # # chmod o-rwx,g-rw /etc/gshadow 30 | # 31 | parameters: 32 | linux: 33 | system: 34 | file: 35 | /etc/gshadow: 36 | user: 'root' 37 | group: 'shadow' 38 | mode: '0640' 39 | 40 | -------------------------------------------------------------------------------- /linux/network/systemd.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | {%- if network.enabled and grains.get('init', None) == 'systemd' %} 3 | 4 | {%- if network.systemd is mapping %} 5 | {%- for config_type, configs in network.systemd.items() %} 6 | 7 | {%- if config_type == 'link' %} 8 | /etc/udev/rules.d/80-net-setup-link.rules: 9 | file.managed: 10 | - makedirs: True 11 | - content: "" 12 | {%- endif %} 13 | 14 | {%- for config_name, config in configs.items() %} 15 | linux_network_systemd_networkd_{{ config_type }}_config_{{ config_name }}: 16 | file.managed: 17 | - name: /etc/systemd/network/{{ config_name }}.{{ config_type }} 18 | - source: salt://linux/files/systemd-network.conf 19 | - template: jinja 20 | - makedirs: True 21 | - defaults: 22 | settings: {{ config }} 23 | - watch_in: 24 | - module: linux_network_systemd_reload 25 | - module: linux_network_systemd_networkd 26 | {%- endfor %} 27 | {%- endfor %} 28 | 29 | linux_network_systemd_reload: 30 | module.wait: 31 | - name: service.systemctl_reload 32 | 33 | linux_network_systemd_networkd: 34 | service.running: 35 | - name: systemd-networkd 36 | - init_delay: 10 37 | - enable: True 38 | - reload: True 39 | - watch: 40 | - module: linux_network_systemd_reload 41 | 42 | {%- endif %} 43 | {%- endif %} 44 | -------------------------------------------------------------------------------- /linux/system/group.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- macro set_p(paramname, dictvar) -%} 5 | {%- if paramname in dictvar -%} 6 | - {{ paramname }}: {{ dictvar[paramname] }} 7 | {%- endif -%} 8 | {%- endmacro -%} 9 | 10 | {%- for group_name, group in system.group.items() %} 11 | 12 | {%- if group.enabled %} 13 | 14 | system_group_{{ group_name }}: 15 | group.present: 16 | - name: {{ group.get('name', group_name) }} 17 | {%- if group.system is defined and group.system %} 18 | - system: True 19 | {%- endif %} 20 | {%- if group.gid is defined and group.gid %} 21 | - gid: {{ group.gid }} 22 | {%- endif %} 23 | {%- if group.members is defined %} 24 | - members: {{ group.members|json }} 25 | {%- else %} 26 | {%- set requires = [] %} 27 | {%- for user in group.get('addusers', []) %} 28 | {%- if user in system.get('user', {}).keys() %} 29 | {%- do requires.append({'user': 'system_user_'+user}) %} 30 | {%- endif %} 31 | {%- endfor %} 32 | - require: {{ requires|yaml }} 33 | {{ set_p('addusers', group)|indent(2, True) }} 34 | {{ set_p('delusers', group)|indent(2, True) }} 35 | {% endif %} 36 | {%- else %} 37 | 38 | system_group_{{ group_name }}: 39 | group.absent: 40 | - name: {{ group.name }} 41 | 42 | {%- endif %} 43 | 44 | {%- endfor %} 45 | 46 | {%- endif %} 47 | 48 | -------------------------------------------------------------------------------- /linux/system/motd.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled and system.motd|length > 0 %} 3 | 4 | /etc/update-motd.d: 5 | file.directory: 6 | - clean: true 7 | 8 | {%- if system.motd is string %} 9 | 10 | {#- Set static motd only #} 11 | /etc/motd: 12 | file.managed: 13 | - contents_pillar: linux:system:motd 14 | 15 | {%- else %} 16 | 17 | {%- if grains.oscodename == "jessie" %} 18 | motd_fix_pam_sshd: 19 | file.replace: 20 | - name: /etc/pam.d/sshd 21 | - pattern: "/run/motd.dynamic" 22 | - repl: "/run/motd" 23 | {%- endif %} 24 | 25 | /etc/motd: 26 | file.absent 27 | 28 | exist_/etc/update-motd.d: 29 | file.directory: 30 | - name: /etc/update-motd.d 31 | 32 | {%- for motd in system.motd %} 33 | {%- set motd_index = loop.index %} 34 | 35 | {%- for name, value in motd.items() %} 36 | motd_{{ motd_index }}_{{ name }}: 37 | file.managed: 38 | - name: /etc/update-motd.d/5{{ motd_index }}-{{ name }} 39 | - source: salt://linux/files/motd.sh 40 | - template: jinja 41 | - mode: 755 42 | - require_in: 43 | - file: /etc/update-motd.d 44 | - require: 45 | - file: exist_/etc/update-motd.d 46 | - defaults: 47 | index: {{ motd_index }} 48 | motd_name: {{ name }} 49 | {%- endfor %} 50 | 51 | {%- endfor %} 52 | 53 | {%- endif %} 54 | 55 | {%- endif %} 56 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-2-4.yml: -------------------------------------------------------------------------------- 1 | # 3.2.4 Ensure suspicious packets are logged 2 | # 3 | # Description 4 | # =========== 5 | # When enabled, this feature logs packets with un-routable source 6 | # addresses to the kernel log. 7 | # 8 | # Rationale 9 | # ========= 10 | # Enabling this feature and logging these packets allows an administrator 11 | # to investigate the possibility that an attacker is sending spoofed 12 | # packets to their system. 13 | # 14 | # Audit 15 | # ===== 16 | # 17 | # Run the following commands and verify output matches: 18 | # 19 | # # sysctl net.ipv4.conf.all.log_martians 20 | # net.ipv4.conf.all.log_martians = 1 21 | # # sysctl net.ipv4.conf.default.log_martians 22 | # net.ipv4.conf.default.log_martians = 1 23 | # 24 | # Remediation 25 | # =========== 26 | # 27 | # Set the following parameters in the /etc/sysctl.conf file: 28 | # 29 | # net.ipv4.conf.all.log_martians = 1 30 | # net.ipv4.conf.default.log_martians = 1 31 | # 32 | # Run the following commands to set the active kernel parameters: 33 | # 34 | # # sysctl -w net.ipv4.conf.all.log_martians=1 35 | # # sysctl -w net.ipv4.conf.default.log_martians=1 36 | # # sysctl -w net.ipv4.route.flush=1 37 | 38 | parameters: 39 | linux: 40 | system: 41 | kernel: 42 | sysctl: 43 | net.ipv4.conf.all.log_martians: 1 44 | net.ipv4.conf.default.log_martians: 1 45 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-1-1-6.yml: -------------------------------------------------------------------------------- 1 | # 1.1.1.6 Ensure mounting of squashfs filesystems is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The squashfs filesystem type is a compressed read-only Linux filesystem 6 | # embedded in small footprint systems (similar to cramfs). A squashfs image 7 | # can be used without having to first decompress the image. 8 | # 9 | # Rationale 10 | # ========= 11 | # Removing support for unneeded filesystem types reduces the local attack 12 | # surface of the server. If this filesystem type is not needed, disable it. 13 | # 14 | # Audit 15 | # ===== 16 | # Run the following commands and verify the output is as indicated: 17 | # 18 | # # modprobe -n -v squashfs 19 | # install /bin/true 20 | # # lsmod | grep squashfs 21 | # 22 | # 23 | # Remediation 24 | # =========== 25 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 26 | # 27 | # install squashfs /bin/true 28 | # 29 | # NOTE 30 | # ==== 31 | # In Ubuntu 16.04 squashfs is built into kernel, and 'install' command 32 | # from modprobe.d dir has no effect. However, this is still checked by 33 | # CIS-CAT in Ubuntu 16.04 benchmark v.1.0.0. This was removed in v.1.1.0. 34 | # 35 | parameters: 36 | linux: 37 | system: 38 | kernel: 39 | module: 40 | squashfs: 41 | install: 42 | command: /bin/true 43 | 44 | -------------------------------------------------------------------------------- /linux/storage/loopback.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import storage with context %} 2 | 3 | {%- if storage.get('enabled', False) %} 4 | 5 | {%- for device, loopback in storage.loopback|dictsort %} 6 | 7 | {%- if loopback.get('enabled', True) %} 8 | 9 | {{ loopback.file }}_directory: 10 | file.directory: 11 | - name: {{ salt['file.dirname'](loopback.file) }} 12 | - makedirs: true 13 | - require_in: 14 | - file: {{ loopback.file }} 15 | 16 | {{ loopback.file }}: 17 | cmd.run: 18 | - name: "truncate --size {{ loopback.size|default('1G') }} {{ loopback.file }}" 19 | - creates: {{ loopback.file }} 20 | 21 | loopback_{{ device }}_init_script: 22 | file.managed: 23 | {%- if grains.get('init', None) == 'upstart' %} 24 | - name: /etc/init/setup-loopback-{{ device }}.conf 25 | - source: salt://linux/files/setup-loopback-device.upstart 26 | {%- else %} 27 | - name: /etc/systemd/system/setup-loopback-{{ device }}.service 28 | - source: salt://linux/files/setup-loopback-device.systemd 29 | {%- endif %} 30 | - template: jinja 31 | - defaults: 32 | file: {{ loopback.file }} 33 | device_name: "/dev/loop{{ loop.index0 }}" 34 | 35 | setup-loopback-{{ device }}: 36 | service.running: 37 | - enable: true 38 | - require: 39 | - cmd: {{ loopback.file }} 40 | - file: loopback_{{ device }}_init_script 41 | {%- endif %} 42 | 43 | {%- endfor %} 44 | 45 | {%- endif %} 46 | -------------------------------------------------------------------------------- /tests/pillar/system_extra.sls: -------------------------------------------------------------------------------- 1 | 2 | linux: 3 | network: 4 | enabled: true 5 | hostname: linux 6 | fqdn: linux.ci.local 7 | system: 8 | auth: 9 | enabled: true 10 | mkhomedir: 11 | enabled: true 12 | umask: 0027 13 | ldap: 14 | enabled: true 15 | binddn: cn=bind,ou=service_users,dc=example,dc=com 16 | bindpw: secret 17 | uri: ldap://127.0.0.1 18 | base: ou=users,dc=example,dc=com 19 | ldap_version: 3 20 | pagesize: 65536 21 | referrals: off 22 | filter: 23 | passwd: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*)) 24 | shadow: (&(&(objectClass=person)(uidNumber=*))(unixHomeDirectory=*)) 25 | group: (&(objectClass=group)(gidNumber=*)) 26 | enabled: true 27 | cluster: default 28 | name: linux 29 | timezone: Europe/Prague 30 | console: 31 | tty0: 32 | autologin: root 33 | ttyS0: 34 | autologin: root 35 | rate: 115200 36 | term: xterm 37 | kernel: 38 | sriov: True 39 | isolcpu: 1,2,3,4 40 | hugepages: 41 | large: 42 | default: true 43 | size: 1G 44 | count: 210 45 | mount_point: /mnt/hugepages_1GB 46 | policyrcd: 47 | - package: cassandra 48 | action: exit 101 49 | - package: '*' 50 | action: switch 51 | -------------------------------------------------------------------------------- /linux/system/cpu.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.cpu.governor is defined %} 3 | 4 | include: 5 | - linux.system.sysfs 6 | 7 | ondemand_service_disable: 8 | service.dead: 9 | - name: ondemand 10 | - enable: false 11 | 12 | {%- if grains.get('virtual', None) in ['physical', None] %} 13 | {#- Governor cannot be set in VMs, etc. #} 14 | 15 | /etc/sysfs.d/governor.conf: 16 | file.managed: 17 | - source: salt://linux/files/governor.conf.jinja 18 | - template: jinja 19 | - user: root 20 | - group: root 21 | - mode: 0644 22 | - defaults: 23 | governor: {{ system.cpu.governor }} 24 | - require: 25 | - file: /etc/sysfs.d 26 | 27 | {% for cpu_core in range(salt['grains.get']('num_cpus', 1)) %} 28 | 29 | {% set core_key = 'devices/system/cpu/cpu' + cpu_core|string + '/cpufreq/scaling_governor' %} 30 | {% if salt['file.file_exists']('/sys/'+ core_key) %} 31 | governor_write_sysfs_cpu_core_{{ cpu_core }}: 32 | module.run: 33 | {%- if 'module.run' in salt['config.get']('use_superseded', default=[]) %} 34 | - sysfs.write: 35 | - key: {{ core_key }} 36 | - value: {{ system.cpu.governor }} 37 | {%- else %} 38 | - name: sysfs.write 39 | - key: {{ core_key }} 40 | - value: {{ system.cpu.governor }} 41 | {%- endif %} 42 | {% endif %} 43 | 44 | {%- endfor %} 45 | 46 | {%- endif %} 47 | 48 | {%- endif %} 49 | -------------------------------------------------------------------------------- /linux/files/hosts: -------------------------------------------------------------------------------- 1 | {#- 2 | vim: syntax=jinja 3 | -#} 4 | {%- from "linux/map.jinja" import network with context -%} 5 | # hosts(1) file managed by salt-minion(1) 6 | # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN 7 | 8 | {%- set hosts = { 9 | '127.0.0.1': [ 10 | 'localhost' 11 | ], 12 | '127.0.1.1': [ 13 | network.fqdn|default(grains.fqdn), 14 | network.hostname|default(grains.nodename) 15 | ], 16 | '::1': [ 17 | 'ip6-localhost', 18 | 'ip6-loopback' 19 | ], 20 | 'fe00::0': [ 21 | 'ip6-localnet', 22 | 'ip6-mcastprefix' 23 | ], 24 | 'ff02::1': [ 25 | 'ip6-allnodes' 26 | ], 27 | 'ff02::2': [ 28 | 'ip6-allrouters' 29 | ], 30 | 'ff02::3': [ 31 | 'ip6-allhosts' 32 | ], 33 | } -%} 34 | {%- for name, host in host_dict.items() -%} 35 | {%- for hname in host.names -%} 36 | {%- if hname in hosts.get('127.0.1.1', []) -%} 37 | {%- do hosts.pop('127.0.1.1') -%} 38 | {%- endif %} 39 | {%- endfor %} 40 | {%- do hosts.update({host.address: host.names}) -%} 41 | {%- endfor %} 42 | {% for address, entries in hosts|dictsort %} 43 | {%- if 'linux_hosts.fqdn_sort_filter' in salt.keys() %} 44 | {%- set sorted_entries = salt['linux_hosts.fqdn_sort_filter'](entries) -%} 45 | {%- else %} 46 | {%- set sorted_entries = entries -%} 47 | {%- endif %} 48 | {{ address }} {{ sorted_entries|join(' ') }} 49 | {%- endfor %} 50 | -------------------------------------------------------------------------------- /linux/system/netconsole.sls: -------------------------------------------------------------------------------- 1 | {% from "linux/map.jinja" import system with context %} 2 | {% if system.enabled and system.netconsole is mapping and system.netconsole.enabled %} 3 | 4 | /etc/dhcp/dhclient-exit-hooks.d/netconsole: 5 | file.managed: 6 | - source: salt://linux/files/netconsole 7 | - makedirs: True 8 | 9 | /etc/network/if-up.d/netconsole: 10 | file.managed: 11 | - source: salt://linux/files/netconsole 12 | - mode: 755 13 | - makedirs: True 14 | 15 | /etc/network/if-down.d/netconsole: 16 | file.managed: 17 | - source: salt://linux/files/netconsole 18 | - mode: 755 19 | - makedirs: True 20 | 21 | /etc/default/netconsole.conf: 22 | file.managed: 23 | - source: salt://linux/files/netconsole.conf 24 | - template: jinja 25 | 26 | {% if system.netconsole is mapping and system.netconsole.target is mapping %} 27 | {% for target, data in system.netconsole.target.items() %} 28 | {% if data is mapping and data.interface is defined %} 29 | /etc/network/if-up.d/netconsole {{ target }} {{ data.interface }}: 30 | cmd.run: 31 | - name: /etc/network/if-up.d/netconsole 32 | - env: 33 | - IFACE: {{ data.interface }} 34 | - METHOD: static 35 | - ADDRFAM: inet 36 | - MODE: start 37 | - onchanges: 38 | - file: /etc/default/netconsole.conf 39 | - require: 40 | - file: /etc/network/if-up.d/netconsole 41 | {% endif %} 42 | {% endfor %} 43 | {% endif %} 44 | 45 | {% endif %} 46 | -------------------------------------------------------------------------------- /tests/pillar/network_openvswitch.sls: -------------------------------------------------------------------------------- 1 | linux: 2 | system: 3 | enabled: true 4 | domain: local 5 | name: linux 6 | network: 7 | enabled: true 8 | hostname: test01 9 | fqdn: test01.local 10 | network_manager: false 11 | bridge: openvswitch 12 | interface: 13 | br-prv: 14 | enabled: true 15 | type: ovs_bridge 16 | mtu: 65000 17 | br-ens0: 18 | enabled: true 19 | type: ovs_bridge 20 | proto: manual 21 | mtu: 9000 22 | use_interfaces: 23 | - ens0 24 | patch-br-ens0-br-prv: 25 | enabled: true 26 | name: ens0-prv 27 | ovs_type: ovs_port 28 | type: ovs_port 29 | bridge: br-ens0 30 | port_type: patch 31 | peer: prv-ens0 32 | tag: 107 33 | mtu: 65000 34 | patch-br-prv-br-ens0: 35 | enabled: true 36 | name: prv-ens0 37 | bridge: br-prv 38 | ovs_type: ovs_port 39 | type: ovs_port 40 | port_type: patch 41 | peer: ens0-prv 42 | tag: 107 43 | mtu: 65000 44 | ens0: 45 | enabled: true 46 | proto: manual 47 | ovs_port_type: OVSPort 48 | type: ovs_port 49 | ovs_bridge: br-ens0 50 | bridge: br-ens0 51 | bond1: 52 | enabled: true 53 | type: ovs_bond 54 | mode: balance-slb 55 | bridge: br-ex 56 | slaves: eno3 eno4 57 | 58 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-1-2.yml: -------------------------------------------------------------------------------- 1 | # 3.1.2 Ensure packet redirect sending is disabled 2 | # 3 | # Description 4 | # =========== 5 | # ICMP Redirects are used to send routing information to other hosts. As a host 6 | # itself does not act as a router (in a host only configuration), there is 7 | # no need to send redirects. 8 | # 9 | # Rationale 10 | # ========= 11 | # An attacker could use a compromised host to send invalid ICMP redirects to 12 | # other router devices in an attempt to corrupt routing and have users access 13 | # a system set up by the attacker as opposed to a valid system. 14 | # 15 | # Audit 16 | # ===== 17 | # 18 | # Run the following commands and verify output matches: 19 | # 20 | # # sysctl net.ipv4.conf.all.send_redirects 21 | # net.ipv4.conf.all.send_redirects = 0 22 | # # sysctl net.ipv4.conf.default.send_redirects 23 | # net.ipv4.conf.default.send_redirects = 0 24 | # 25 | # Remediation 26 | # =========== 27 | # 28 | # Set the following parameters in the /etc/sysctl.conf file: 29 | # 30 | # net.ipv4.conf.all.send_redirects = 0 31 | # net.ipv4.conf.default.send_redirects = 0 32 | # 33 | # Run the following commands to set the active kernel parameters: 34 | # 35 | # # sysctl -w net.ipv4.conf.all.send_redirects=0 36 | # # sysctl -w net.ipv4.conf.default.send_red 37 | 38 | parameters: 39 | linux: 40 | system: 41 | kernel: 42 | sysctl: 43 | net.ipv4.conf.all.send_redirects: 0 44 | net.ipv4.conf.default.send_redirects: 0 45 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-2-3-1.yml: -------------------------------------------------------------------------------- 1 | # 2.3.1 Ensure NIS Client is not installed 2 | # 3 | # Description 4 | # =========== 5 | # The Network Information Service (NIS), formerly known as Yellow Pages, 6 | # is a client-server directory service protocol used to distribute system 7 | # configuration files. The NIS client ( ypbind ) was used to bind a machine 8 | # to an NIS server and receive the distributed configuration files. 9 | # 10 | # Rationale 11 | # ========= 12 | # The NIS service is inherently an insecure system that has been vulnerable 13 | # to DOS attacks, buffer overflows and has poor authentication for querying 14 | # NIS maps. NIS generally has been replaced by such protocols as Lightweight 15 | # Directory Access Protocol (LDAP). It is recommended that the service be 16 | # removed. 17 | # 18 | # Audit 19 | # ===== 20 | # Run the following command and verify nis is not installed: 21 | # 22 | # dpkg -s nis 23 | # 24 | # Remediation 25 | # =========== 26 | # Run the following command to uninstall nis: 27 | # 28 | # apt-get remove nis 29 | # 30 | # Impact 31 | # ====== 32 | # Many insecure service clients are used as troubleshooting tools and in 33 | # testing environments. Uninstalling them can inhibit capability to test 34 | # and troubleshoot. If they are required it is advisable to remove the clients 35 | # after use to prevent accidental or intentional misuse. 36 | # 37 | parameters: 38 | linux: 39 | system: 40 | package: 41 | nis: 42 | version: removed 43 | 44 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-2-3.yml: -------------------------------------------------------------------------------- 1 | # 3.2.3 Ensure secure ICMP redirects are not accepted 2 | # 3 | # Description 4 | # =========== 5 | # Secure ICMP redirects are the same as ICMP redirects, except they come from 6 | # gateways listed on the default gateway list. It is assumed that these 7 | # gateways are known to your system, and that they are likely to be secure. 8 | # 9 | # Rationale 10 | # ========= 11 | # It is still possible for even known gateways to be compromised. Setting 12 | # net.ipv4.conf.all.secure_redirects to 0 protects the system from routing 13 | # table updates by possibly compromised known gateways. 14 | # 15 | # Audit 16 | # ===== 17 | # 18 | # Run the following commands and verify output matches: 19 | # 20 | # # sysctl net.ipv4.conf.all.secure_redirects 21 | # net.ipv4.conf.all.secure_redirects = 0 22 | # # sysctl net.ipv4.conf.default.secure_redirects 23 | # net.ipv4.conf.default.secure_redirects = 0 24 | # 25 | # Remediation 26 | # =========== 27 | # 28 | # Set the following parameters in the /etc/sysctl.conf file: 29 | # 30 | # net.ipv4.conf.all.secure_redirects = 0 31 | # net.ipv4.conf.default.secure_redirects = 0 32 | # 33 | # Run the following commands to set the active kernel parameters: 34 | # 35 | # # sysctl -w net.ipv4.conf.all.secure_redirects=0 36 | # # sysctl -w net.ipv4.conf.default.secure_redirects=0 37 | # # sysctl -w net.ipv4.route.flush=1 38 | 39 | parameters: 40 | linux: 41 | system: 42 | kernel: 43 | sysctl: 44 | net.ipv4.conf.all.secure_redirects: 0 45 | net.ipv4.conf.default.secure_redirects: 0 46 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-1-1-8.yml: -------------------------------------------------------------------------------- 1 | # 1.1.1.8 Ensure mounting of FAT filesystems is disabled 2 | # 3 | # Description 4 | # =========== 5 | # The FAT filesystem format is primarily used on older windows systems and 6 | # portable USB drives or flash modules. It comes in three types FAT12, FAT16, 7 | # and FAT32 all of which are supported by the vfat kernel module. 8 | # 9 | # Rationale 10 | # ========= 11 | # Removing support for unneeded filesystem types reduces the local attack 12 | # surface of the server. If this filesystem type is not needed, disable it. 13 | # 14 | # Audit 15 | # ===== 16 | # Run the following commands and verify the output is as indicated: 17 | # 18 | # # modprobe -n -v vfat 19 | # install /bin/true 20 | # # lsmod | grep vfat 21 | # 22 | # 23 | # Remediation 24 | # =========== 25 | # 26 | # Edit or create the file /etc/modprobe.d/CIS.conf and add the following line: 27 | # 28 | # install vfat /bin/true 29 | # 30 | # Impact 31 | # ====== 32 | # FAT filesystems are often used on portable USB sticks and other flash 33 | # media are commonly used to transfer files between workstations, removing 34 | # VFAT support may prevent the ability to transfer files in this way. 35 | # 36 | # NOTE 37 | # ==== 38 | # In Ubuntu 16.04 vfat is built into kernel, and 'install' command 39 | # from modprobe.d dir has no effect. However, this is still checked by 40 | # CIS-CAT in Ubuntu 16.04 benchmark v.1.0.0. This was removed in v.1.1.0. 41 | # 42 | parameters: 43 | linux: 44 | system: 45 | kernel: 46 | module: 47 | vfat: 48 | install: 49 | command: /bin/true 50 | 51 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-2-5.yml: -------------------------------------------------------------------------------- 1 | # 3.2.5 Ensure broadcast ICMP requests are ignored 2 | # 3 | # Description 4 | # =========== 5 | # Setting net.ipv4.icmp_echo_ignore_broadcasts to 1 will cause the 6 | # system to ignore all ICMP echo and timestamp requests to broadcast 7 | # and multicast addresses. 8 | # 9 | # Rationale 10 | # ========= 11 | # Accepting ICMP echo and timestamp requests with broadcast or multicast 12 | # destinations for your network could be used to trick your host into starting 13 | # (or participating) in a Smurf attack. A Smurf attack relies on an attacker 14 | # sending large amounts of ICMP broadcast messages with a spoofed source 15 | # address. All hosts receiving this message and responding would send 16 | # echo-reply messages back to the spoofed address, which is probably not 17 | # routable. If many hosts respond to the packets, the amount of traffic on 18 | # the network could be significantly multiplied. 19 | # 20 | # Audit 21 | # ===== 22 | # 23 | # Run the following commands and verify output matches: 24 | # 25 | # # sysctl net.ipv4.icmp_echo_ignore_broadcasts 26 | # net.ipv4.icmp_echo_ignore_broadcasts = 1 27 | # 28 | # Remediation 29 | # =========== 30 | # 31 | # Set the following parameter in the /etc/sysctl.conf file: 32 | # 33 | # net.ipv4.icmp_echo_ignore_broadcasts = 1 34 | # 35 | # Run the following commands to set the active kernel parameters: 36 | # 37 | # # sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1 38 | # # sysctl -w net.ipv4.route.flush=1 39 | 40 | parameters: 41 | linux: 42 | system: 43 | kernel: 44 | sysctl: 45 | net.ipv4.icmp_echo_ignore_broadcasts: 1 46 | -------------------------------------------------------------------------------- /linux/files/login.defs.jinja: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import login_defs with context -%} 2 | # This file is managed by Salt, do not edit 3 | {%- set allowed_options = [ 4 | 'CHFN_RESTRICT', 5 | 'CONSOLE_GROUPS', 6 | 'CREATE_HOME', 7 | 'DEFAULT_HOME', 8 | 'ENCRYPT_METHOD', 9 | 'ENV_HZ', 10 | 'ENV_PATH', 11 | 'ENV_SUPATH', 12 | 'ERASECHAR', 13 | 'FAIL_DELAY', 14 | 'FAKE_SHELL', 15 | 'GID_MAX', 16 | 'GID_MIN', 17 | 'HUSHLOGIN_FILE', 18 | 'KILLCHAR', 19 | 'LOG_OK_LOGINS', 20 | 'LOG_UNKFAIL_ENAB', 21 | 'LOGIN_RETRIES', 22 | 'LOGIN_TIMEOUT', 23 | 'MAIL_DIR', 24 | 'MAIL_FILE', 25 | 'MAX_MEMBERS_PER_GROUP', 26 | 'MD5_CRYPT_ENAB', 27 | 'PASS_MAX_DAYS', 28 | 'PASS_MIN_DAYS', 29 | 'PASS_WARN_AGE', 30 | 'SHA_CRYPT_MIN_ROUNDS', 31 | 'SHA_CRYPT_MAX_ROUNDS', 32 | 'SULOG_FILE', 33 | 'SU_NAME', 34 | 'SUB_GID_MIN', 35 | 'SUB_GID_MAX', 36 | 'SUB_GID_COUNT', 37 | 'SUB_UID_MIN', 38 | 'SUB_UID_MAX', 39 | 'SUB_UID_COUNT', 40 | 'SYS_GID_MAX', 41 | 'SYS_GID_MIN', 42 | 'SYS_UID_MAX', 43 | 'SYS_UID_MIN', 44 | 'SYSLOG_SG_ENAB', 45 | 'SYSLOG_SU_ENAB', 46 | 'TTYGROUP', 47 | 'TTYPERM', 48 | 'TTYTYPE_FILE', 49 | 'UID_MAX', 50 | 'UID_MIN', 51 | 'UMASK', 52 | 'USERDEL_CMD', 53 | 'USERGROUPS_ENAB' 54 | ] %} 55 | {%- for opt_name in allowed_options %} 56 | {%- if opt_name in login_defs %} 57 | {%- set opt_params = login_defs.get(opt_name) %} 58 | {%- if opt_params.get('enabled', true) %} 59 | {{ opt_name.ljust(20) }} {{ opt_params.value }} 60 | {%- endif %} 61 | {%- endif %} 62 | {%- endfor %} 63 | -------------------------------------------------------------------------------- /linux/system/hugepages.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | include: 4 | - linux.system.grub 5 | 6 | {%- if "pse" in grains.cpu_flags or "pdpe1gb" in grains.cpu_flags or "aarch64" in grains.cpuarch %} 7 | 8 | /etc/default/grub.d/90-hugepages.cfg: 9 | file.managed: 10 | - source: salt://linux/files/grub_hugepages 11 | - template: jinja 12 | - require: 13 | - file: grub_d_directory 14 | {%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %} 15 | - watch_in: 16 | - cmd: grub_update 17 | 18 | {%- endif %} 19 | 20 | {%- for hugepages_type, hugepages in system.kernel.hugepages.items() %} 21 | 22 | hugepages_mount_{{ hugepages_type }}: 23 | mount.mounted: 24 | - name: {{ hugepages.mount_point }} 25 | - device: Hugetlbfs-kvm-{{ hugepages.size|lower }} 26 | - fstype: hugetlbfs 27 | - mkmnt: true 28 | - opts: mode=775,pagesize={{ hugepages.size }} 29 | - mount: {{ hugepages.mount|default('true') }} 30 | 31 | # Make hugepages available right away with a temporary systctl write 32 | # This will be handled via krn args after reboot, so don't use `sysctl.present` 33 | {%- if hugepages.get('default', False) %} 34 | hugepages_sysctl_vm_nr_hugepages: 35 | cmd.run: 36 | - name: "sysctl vm.nr_hugepages={{ hugepages.count }}" 37 | - unless: "sysctl vm.nr_hugepages | grep -qE '{{ hugepages.count }}'" 38 | {%- endif %} 39 | 40 | {%- endfor %} 41 | 42 | {%- endif %} 43 | 44 | # systemd always creates default mount point at /dev/hugepages 45 | # we have to disable it, as we configure our own mount point for DPDK. 46 | mask_dev_hugepages: 47 | cmd.run: 48 | - name: "systemctl mask dev-hugepages.mount" 49 | -------------------------------------------------------------------------------- /linux/system/shell.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | {%- if system.shell is defined %} 4 | 5 | {%- if system.shell.umask is defined %} 6 | etc_bash_bashrc_umask: 7 | file.blockreplace: 8 | - name: /etc/bash.bashrc 9 | - marker_start: "# BEGIN CIS 5.4.4 default user umask" 10 | - marker_end: "# END CIS 5.4.4 default user umask" 11 | - content: "umask {{ system.shell.umask }}" 12 | - append_if_not_found: True 13 | - onlyif: test -f /etc/bash.bashrc 14 | 15 | etc_profile_umask: 16 | file.blockreplace: 17 | - name: /etc/profile 18 | - marker_start: "# BEGIN CIS 5.4.4 default user umask" 19 | - marker_end: "# END CIS 5.4.4 default user umask" 20 | - content: "umask {{ system.shell.umask }}" 21 | - append_if_not_found: True 22 | - onlyif: test -f /etc/profile 23 | {%- endif %} 24 | 25 | {%- if system.shell.timeout is defined %} 26 | etc_bash_bashrc_timeout: 27 | file.blockreplace: 28 | - name: /etc/bash.bashrc 29 | - marker_start: "# BEGIN CIS 5.4.5 default user shell timeout" 30 | - marker_end: "# END CIS 5.4.5 default user shell timeout" 31 | - content: "TMOUT={{ system.shell.timeout }}" 32 | - append_if_not_found: True 33 | - onlyif: test -f /etc/bash.bashrc 34 | 35 | etc_profile_timeout: 36 | file.blockreplace: 37 | - name: /etc/profile 38 | - marker_start: "# BEGIN CIS 5.4.5 default user shell timeout" 39 | - marker_end: "# END CIS 5.4.5 default user shell timeout" 40 | - content: "TMOUT={{ system.shell.timeout }}" 41 | - append_if_not_found: True 42 | - onlyif: test -f /etc/profile 43 | {%- endif %} 44 | {%- endif %} 45 | {%- endif %} 46 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-5-4-1-3.yml: -------------------------------------------------------------------------------- 1 | # CIS 5.4.1.3 Ensure password expiration warning days is 7 or more (Scored) 2 | # 3 | # Description 4 | # =========== 5 | # The PASS_WARN_AGE parameter in /etc/login.defs allows an administrator to 6 | # notify users that their password will expire in a defined number of days. 7 | # It is recommended that the PASS_WARN_AGE parameter be set to 7 or more days. 8 | # 9 | # Rationale 10 | # ========= 11 | # Providing an advance warning that a password will be expiring gives users 12 | # time to think of a secure password. Users caught unaware may choose a simple 13 | # password or write it down where it may be discovered. 14 | # 15 | # Audit 16 | # ===== 17 | # Run the following command and verify PASS_WARN_AGE is 7 or more: 18 | # 19 | # # grep PASS_WARN_AGE /etc/login.defs 20 | # PASS_WARN_AGE 7 21 | # 22 | # Verify all users with a password have their number of days of warning before 23 | # password expires set to 7 or more: 24 | # 25 | # # egrep ^[^:]+:[^\!*] /etc/shadow | cut -d: -f1 26 | # 27 | # # chage --list 28 | # Number of days of warning before password expires: 7 29 | # 30 | # Remediation 31 | # =========== 32 | # 33 | # Set the PASS_WARN_AGE parameter to 7 in /etc/login.defs : 34 | # 35 | # PASS_WARN_AGE 7 36 | # 37 | # Modify user parameters for all users with a password set to match: 38 | # 39 | # # chage --warndays 7 40 | # 41 | # Notes 42 | # ===== 43 | # You can also check this setting in /etc/shadow directly. The 6th field 44 | # should be 7 or more for all users with a password. 45 | # 46 | parameters: 47 | linux: 48 | system: 49 | login_defs: 50 | PASS_WARN_AGE: 51 | value: 7 52 | 53 | -------------------------------------------------------------------------------- /linux/system/at.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | {%- if system.at.enabled is defined and system.at.enabled %} 4 | 5 | at_packages: 6 | pkg.installed: 7 | - names: {{ system.at.pkgs }} 8 | 9 | at_services: 10 | service.running: 11 | - enable: true 12 | - names: {{ system.at.services }} 13 | - require: 14 | - pkg: at_packages 15 | {%- if grains.get('noservices') %} 16 | - onlyif: /bin/false 17 | {%- endif %} 18 | 19 | {%- set allow_users = [] %} 20 | {%- for user_name, user_params in system.at.get('user', {}).items() %} 21 | {%- set user_enabled = user_params.get('enabled', false) and 22 | system.get('user', {}).get( 23 | user_name, {'enabled': true}).get('enabled', true) %} 24 | {%- if user_enabled %} 25 | {%- do allow_users.append(user_name) %} 26 | {%- endif %} 27 | {%- endfor %} 28 | 29 | etc_at_allow: 30 | {%- if allow_users %} 31 | file.managed: 32 | - name: /etc/at.allow 33 | - template: jinja 34 | - source: salt://linux/files/cron_users.jinja 35 | - user: root 36 | - group: daemon 37 | - mode: 0640 38 | - defaults: 39 | users: {{ allow_users | yaml }} 40 | - require: 41 | - cron_packages 42 | {%- else %} 43 | file.absent: 44 | - name: /etc/at.allow 45 | {%- endif %} 46 | 47 | 48 | {# 49 | /etc/at.deny should be absent to comply with 50 | CIS 5.1.8 Ensure at/cron is restricted to authorized users 51 | #} 52 | etc_at_deny: 53 | file.absent: 54 | - name: /etc/at.deny 55 | 56 | {%- else %} 57 | 58 | fake_linux_system_at: 59 | test.nop: 60 | - comment: Fake state to satisfy 'require sls:linux.system.at' 61 | 62 | {%- endif %} 63 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-5-4-1-2.yml: -------------------------------------------------------------------------------- 1 | # CIS 5.4.1.2 Ensure minimum days between password changes is 7 or more (Scored) 2 | # 3 | # Description 4 | # =========== 5 | # The PASS_MIN_DAYS parameter in /etc/login.defs allows an administrator to 6 | # prevent users from changing their password until a minimum number of days 7 | # have passed since the last time the user changed their password. It is 8 | # recommended that PASS_MIN_DAYS parameter be set to 7 or more days. 9 | # 10 | # Rationale 11 | # ========= 12 | # By restricting the frequency of password changes, an administrator can 13 | # prevent users from repeatedly changing their password in an attempt to 14 | # circumvent password reuse controls. 15 | # 16 | # Audit 17 | # ===== 18 | # Run the following command and verify PASS_MIN_DAYS is 7 or more: 19 | # 20 | # # grep PASS_MIN_DAYS /etc/login.defs 21 | # PASS_MIN_DAYS 7 22 | # 23 | # Verify all users with a password have their minimum days between password 24 | # change set to 7 or more: 25 | # 26 | # # egrep ^[^:]+:[^\!*] /etc/shadow | cut -d: -f1 27 | # 28 | # # chage --list 29 | # Minimum number of days between password change: 7 30 | # 31 | # Remediation 32 | # =========== 33 | # Set the PASS_MIN_DAYS parameter to 7 in /etc/login.defs : 34 | # 35 | # PASS_MIN_DAYS 7 36 | # 37 | # Modify user parameters for all users with a password set to match: 38 | # 39 | # # chage --mindays 7 40 | # 41 | # Notes 42 | # ===== 43 | # You can also check this setting in /etc/shadow directly. The 5th field 44 | # should be 7 or more for all users with a password. 45 | # 46 | parameters: 47 | linux: 48 | system: 49 | login_defs: 50 | PASS_MIN_DAYS: 51 | value: 7 52 | 53 | -------------------------------------------------------------------------------- /linux/system/sysfs.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | linux_sysfs_package: 4 | pkg.installed: 5 | - pkgs: 6 | - sysfsutils 7 | - refresh: true 8 | 9 | /etc/sysfs.d: 10 | file.directory: 11 | - require: 12 | - pkg: linux_sysfs_package 13 | 14 | {% set apply = system.get('sysfs', {}).pop('enable_apply', True) %} 15 | 16 | {%- for name, sysfs in system.get('sysfs', {}).items() %} 17 | 18 | /etc/sysfs.d/{{ name }}.conf: 19 | file.managed: 20 | - source: salt://linux/files/sysfs.conf 21 | - template: jinja 22 | - user: root 23 | - group: root 24 | - mode: 0644 25 | - defaults: 26 | name: {{ name }} 27 | sysfs: {{ sysfs|yaml }} 28 | - require: 29 | - file: /etc/sysfs.d 30 | 31 | {%- if sysfs is mapping %} 32 | {%- set sysfs_list = [sysfs] %} 33 | {%- else %} 34 | {%- set sysfs_list = sysfs %} 35 | {%- endif %} 36 | 37 | {%- if apply %} 38 | 39 | {%- for item in sysfs_list %} 40 | {%- set list_idx = loop.index %} 41 | {%- for key, value in item.items() %} 42 | {%- if key not in ["mode", "owner"] %} 43 | {%- if grains.get('virtual_subtype', None) not in ['Docker', 'LXC'] %} 44 | {#- Sysfs cannot be set in docker, LXC, etc. #} 45 | linux_sysfs_write_{{ list_idx }}_{{ name }}_{{ key }}: 46 | module.run: 47 | {%- if 'module.run' in salt['config.get']('use_superseded', default=[]) %} 48 | - sysfs.write: 49 | - key: {{ key }} 50 | - value: {{ value }} 51 | {%- else %} 52 | - name: sysfs.write 53 | - key: {{ key }} 54 | - value: {{ value }} 55 | {%- endif %} 56 | {%- endif %} 57 | {%- endif %} 58 | {%- endfor %} 59 | 60 | {%- endfor %} 61 | 62 | {%- endif %} 63 | 64 | {%- endfor %} 65 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-5-4-1-1.yml: -------------------------------------------------------------------------------- 1 | # CIS 5.4.1.1 Ensure password expiration is 90 days or less (Scored) 2 | # 3 | # Description 4 | # =========== 5 | # The PASS_MAX_DAYS parameter in /etc/login.defs allows an administrator to 6 | # force passwords to expire once they reach a defined age. It is recommended 7 | # that the PASS_MAX_DAYS parameter be set to less than or equal to 90 days. 8 | # 9 | # Rationale 10 | # ========= 11 | # The window of opportunity for an attacker to leverage compromised credentials 12 | # or successfully compromise credentials via an online brute force attack is 13 | # limited by the age of the password. Therefore, reducing the maximum age of a 14 | # password also reduces an attacker's window of opportunity. 15 | # 16 | # Audit 17 | # ===== 18 | # Run the following command and verify PASS_MAX_DAYS is 90 or less: 19 | # 20 | # # grep PASS_MAX_DAYS /etc/login.defs 21 | # PASS_MAX_DAYS 90 22 | # 23 | # Verify all users with a password have their maximum days between password 24 | # change set to 90 or less: 25 | # 26 | # # egrep ^[^:]+:[^\!*] /etc/shadow | cut -d: -f1 27 | # 28 | # # chage --list 29 | # Maximum number of days between password change: 90 30 | # 31 | # Remediation 32 | # =========== 33 | # Set the PASS_MAX_DAYS parameter to 90 in /etc/login.defs : 34 | # 35 | # PASS_MAX_DAYS 90 36 | # 37 | # Modify user parameters for all users with a password set to match: 38 | # 39 | # # chage --maxdays 90 40 | # 41 | # Notes 42 | # ===== 43 | # You can also check this setting in /etc/shadow directly. The 5th field 44 | # should be 90 or less for all users with a password. 45 | # 46 | parameters: 47 | linux: 48 | system: 49 | login_defs: 50 | PASS_MAX_DAYS: 51 | value: 90 52 | 53 | -------------------------------------------------------------------------------- /linux/system/config.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- macro load_support_file(file, pillar, grains) %}{% include file %}{% endmacro %} 3 | 4 | {%- if system.enabled %} 5 | 6 | {%- for config_name, config in system.get('config', {}).items() %} 7 | {%- if config.enabled|default(True) %} 8 | {%- for service_name in config.pillar.keys() %} 9 | {%- if pillar.get(service_name, {}).get('_support', {}).get('config', {}).get('enabled', False) %} 10 | {%- set support_fragment_file = service_name+'/meta/config.yml' %} 11 | {%- set service_config_files = load_support_file(support_fragment_file, config.pillar, config.get('grains', {}))|load_yaml %} 12 | {%- for service_config_name, service_config in service_config_files.config.items() %} 13 | 14 | {{ service_config.path }}: 15 | file.managed: 16 | - source: {{ service_config.source }} 17 | - user: {{ config.get('user', service_config.get('user', 'root')) }} 18 | - group: {{ config.get('group', service_config.get('group', 'root')) }} 19 | - mode: {{ config.get('mode', service_config.get('mode', '644')) }} 20 | {%- if service_config.template is defined %} 21 | - template: {{ service_config.template }} 22 | {%- endif %} 23 | - makedirs: true 24 | - defaults: 25 | pillar: {{ config.pillar|yaml }} 26 | grains: {{ config.get('grains', {}) }} 27 | {%- for key, value in service_config.get('defaults', {}).items() %} 28 | {{ key }}: {{ value }} 29 | {%- endfor %} 30 | 31 | {%- endfor %} 32 | {%- endif %} 33 | {%- endfor %} 34 | {%- else %} 35 | {# TODO: configmap not using support between formulas #} 36 | {%- endif %} 37 | {%- endfor %} 38 | 39 | {%- endif %} 40 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-2-2.yml: -------------------------------------------------------------------------------- 1 | # 3.2.2 Ensure ICMP redirects are not accepted 2 | # 3 | # Description 4 | # =========== 5 | # ICMP redirect messages are packets that convey routing information and tell 6 | # your host (acting as a router) to send packets via an alternate path. It is 7 | # a way of allowing an outside routing device to update your system routing 8 | # tables. By setting net.ipv4.conf.all.accept_redirects to 0, the system will 9 | # not accept any ICMP redirect messages, and therefore, won't allow outsiders 10 | # to update the system's routing tables. 11 | # 12 | # Rationale 13 | # ========= 14 | # Attackers could use bogus ICMP redirect messages to maliciously alter the 15 | # system routing tables and get them to send packets to incorrect networks and 16 | # allow your system packets to be captured. 17 | # 18 | # Audit 19 | # ===== 20 | # 21 | # Run the following commands and verify output matches: 22 | # 23 | # # sysctl net.ipv4.conf.all.accept_redirects 24 | # net.ipv4.conf.all.accept_redirects = 0 25 | # # sysctl net.ipv4.conf.default.accept_redirects 26 | # net.ipv4.conf.default.accept_redirects = 0 27 | # 28 | # Remediation 29 | # =========== 30 | # 31 | # Set the following parameters in the /etc/sysctl.conf file: 32 | # 33 | # net.ipv4.conf.all.accept_redirects = 0 34 | # net.ipv4.conf.default.accept_redirects = 0 35 | # 36 | # Run the following commands to set the active kernel parameters: 37 | # 38 | # # sysctl -w net.ipv4.conf.all.accept_redirects=0 39 | # # sysctl -w net.ipv4.conf.default.accept_redirects=0 40 | # # sysctl -w net.ipv4.route.flush=1 41 | 42 | parameters: 43 | linux: 44 | system: 45 | kernel: 46 | sysctl: 47 | net.ipv4.conf.all.accept_redirects: 0 48 | net.ipv4.conf.default.accept_redirects: 0 49 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-1-21.yml: -------------------------------------------------------------------------------- 1 | # CIS 1.1.21 Disable Automounting 2 | # 3 | # Description 4 | # =========== 5 | # autofs allows automatic mounting of devices, typically including CD/DVDs 6 | # and USB drives. 7 | # 8 | # Rationale 9 | # ========= 10 | # With automounting enabled anyone with physical access could attach a USB 11 | # drive or disc and have its contents available in system even if they lacked 12 | # permissions to mount it themselves. 13 | # 14 | # Audit 15 | # ===== 16 | # Run the following command to verify autofs is not enabled: 17 | # 18 | # # systemctl is-enabled autofs 19 | # disabled 20 | # 21 | # Verify result is not "enabled". 22 | # 23 | # Remediation 24 | # =========== 25 | # 26 | # Run the following command to disable autofs : 27 | # 28 | # # systemctl disable autofs 29 | # 30 | # Impact 31 | # ====== 32 | # The use portable hard drives is very common for workstation users. If your 33 | # organization allows the use of portable storage or media on workstations 34 | # and physical access controls to workstations is considered adequate there 35 | # is little value add in turning off automounting. 36 | # 37 | # Notes 38 | # ===== 39 | # This control should align with the tolerance of the use of portable drives 40 | # and optical media in the organization. On a server requiring an admin to 41 | # manually mount media can be part of defense-in-depth to reduce the risk of 42 | # unapproved software or information being introduced or proprietary software 43 | # or information being exfiltrated. If admins commonly use flash drives and 44 | # Server access has sufficient physical controls, requiring manual mounting 45 | # may not increase security. 46 | # 47 | parameters: 48 | linux: 49 | system: 50 | service: 51 | autofs: 52 | status: disabled 53 | 54 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-1-5-1.yml: -------------------------------------------------------------------------------- 1 | # CIS 1.5.1 Ensure core dumps are restricted (Scored) 2 | # 3 | # Description 4 | # =========== 5 | # 6 | # A core dump is the memory of an executable program. It is generally used to determine 7 | # why a program aborted. It can also be used to glean confidential information from a core 8 | # file. The system provides the ability to set a soft limit for core dumps, but this can be 9 | # overridden by the user. 10 | # 11 | # Rationale 12 | # ========= 13 | # 14 | # Setting a hard limit on core dumps prevents users from overriding the soft variable. If core 15 | # dumps are required, consider setting limits for user groups (see limits.conf(5) ). In 16 | # addition, setting the fs.suid_dumpable variable to 0 will prevent setuid programs from 17 | # dumping core. 18 | # 19 | # Audit 20 | # ===== 21 | # 22 | # Run the following commands and verify output matches: 23 | # 24 | # # grep "hard core" /etc/security/limits.conf /etc/security/limits.d/* 25 | # * hard core 0 26 | # # sysctl fs.suid_dumpable 27 | # fs.suid_dumpable = 0 28 | # 29 | # Remediation 30 | # =========== 31 | # 32 | # Add the following line to the /etc/security/limits.conf file or a 33 | # /etc/security/limits.d/* file: 34 | # 35 | # * hard core 0 36 | # 37 | # Set the following parameter in the /etc/sysctl.conf file: 38 | # 39 | # fs.suid_dumpable = 0 40 | # 41 | # Run the following command to set the active kernel parameter: 42 | # 43 | # # sysctl -w fs.suid_dumpable=0 44 | 45 | parameters: 46 | linux: 47 | system: 48 | limit: 49 | cis: 50 | enabled: true 51 | domain: '*' 52 | limits: 53 | - type: 'hard' 54 | item: 'core' 55 | value: 0 56 | kernel: 57 | sysctl: 58 | fs.suid_dumpable: 0 59 | 60 | -------------------------------------------------------------------------------- /linux/storage/mount.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import storage with context %} 2 | {%- if storage.enabled %} 3 | 4 | {%- for name, mount in storage.mount|dictsort %} 5 | 6 | {%- if mount.enabled %} 7 | 8 | {%- if not mount.file_system in ['nfs', 'nfs4', 'cifs', 'tmpfs', 'none'] %} 9 | 10 | mkfs_{{ mount.device}}: 11 | cmd.run: 12 | - name: "mkfs.{{ mount.file_system }} -L {{ name }} {{ mount.device }}" 13 | - onlyif: "test `blkid {{ mount.device }} | grep -q TYPE;echo $?` -eq 1" 14 | - require_in: 15 | - mount: {{ mount.path }} 16 | {%- if mount.file_system == 'xfs' %} 17 | - require: 18 | - pkg: xfs_packages_{{ mount.device }} 19 | 20 | xfs_packages_{{ mount.device }}: 21 | pkg.installed: 22 | - name: xfsprogs 23 | {%- endif %} 24 | 25 | {%- endif %} 26 | 27 | {%- if mount.file_system == 'nfs' %} 28 | linux_storage_nfs_packages_{{ mount.path }}: 29 | pkg.installed: 30 | - pkgs: {{ storage.nfs.pkgs | json }} 31 | {%- endif %} 32 | 33 | {{ mount.path }}: 34 | mount.mounted: 35 | - order: 1 36 | - device: {{ mount.device }} 37 | - fstype: {{ mount.file_system }} 38 | - mkmnt: True 39 | - opts: {{ mount.get('opts', 'defaults,noatime') }} 40 | - dump: {{ mount.dump|default('0', true) }} 41 | - pass_num: {{ mount.pass_num|default('0', true) }} 42 | {%- if mount.file_system == 'xfs' %} 43 | - require: 44 | - pkg: xfs_packages_{{ mount.device }} 45 | {%- endif %} 46 | 47 | {%- if mount.user is defined %} 48 | {{ mount.path }}_permissions: 49 | file.directory: 50 | - name: {{ mount.path }} 51 | - user: {{ mount.user }} 52 | - group: {{ mount.get('group', 'root') }} 53 | - mode: {{ mount.get('mode', 755) }} 54 | - require: 55 | - mount: {{ mount.path }} 56 | {%- endif %} 57 | 58 | {%- endif %} 59 | 60 | {%- endfor %} 61 | 62 | {%- endif %} 63 | -------------------------------------------------------------------------------- /linux/system/atop.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | {%- if system.atop.enabled %} 4 | 5 | atop_packages: 6 | pkg.installed: 7 | - name: atop 8 | 9 | atop_defaults: 10 | file.managed: 11 | - name: /etc/default/atop 12 | - source: salt://linux/files/atop.conf 13 | - template: jinja 14 | - user: root 15 | - group: root 16 | - mode: 644 17 | 18 | atop_logpath: 19 | file.directory: 20 | - name: {{ system.atop.logpath }} 21 | - user: root 22 | - group: root 23 | - mode: 750 24 | - makedirs: true 25 | 26 | {%- if grains.get('init', None) == 'systemd' %} 27 | atop_systemd_file: 28 | file.managed: 29 | - name: /etc/systemd/system/atop.service 30 | - source: salt://linux/files/atop.service 31 | - user: root 32 | - mode: 644 33 | - defaults: 34 | service_name: atop 35 | config_file: /etc/default/atop 36 | autostart: {{ system.atop.autostart }} 37 | - template: jinja 38 | - require_in: 39 | - service: atop_service 40 | {%- endif %} 41 | 42 | atop_service: 43 | service.running: 44 | - name: atop 45 | - enable: {{ system.atop.autostart }} 46 | - watch: 47 | - file: atop_defaults 48 | {%- if grains.get('noservices') %} 49 | - onlyif: /bin/false 50 | {%- endif %} 51 | 52 | {%- else %} 53 | 54 | atop_service_stop: 55 | service.dead: 56 | - name: atop 57 | - enable: false 58 | - require_in: 59 | - pkg: atop_pkg_purge 60 | {%- if grains.get('noservices') %} 61 | - onlyif: /bin/false 62 | {%- endif %} 63 | 64 | atop_defaults_purge: 65 | file.absent: 66 | - names: 67 | - /etc/default/atop 68 | - /etc/systemd/system/atop.service 69 | - require: 70 | - pkg: atop_pkg_purge 71 | 72 | atop_pkg_purge: 73 | pkg.purged: 74 | - name: atop 75 | 76 | {%- endif %} 77 | -------------------------------------------------------------------------------- /linux/files/nslcd.conf: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import ldap with context -%} 2 | # /etc/nslcd.conf 3 | # nslcd configuration file. See nslcd.conf(5) 4 | # for details. 5 | 6 | # The user and group nslcd should run as. 7 | uid {{ ldap.uid }} 8 | gid {{ ldap.gid }} 9 | 10 | {%- if ldap.enabled %} 11 | 12 | {%- if ldap.uri is defined %} 13 | # The location at which the LDAP server(s) should be reachable. 14 | uri {{ ldap.uri }} 15 | {%- endif %} 16 | 17 | {%- if ldap.base is defined %} 18 | # The search base that will be used for all queries. 19 | base {{ ldap.base }} 20 | {%- endif %} 21 | 22 | # The LDAP protocol version to use. 23 | ldap_version {{ ldap.version }} 24 | 25 | {%- if ldap.binddn is defined %} 26 | # The DN to bind with for normal lookups. 27 | binddn {{ ldap.binddn }} 28 | {%- if ldap.bindpw is defined %} 29 | bindpw {{ ldap.bindpw }} 30 | {%- endif %} 31 | {%- endif %} 32 | 33 | {%- if ldap.rootpwmoddn is defined %} 34 | # The DN used for password modifications by root. 35 | rootpwmoddn {{ ldap.rootpwmoddn }} 36 | {%- endif %} 37 | 38 | # SSL options 39 | #ssl off 40 | #tls_reqcert never 41 | #tls_cacertfile /etc/ssl/certs/ca-certificates.crt 42 | 43 | # The search scope. 44 | scope {{ ldap.scope }} 45 | 46 | {%- if ldap.pagesize is defined %} 47 | pagesize {{ ldap.pagesize }} 48 | {%- endif %} 49 | {%- if ldap.referrals is defined %} 50 | referrals {{ ldap.referrals }} 51 | {%- endif %} 52 | 53 | {%- if ldap.filter is defined %} 54 | # Filters 55 | {%- for key, value in ldap.filter.items() %} 56 | filter {{ key }} {{ value }} 57 | {%- endfor %} 58 | {%- endif %} 59 | {%- if ldap.map is defined %} 60 | # Mappings 61 | {%- for map_name,map in ldap.map.items() %} 62 | {%- for key, value in map.items() %} 63 | map {{ map_name }} {{ key }} {{ value }} 64 | {%- endfor %} 65 | {%- endfor %} 66 | {%- endif %} 67 | 68 | {%- endif %} 69 | -------------------------------------------------------------------------------- /linux/system/job.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | include: 5 | - linux.system.user 6 | - linux.system.cron 7 | 8 | {%- for name, job in system.job.items() %} 9 | {%- set job_user = job.get('user', 'root') %} 10 | 11 | linux_job_{{ job.command }}: 12 | {%- if job.get('enabled', True) %} 13 | cron.present: 14 | - name: > 15 | {{ job.command }} 16 | {%- if job.get('identifier', True) %} 17 | - identifier: {{ job.get('identifier', job.get('name', name)) }} 18 | {%- endif %} 19 | {%- if job.get('commented', False) %} 20 | - commented: True 21 | {%- endif %} 22 | - user: {{ job_user }} 23 | {%- if job.special is defined %} 24 | - special: '{{ job.special }}' 25 | {%- else %} 26 | {%- if job.minute is defined %} 27 | - minute: '{{ job.minute }}' 28 | {%- endif %} 29 | {%- if job.hour is defined %} 30 | - hour: '{{ job.hour }}' 31 | {%- endif %} 32 | {%- if job.daymonth is defined %} 33 | - daymonth: '{{ job.daymonth }}' 34 | {%- endif %} 35 | {%- if job.month is defined %} 36 | - month: '{{ job.month }}' 37 | {%- endif %} 38 | {%- if job.dayweek is defined %} 39 | - dayweek: '{{ job.dayweek }}' 40 | {%- endif %} 41 | {%- endif %} 42 | - require: 43 | - sls: linux.system.cron 44 | {%- if job_user in system.get('user', {}).keys() %} 45 | - user: system_user_{{ job_user }} 46 | {%- endif %} 47 | {%- else %} 48 | cron.absent: 49 | - name: {{ job.command }} 50 | {%- if job.get('identifier', True) %} 51 | - identifier: {{ job.get('identifier', job.get('name', name)) }} 52 | {%- endif %} 53 | - user: {{ job_user }} 54 | {%- endif %} 55 | {%- endfor %} 56 | {%- endif %} 57 | -------------------------------------------------------------------------------- /linux/system/package.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- set pkgs_groups = { 5 | 'latest': [], 6 | 'purged': [], 7 | 'removed': [], 8 | 'installed': [], 9 | } %} 10 | {%- for name, package in system.package.items() %} 11 | 12 | {%- if package.repo is defined or package.hold is defined or package.verify is defined %} 13 | linux_extra_package_{{ name }}: 14 | {%- if package.version is defined %} 15 | {%- if package.version == 'latest' %} 16 | pkg.latest: 17 | {%- elif package.version == 'purged' %} 18 | pkg.purged: 19 | {%- elif package.version == 'removed' %} 20 | pkg.removed: 21 | {%- else %} 22 | pkg.installed: 23 | - version: {{ package.version }} 24 | {%- endif %} 25 | {%- else %} 26 | pkg.installed: 27 | {%- endif %} 28 | - name: {{ name }} 29 | {%- if package.repo is defined %} 30 | - fromrepo: {{ package.repo }} 31 | {%- endif %} 32 | {%- if package.hold is defined %} 33 | - hold: {{ package.hold }} 34 | {%- endif %} 35 | {%- if package.verify is defined %} 36 | - skip_verify: {{ "False" if package.verify else "True" }} 37 | {%- endif %} 38 | {%- else %} 39 | {%- if package.version is not defined %} 40 | {%- do pkgs_groups['installed'].append(name) %} 41 | {%- elif package.version in ('latest', 'purged', 'removed') %} 42 | {%- do pkgs_groups[package.version].append(name) %} 43 | {%- else %} 44 | {%- do pkgs_groups['installed'].append({name: package.version}) %} 45 | {%- endif %} 46 | {%- endif %} 47 | 48 | {%- endfor %} 49 | 50 | {%- for pkgs_group, pkgs in pkgs_groups.items() %} 51 | {%- if pkgs %} 52 | linux_extra_packages_{{ pkgs_group }}: 53 | pkg.{{ pkgs_group }}: 54 | - pkgs: {{ pkgs | json }} 55 | {%- endif %} 56 | {%- endfor %} 57 | 58 | {%- endif %} 59 | -------------------------------------------------------------------------------- /metadata/service/system/cis/init.yml: -------------------------------------------------------------------------------- 1 | classes: 2 | - service.linux.system.cis.cis-1-1-1-1 3 | - service.linux.system.cis.cis-1-1-1-2 4 | - service.linux.system.cis.cis-1-1-1-3 5 | - service.linux.system.cis.cis-1-1-1-4 6 | - service.linux.system.cis.cis-1-1-1-5 7 | - service.linux.system.cis.cis-1-1-1-6 8 | - service.linux.system.cis.cis-1-1-1-7 9 | - service.linux.system.cis.cis-1-1-1-8 10 | - service.linux.system.cis.cis-1-1-14_15_16 11 | - service.linux.system.cis.cis-1-1-21 12 | - service.linux.system.cis.cis-1-5-1 13 | - service.linux.system.cis.cis-1-5-3 14 | - service.linux.system.cis.cis-1-5-4 15 | - service.linux.system.cis.cis-2-3-1 16 | - service.linux.system.cis.cis-2-3-2 17 | - service.linux.system.cis.cis-2-3-3 18 | - service.linux.system.cis.cis-2-3-4 19 | - service.linux.system.cis.cis-3-1-2 20 | - service.linux.system.cis.cis-3-2-1 21 | - service.linux.system.cis.cis-3-2-2 22 | - service.linux.system.cis.cis-3-2-3 23 | - service.linux.system.cis.cis-3-2-4 24 | - service.linux.system.cis.cis-3-2-5 25 | - service.linux.system.cis.cis-3-2-6 26 | - service.linux.system.cis.cis-3-2-7 27 | - service.linux.system.cis.cis-3-2-8 28 | # Temp. disable PROD-22520 29 | #- service.linux.system.cis.cis-3-3-3 30 | - service.linux.system.cis.cis-3-5-1 31 | - service.linux.system.cis.cis-3-5-2 32 | - service.linux.system.cis.cis-3-5-3 33 | - service.linux.system.cis.cis-3-5-4 34 | - service.linux.system.cis.cis-5-4-1-1 35 | - service.linux.system.cis.cis-5-4-1-2 36 | - service.linux.system.cis.cis-5-4-1-3 37 | - service.linux.system.cis.cis-5-4-4 38 | - service.linux.system.cis.cis-6-1-2 39 | - service.linux.system.cis.cis-6-1-3 40 | - service.linux.system.cis.cis-6-1-4 41 | - service.linux.system.cis.cis-6-1-5 42 | - service.linux.system.cis.cis-6-1-6 43 | - service.linux.system.cis.cis-6-1-7 44 | - service.linux.system.cis.cis-6-1-8 45 | - service.linux.system.cis.cis-6-1-9 46 | -------------------------------------------------------------------------------- /tests/pillar/storage.sls: -------------------------------------------------------------------------------- 1 | linux: 2 | system: 3 | enabled: true 4 | name: linux 5 | domain: local 6 | network: 7 | enabled: true 8 | hostname: linux 9 | fqdn: linux.ci.local 10 | storage: 11 | enabled: true 12 | swap: 13 | file: 14 | enabled: true 15 | engine: file 16 | device: /tmp/loop_dev2 17 | size: 5 18 | mount: 19 | # NOTE: simple dummy loop devices, use for test purposes only 20 | dev0: 21 | enabled: false 22 | device: /tmp/loop_dev0 23 | path: /tmp/node/dev0 24 | file_system: xfs 25 | opts: noatime,nobarrier,logbufs=8,nobootwait,nobarrier 26 | user: root 27 | group: root 28 | mode: 755 29 | dev1: 30 | enabled: true 31 | device: /tmp/loop_dev1 32 | path: /mnt 33 | file_system: ext4 34 | #opts: noatime,nobarrier,logbufs=8,nobootwait,nobarrier 35 | user: root 36 | group: root 37 | lvm: 38 | vg0: 39 | name: vg0-dummy 40 | enabled: true 41 | devices: 42 | - /tmp/loop_dev3 43 | volume: 44 | lv01: 45 | size: 5M 46 | mount: 47 | device: /dev/vg0/lv01 48 | path: /mnt/lv01 49 | lv02: 50 | size: 5M 51 | mount: 52 | device: /dev/vg0/lv02 53 | path: /mnt/lv02 54 | file_system: ext4 55 | lv03: 56 | size: 5M 57 | mount: 58 | device: /dev/vg0/lv03 59 | path: /mnt/lv03 60 | file_system: xfs 61 | disk: 62 | first_drive: 63 | name: /tmp/loop_dev4 64 | type: gpt 65 | partitions: 66 | - size: 5 67 | type: fat32 68 | - size: 5 69 | type: fat32 70 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-2-7.yml: -------------------------------------------------------------------------------- 1 | # 3.2.7 Ensure Reverse Path Filtering is enabled 2 | # 3 | # Description 4 | # =========== 5 | # Setting net.ipv4.conf.all.rp_filter and net.ipv4.conf.default.rp_filter to 1 6 | # forces the Linux kernel to utilize reverse path filtering on a received 7 | # packet to determine if the packet was valid. Essentially, with reverse path 8 | # filtering, if the return packet does not go out the same interface that the 9 | # corresponding source packet came from, the packet is dropped (and logged if 10 | # log_martians is set). 11 | # 12 | # Rationale 13 | # ========= 14 | # Setting these flags is a good way to deter attackers from sending your system 15 | # bogus packets that cannot be responded to. One instance where this feature 16 | # breaks down is if asymmetrical routing is employed. This would occur when 17 | # using dynamic routing protocols (bgp, ospf, etc) on your system. If you are 18 | # using asymmetrical routing on your system, you will not be able to enable 19 | # this feature without breaking the routing. 20 | # 21 | # Audit 22 | # ===== 23 | # 24 | # Run the following commands and verify output matches: 25 | # 26 | # # sysctl net.ipv4.conf.all.rp_filter 27 | # net.ipv4.conf.all.rp_filter = 1 28 | # # sysctl net.ipv4.conf.default.rp_filter 29 | # net.ipv4.conf.default.rp_filter = 1 30 | # 31 | # Remediation 32 | # =========== 33 | # 34 | # Set the following parameters in the /etc/sysctl.conf file: 35 | # 36 | # net.ipv4.conf.all.rp_filter = 1 37 | # net.ipv4.conf.default.rp_filter = 1 38 | # 39 | # Run the following commands to set the active kernel parameters: 40 | # 41 | # # sysctl -w net.ipv4.conf.all.rp_filter=1 42 | # # sysctl -w net.ipv4.conf.default.rp_filter=1 43 | # # sysctl -w net.ipv4.route.flush=1 44 | 45 | parameters: 46 | linux: 47 | system: 48 | kernel: 49 | sysctl: 50 | net.ipv4.conf.all.rp_filter: 1 51 | net.ipv4.conf.default.rp_filter: 1 52 | -------------------------------------------------------------------------------- /linux/meta/sensu.yml: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system,network,storage,monitoring with context -%} 2 | check: 3 | local_linux_system_zombie_procs: 4 | command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_procs -w {{ monitoring.zombie.warn }} -c {{ monitoring.zombie.crit }} -s Z" 5 | interval: 60 6 | occurrences: 3 7 | subscribers: 8 | - local-linux-system 9 | local_linux_system_total_procs: 10 | command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_procs -w {{ monitoring.procs.warn }} -c {{ monitoring.procs.crit }}" 11 | interval: 60 12 | occurrences: 5 13 | subscribers: 14 | - local-linux-system 15 | local_linux_system_load: 16 | command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_load -r -w {{ monitoring.load.warn }} -c {{ monitoring.load.crit }}" 17 | interval: 60 18 | occurrences: 1 19 | subscribers: 20 | - local-linux-system 21 | {%- if storage.swap|length > 0 %} 22 | local_linux_storage_swap_usage_{{ system.name|replace('.', '-') }}-{{ system.domain|replace('.', '-') }}: 23 | command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_swap -a -w {{ monitoring.swap.warn }} -c {{ monitoring.swap.crit }}" 24 | interval: 60 25 | occurrences: 1 26 | subscribers: 27 | - {{ system.name|replace('.', '-') }}-{{ system.domain|replace('.', '-') }} 28 | {%- endif %} 29 | local_linux_storage_disk_usage: 30 | command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_disk -w {{ monitoring.disk.warn }} -c {{ monitoring.disk.crit }} -p / -p /var -p /usr -p /tmp -p /var/log" 31 | interval: 60 32 | occurrences: 1 33 | subscribers: 34 | - local-linux-storage 35 | local_linux_network_fqdn: 36 | command: "PATH=$PATH:/etc/sensu/plugins check_fqdn.py -n :::hostname::: -f :::fqdn:::" 37 | interval: 60 38 | occurrences: 1 39 | subscribers: 40 | - local-linux-network 41 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-2-8.yml: -------------------------------------------------------------------------------- 1 | # 3.2.8 Ensure TCP SYN Cookies is enabled 2 | # 3 | # Description 4 | # =========== 5 | # When tcp_syncookies is set, the kernel will handle TCP SYN packets normally 6 | # until the half-open connection queue is full, at which time, the SYN cookie 7 | # functionality kicks in. SYN cookies work by not using the SYN queue at all. 8 | # Instead, the kernel simply replies to the SYN with a SYN|ACK, but will 9 | # include a specially crafted TCP sequence number that encodes the source and 10 | # destination IP address and port number and the time the packet was sent. 11 | # A legitimate connection would send the ACK packet of the three way handshake 12 | # with the specially crafted sequence number. This allows the system to verify 13 | # that it has received a valid response to a SYN cookie and allow the 14 | # connection, even though there is no corresponding SYN in the queue. 15 | # 16 | # Rationale 17 | # ========= 18 | # Attackers use SYN flood attacks to perform a denial of service attacked on a 19 | # system by sending many SYN packets without completing the three way handshake. 20 | # This will quickly use up slots in the kernel's half-open connection queue and 21 | # prevent legitimate connections from succeeding. SYN cookies allow the system 22 | # to keep accepting valid connections, even if under a denial of service attack. 23 | # 24 | # Audit 25 | # ===== 26 | # 27 | # Run the following commands and verify output matches: 28 | # 29 | # # sysctl net.ipv4.tcp_syncookies 30 | # net.ipv4.tcp_syncookies = 1 31 | # 32 | # Remediation 33 | # =========== 34 | # 35 | # Set the following parameter in the /etc/sysctl.conf file: 36 | # 37 | # net.ipv4.tcp_syncookies = 1 38 | # 39 | # Run the following commands to set the active kernel parameters: 40 | # 41 | # # sysctl -w net.ipv4.tcp_syncookies=1 42 | # # sysctl -w net.ipv4.route.flush=1 43 | 44 | parameters: 45 | linux: 46 | system: 47 | kernel: 48 | sysctl: 49 | net.ipv4.tcp_syncookies: 1 50 | -------------------------------------------------------------------------------- /linux/meta/collectd.yml: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import monitoring with context %} 2 | local_plugin: 3 | linux_network_netlink: 4 | plugin: netlink 5 | template: linux/files/collectd_netlink.conf 6 | ignore_selected: {{ monitoring.netlink.ignore_selected }} 7 | {%- if monitoring.netlink.interfaces is list and monitoring.netlink.interfaces|length > 0 %} 8 | {%- set interfaces = monitoring.netlink.interfaces %} 9 | {%- else %} 10 | {%- set interfaces = salt['linux_netlink.ls'](monitoring.netlink.interface_regex) %} 11 | {%- endif %} 12 | {%- if interfaces %} 13 | interfaces: 14 | {%- for interface_name in interfaces|sort %} 15 | - {{ interface_name }} 16 | {%- endfor %} 17 | {%- endif %} 18 | linux_system_cpu: 19 | plugin: cpu 20 | linux_system_entropy: 21 | plugin: entropy 22 | linux_system_load: 23 | plugin: load 24 | linux_system_contextswitch: 25 | plugin: contextswitch 26 | linux_system_memory: 27 | plugin: memory 28 | linux_system_uptime: 29 | plugin: uptime 30 | linux_system_users: 31 | plugin: users 32 | linux_storage_df: 33 | plugin: df 34 | template: linux/files/collectd_df.conf 35 | ignore_selected: True 36 | fs_types: 37 | - rootfs 38 | - sysfs 39 | - proc 40 | - devtmpfs 41 | - devpts 42 | - tmpfs 43 | - fusectl 44 | - cgroup 45 | - overlay 46 | linux_storage_disk: 47 | plugin: disk 48 | template: linux/files/collectd_disk.conf 49 | ignore_selected: True 50 | linux_storage_swap: 51 | plugin: swap 52 | template: linux/files/collectd_swap.conf 53 | report_bytes: True 54 | {%- if monitoring.bond_status.interfaces is defined and monitoring.bond_status.interfaces is list %} 55 | linux_bond_status: 56 | plugin: python 57 | template: linux/files/collectd_bond_status.conf 58 | interfaces: 59 | {%- for interface in monitoring.bond_status.interfaces %} 60 | - {{ interface }} 61 | {%- endfor %} 62 | {%- endif %} 63 | -------------------------------------------------------------------------------- /linux/files/openvswitch-switch.default: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | {%- set openvswitch = network.openvswitch %} 3 | # This is a POSIX shell fragment -*- sh -*- 4 | 5 | # FORCE_COREFILES: If 'yes' then core files will be enabled. 6 | # FORCE_COREFILES=yes 7 | 8 | # OVS_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, 9 | # a suitable place to specify --ovs-vswitchd-wrapper=valgrind. 10 | # OVS_CTL_OPTS= 11 | 12 | # OVS_VSWITCHD_OPTS: Extra options to pass to ovs-ctl. 13 | # Options to start Open vSwitch daemon with. 14 | # Example: '-vconsole:dbg -vsyslog:dbg -vfile:dbg -vFACILITY:clock2' 15 | # OVS_VSWITCHD_OPTS= 16 | {%- if openvswitch.get('logging', {}).vswitchd is defined %} 17 | {%- set _vswitchd_opts = [] %} 18 | {%- for opt in ['console', 'file', 'syslog'] %} 19 | {%- if openvswitch.logging.vswitchd.get(opt) %} 20 | {%- do _vswitchd_opts.append("-v"+ opt + ":" + openvswitch.logging.vswitchd.get(opt)) %} 21 | {%- endif %} 22 | {%- endfor %} 23 | {%- if openvswitch.logging.vswitchd.facility is defined %} 24 | {%- do _vswitchd_opts.append("-vFACILITY:" + openvswitch.logging.vswitchd.facility) %} 25 | {%- endif %} 26 | OVS_VSWITCHD_OPTS="{{ ' '.join(_vswitchd_opts) }}" 27 | {%- endif %} 28 | 29 | # OVSDB_OPTS: Extra options to pass to ovs-ctl. 30 | # Options to start Open vSwitch DB daemon with. 31 | # Example: '-vconsole:dbg -vsyslog:dbg -vfile:dbg -vFACILITY:clock2' 32 | # OVSDB_OPTS= 33 | {%- if openvswitch.get('logging', {}).ovsdb is defined %} 34 | {%- set _ovsdb_opts = [] %} 35 | {%- for opt in ['console', 'file', 'syslog'] %} 36 | {%- if openvswitch.logging.ovsdb.get(opt) %} 37 | {%- do _ovsdb_opts.append("-v" + opt + ":" + openvswitch.logging.ovsdb.get(opt)) %} 38 | {%- endif %} 39 | {%- endfor %} 40 | {%- if openvswitch.logging.ovsdb.facility is defined %} 41 | {%- do _ovsdb_opts.append("-vFACILITY:" + openvswitch.logging.ovsdb.facility) %} 42 | {%- endif %} 43 | OVSDB_OPTS="{{ ' '.join(_ovsdb_opts) }}" 44 | {%- endif %} 45 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-2-3-2.yml: -------------------------------------------------------------------------------- 1 | # 2.3.2 Ensure rsh client is not installed 2 | # 3 | # Description 4 | # =========== 5 | # The rsh package contains the client commands for the rsh services. 6 | # 7 | # Rationale 8 | # ========= 9 | # These legacy clients contain numerous security exposures and have been 10 | # replaced with the more secure SSH package. Even if the server is removed, 11 | # it is best to ensure the clients are also removed to prevent users from 12 | # inadvertently attempting to use these commands and therefore exposing 13 | # their credentials. Note that removing the rsh package removes the 14 | # clients for rsh , rcp and rlogin . 15 | # 16 | # Audit 17 | # ===== 18 | # Run the following commands and verify rsh is not installed: 19 | # 20 | # dpkg -s rsh-client 21 | # dpkg -s rsh-redone-client 22 | # 23 | # Remediation 24 | # =========== 25 | # Run the following command to uninstall rsh : 26 | # 27 | # apt-get remove rsh-client rsh-redone-client 28 | # 29 | # Impact 30 | # ====== 31 | # Many insecure service clients are used as troubleshooting tools and in 32 | # testing environments. Uninstalling them can inhibit capability to test 33 | # and troubleshoot. If they are required it is advisable to remove the 34 | # clients after use to prevent accidental or intentional misuse. 35 | # 36 | # NOTE 37 | # ==== 38 | # It is not possible to remove rsh-client by means of SaltStack because 39 | # of the way SaltStack checks that package was really removed. 'rsh-client' 40 | # is "provided" by openssh-client package, and SaltStack thinks that 41 | # it is the same as 'rsh-client is installed'. So each time we try to 42 | # remove 'rsh-client' on a system where 'openssh-client' is installed 43 | # (that's almost every system), we got state failure. 44 | # This was fixed in upstream SaltStack in 2018, not sure where we start using 45 | # this version. Until that moment 'rsh-client' should remain unmanaged. 46 | # 47 | parameters: 48 | linux: 49 | system: 50 | package: 51 | # rsh-client: 52 | # version: removed 53 | rsh-redone-client: 54 | version: removed 55 | 56 | -------------------------------------------------------------------------------- /linux/system/sudo.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- if system.get('sudo', {}).get('enabled', False) %} 5 | 6 | {%- if system.get('sudo', {}).get('aliases', False) is mapping %} 7 | /etc/sudoers.d/90-salt-sudo-aliases: 8 | file.managed: 9 | - source: salt://linux/files/sudoer-aliases 10 | - template: jinja 11 | - user: root 12 | - group: root 13 | - mode: 440 14 | - defaults: 15 | aliases: {{ system.sudo.aliases|yaml }} 16 | - check_cmd: /usr/sbin/visudo -c -f 17 | {%- else %} 18 | /etc/sudoers.d/90-salt-sudo-aliases: 19 | file.absent: 20 | - name: /etc/sudoers.d/90-salt-sudo-aliases 21 | {%- endif %} 22 | 23 | 24 | {%- if system.get('sudo', {}).get('users', False) is mapping %} 25 | /etc/sudoers.d/91-salt-sudo-users: 26 | file.managed: 27 | - source: salt://linux/files/sudoer-users 28 | - template: jinja 29 | - user: root 30 | - group: root 31 | - mode: 440 32 | - defaults: 33 | users: {{ system.sudo.users|yaml }} 34 | - check_cmd: /usr/sbin/visudo -c -f 35 | {%- else %} 36 | /etc/sudoers.d/91-salt-sudo-users: 37 | file.absent: 38 | - name: /etc/sudoers.d/91-salt-sudo-users 39 | {%- endif %} 40 | 41 | {%- if system.get('sudo', {}).get('groups', False) is mapping %} 42 | /etc/sudoers.d/91-salt-sudo-groups: 43 | file.managed: 44 | - source: salt://linux/files/sudoer-groups 45 | - template: jinja 46 | - user: root 47 | - group: root 48 | - mode: 440 49 | - defaults: 50 | groups: {{ system.sudo.groups|yaml }} 51 | - check_cmd: /usr/sbin/visudo -c -f 52 | {%- else %} 53 | /etc/sudoers.d/91-salt-sudo-groups: 54 | file.absent: 55 | - name: /etc/sudoers.d/91-salt-sudo-groups 56 | {%- endif %} 57 | 58 | {%- else %} 59 | 60 | /etc/sudoers.d/90-salt-sudo-aliases: 61 | file.absent: 62 | - name: /etc/sudoers.d/90-salt-sudo-aliases 63 | 64 | /etc/sudoers.d/91-salt-sudo-users: 65 | file.absent: 66 | - name: /etc/sudoers.d/91-salt-sudo-users 67 | 68 | /etc/sudoers.d/91-salt-sudo-groups: 69 | file.absent: 70 | - name: /etc/sudoers.d/91-salt-sudo-groups 71 | 72 | {%- endif %} 73 | {%- endif %} 74 | -------------------------------------------------------------------------------- /linux/system/file.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | {%- if system.enabled %} 3 | 4 | {%- for file_name, file in system.file.items() %} 5 | 6 | linux_file_{{ file_name }}: 7 | {%- if file.absent is defined and file.absent is sameas true %} 8 | file.absent: 9 | 10 | {%- elif file.serialize is defined %} 11 | file.serialize: 12 | - formatter: {{ file.serialize }} 13 | {%- if file.contents is defined %} 14 | - dataset: {{ file.contents|json }} 15 | {%- elif file.contents_pillar is defined %} 16 | - dataset_pillar: {{ file.contents_pillar }} 17 | {%- endif %} 18 | 19 | {%- else %} 20 | file.managed: 21 | {%- if file.source is defined %} 22 | - source: {{ file.source }} 23 | {%- if file.hash is defined %} 24 | - source_hash: {{ file.hash }} 25 | {%- else %} 26 | - skip_verify: True 27 | {%- endif %} 28 | {%- if file.template is defined %} 29 | - template: {{ file.template }} 30 | {%- if file.defaults is defined %} 31 | - defaults: {{ file.defaults|json }} 32 | {%- endif %} 33 | {%- if file.context is defined %} 34 | - context: {{ file.context|json }} 35 | {%- endif %} 36 | {%- endif %} 37 | {%- elif file.contents is defined %} 38 | - contents: {{ file.contents|json }} 39 | {%- elif file.contents_pillar is defined %} 40 | - contents_pillar: {{ file.contents_pillar }} 41 | {%- elif file.contents_grains is defined %} 42 | - contents_grains: {{ file.contents_grains }} 43 | {%- endif %} 44 | 45 | {%- endif %} 46 | {%- if file.name is defined %} 47 | - name: {{ file.name }} 48 | {%- else %} 49 | - name: {{ file_name }} 50 | {%- endif %} 51 | - makedirs: {{ file.get('makedirs', 'True') }} 52 | - replace: {{ file.get('replace', 'True') }} 53 | - user: {{ file.get('user', 'root') }} 54 | - group: {{ file.get('group', 'root') }} 55 | {%- if file.mode is defined %} 56 | - mode: {{ file.mode }} 57 | {%- endif %} 58 | {%- if file.dir_mode is defined %} 59 | - dir_mode: {{ file.dir_mode }} 60 | {%- endif %} 61 | {%- if file.encoding is defined %} 62 | - encoding: {{ file.encoding }} 63 | {%- endif %} 64 | 65 | {%- endfor %} 66 | 67 | {%- endif %} 68 | -------------------------------------------------------------------------------- /tests/pillar/network_openvswitch_dpdk.sls: -------------------------------------------------------------------------------- 1 | linux: 2 | system: 3 | enabled: true 4 | domain: local 5 | name: linux 6 | network: 7 | enabled: true 8 | hostname: test01 9 | fqdn: test01.local 10 | network_manager: false 11 | bridge: openvswitch 12 | dpdk: 13 | enabled: true 14 | driver: uio 15 | openvswitch: 16 | pmd_cpu_mask: "0x6" 17 | dpdk_socket_mem: "1024" 18 | dpdk_lcore_mask: "0x400" 19 | memory_channels: "2" 20 | vhost_socket_dir: 21 | name: "openvswitch-vhost" 22 | path: "/run/openvswitch-vhost" 23 | interface: 24 | eth0: 25 | enabled: true 26 | type: eth 27 | proto: manual 28 | ovs_bridge: br-prv 29 | dpdk0: 30 | name: enp5s0f1 31 | pci: "0000:05:00.1" 32 | driver: igb_uio 33 | bond: dpdkbond0 34 | enabled: true 35 | type: dpdk_ovs_port 36 | dpdk1: 37 | name: enp5s0f2 38 | pci: "0000:05:00.2" 39 | driver: igb_uio 40 | bond: dpdkbond0 41 | enabled: true 42 | type: dpdk_ovs_port 43 | dpdk2: 44 | name: enp6s0f1 45 | pci: "0000:06:00.1" 46 | driver: igb_uio 47 | bond: dpdkbond1 48 | enabled: true 49 | type: dpdk_ovs_port 50 | dpdk3: 51 | name: enp6s0f2 52 | pci: "0000:06:00.2" 53 | driver: igb_uio 54 | bond: dpdkbond1 55 | enabled: true 56 | type: dpdk_ovs_port 57 | dpdkbond0: 58 | enabled: true 59 | bridge: br-prv 60 | type: dpdk_ovs_bond 61 | mode: active-backup 62 | dpdkbond1: 63 | enabled: true 64 | bridge: br-mesh 65 | type: dpdk_ovs_bond 66 | mode: balance-slb 67 | br-prv: 68 | enabled: true 69 | type: dpdk_ovs_bridge 70 | br-mesh: 71 | tag: 1302 72 | enabled: true 73 | type: dpdk_ovs_bridge 74 | address: 1.2.3.4 75 | netmask: 255.255.255.252 76 | dummy0: 77 | enabled: true 78 | name: dummy0 79 | proto: manual 80 | ovs_port_type: OVSIntPort 81 | type: ovs_port 82 | ovs_bridge: br-prv 83 | bridge: br-prv 84 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: docker 4 | hostname: linux-formula 5 | run_options: -v /dev/log:/dev/log:ro 6 | 7 | provisioner: 8 | name: salt_solo 9 | salt_install: bootstrap 10 | salt_bootstrap_url: https://bootstrap.saltstack.com 11 | salt_version: latest 12 | require_chef: false 13 | log_level: error 14 | formula: linux 15 | grains: 16 | noservices: true 17 | state_top: 18 | base: 19 | "*": 20 | - linux 21 | pillars: 22 | top.sls: 23 | base: 24 | "*": 25 | - linux 26 | 27 | verifier: 28 | name: inspec 29 | sudo: true 30 | 31 | platforms: 32 | - name: <%=ENV['PLATFORM'] || 'saltstack-ubuntu-xenial-salt-stable' %> 33 | driver_config: 34 | image: <%=ENV['PLATFORM'] || 'epcim/salt:saltstack-ubuntu-xenial-salt-stable'%> 35 | platform: ubuntu 36 | 37 | 38 | suites: 39 | 40 | - name: network 41 | provisioner: 42 | pillars-from-files: 43 | linux.sls: tests/pillar/network.sls 44 | 45 | #- name: storage 46 | #provisioner: 47 | #pillars-from-files: 48 | #linux.sls: tests/pillar/storage.sls 49 | #init_environment: | 50 | #sudo mkdir -p /tmp/node 51 | #sudo dd if=/dev/zero of=/tmp/loop_dev0 bs=1024 count=$((30*1024)); 52 | #sudo dd if=/dev/zero of=/tmp/loop_dev1 bs=1024 count=$((30*1024)); 53 | #sudo dd if=/dev/zero of=/tmp/loop_dev2 bs=1024 count=$((30*1024)); 54 | #sudo dd if=/dev/zero of=/tmp/loop_dev3 bs=1024 count=$((30*1024)); 55 | #sudo dd if=/dev/zero of=/tmp/loop_dev4 bs=1024 count=$((30*1024)); 56 | #sudo mkfs.ext4 /tmp/loop_dev1 57 | #sudo mkswap /tmp/loop_dev2 58 | #sudo chown root /tmp/loop_dev*; 59 | #sudo chmod 0600 /tmp/loop_dev*; 60 | 61 | - name: system 62 | provisioner: 63 | pillars-from-files: 64 | linux.sls: tests/pillar/system.sls 65 | 66 | - name: system_file 67 | provisioner: 68 | pillars-from-files: 69 | linux.sls: tests/pillar/system_file.sls 70 | pillars_from_directories: 71 | - source: tests/example 72 | dest: srv/salt/linux/files/test 73 | 74 | - name: duo 75 | provisioner: 76 | pillars-from-files: 77 | linux.sls: tests/pillar/system_duo.sls 78 | 79 | # vim: ft=yaml sw=2 ts=2 sts=2 tw=125 80 | -------------------------------------------------------------------------------- /linux/storage/lvm.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import storage with context %} 2 | {%- if storage.enabled %} 3 | 4 | linux_lvm_pkgs: 5 | pkg.installed: 6 | - pkgs: {{ storage.lvm_pkgs | json }} 7 | 8 | 9 | /etc/lvm/lvm.conf: 10 | file.managed: 11 | - source: salt://linux/files/lvm.conf 12 | - template: jinja 13 | - require: 14 | - pkg: linux_lvm_pkgs 15 | 16 | lvm_services: 17 | service.running: 18 | - enable: true 19 | - names: {{ storage.lvm_services }} 20 | - require: 21 | - file: /etc/lvm/lvm.conf 22 | - watch: 23 | - file: /etc/lvm/lvm.conf 24 | 25 | {%- for vgname, vg in storage.lvm.items() %} 26 | 27 | {%- if vg.get('enabled', True) %} 28 | 29 | {%- for dev in vg.devices %} 30 | lvm_{{ vg.get('name', vgname) }}_pv_{{ dev }}: 31 | lvm.pv_present: 32 | - name: {{ dev }} 33 | - require: 34 | - pkg: linux_lvm_pkgs 35 | - file: /etc/lvm/lvm.conf 36 | - service: lvm_services 37 | - require_in: 38 | - lvm: lvm_vg_{{ vg.get('name', vgname) }} 39 | {%- endfor %} 40 | 41 | lvm_vg_{{ vg.get('name', vgname) }}: 42 | lvm.vg_present: 43 | - name: {{ vg.get('name', vgname) }} 44 | - devices: {{ vg.devices|join(',') }} 45 | 46 | {%- for lvname, volume in vg.get('volume', {}).items() %} 47 | 48 | lvm_{{ vg.get('name', vgname) }}_lv_{{ volume.get('name', lvname) }}: 49 | lvm.lv_present: 50 | - order: 1 51 | - name: {{ volume.get('name', lvname) }} 52 | - vgname: {{ vg.get('name', vgname) }} 53 | - size: {{ volume.size }} 54 | {%- if (volume.force is defined and volume.force is sameas true) or 55 | (volume.get('name', lvname) not in grains.lvm[vg.get('name', vgname)]) %} 56 | - force: True 57 | {%- else %} 58 | - force: False 59 | {%- endif %} 60 | - require: 61 | - lvm: lvm_vg_{{ vg.get('name', vgname) }} 62 | {%- if (volume.mount is defined) and 63 | (volume.get('name', lvname) not in grains.lvm[vg.get('name', vgname)]) %} 64 | - require_in: 65 | - mount: {{ volume.mount.path }} 66 | {%- if not volume.mount.get('file_system', None) in ['nfs', 'nfs4', 'cifs', 'tmpfs', None] %} 67 | - cmd: mkfs_{{ volume.mount.device}} 68 | {%- endif %} 69 | {%- endif %} 70 | 71 | {%- endfor %} 72 | 73 | {%- endif %} 74 | 75 | {%- endfor %} 76 | 77 | {%- endif %} 78 | -------------------------------------------------------------------------------- /linux/system/cron.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context %} 2 | 3 | {%- if system.cron.enabled is defined and system.cron.enabled %} 4 | 5 | cron_packages: 6 | pkg.installed: 7 | - names: {{ system.cron.pkgs }} 8 | 9 | cron_services: 10 | service.running: 11 | - enable: true 12 | - names: {{ system.cron.services }} 13 | - require: 14 | - pkg: cron_packages 15 | {%- if grains.get('noservices') %} 16 | - onlyif: /bin/false 17 | {%- endif %} 18 | 19 | {%- set allow_users = [] %} 20 | {%- for user_name, user_params in system.cron.get('user', {}).items() %} 21 | {%- set user_enabled = user_params.get('enabled', false) and 22 | system.get('user', {}).get( 23 | user_name, {'enabled': true}).get('enabled', true) %} 24 | {%- if user_enabled %} 25 | {%- do allow_users.append(user_name) %} 26 | {%- endif %} 27 | {%- endfor %} 28 | 29 | etc_cron_allow: 30 | {%- if allow_users %} 31 | file.managed: 32 | - name: /etc/cron.allow 33 | - template: jinja 34 | - source: salt://linux/files/cron_users.jinja 35 | - user: root 36 | - group: crontab 37 | - mode: 0640 38 | - defaults: 39 | users: {{ allow_users | yaml }} 40 | - require: 41 | - cron_packages 42 | {%- else %} 43 | file.absent: 44 | - name: /etc/cron.allow 45 | {%- endif %} 46 | 47 | {# 48 | /etc/cron.deny should be absent to comply with 49 | CIS 5.1.8 Ensure at/cron is restricted to authorized users 50 | #} 51 | etc_cron_deny: 52 | file.absent: 53 | - name: /etc/cron.deny 54 | 55 | etc_crontab: 56 | file.managed: 57 | - name: /etc/crontab 58 | - user: root 59 | - group: root 60 | - mode: 0600 61 | - replace: False 62 | - require: 63 | - cron_packages 64 | 65 | etc_cron_dirs: 66 | file.directory: 67 | - names: 68 | - /etc/cron.d 69 | - /etc/cron.daily 70 | - /etc/cron.hourly 71 | - /etc/cron.monthly 72 | - /etc/cron.weekly 73 | - user: root 74 | - group: root 75 | - dir_mode: 0600 76 | - recurse: 77 | - ignore_files 78 | - require: 79 | - cron_packages 80 | 81 | {%- else %} 82 | 83 | fake_linux_system_cron: 84 | test.nop: 85 | - comment: Fake state to satisfy 'require sls:linux.system.cron' 86 | 87 | {%- endif %} 88 | -------------------------------------------------------------------------------- /linux/files/modprobe.conf.jinja: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import system with context -%} 2 | # This file is managed by Salt, do not edit. 3 | {%- set module_content = system.kernel.module.get(module_name) %} 4 | {%- if module_content.get('blacklist', false) %} 5 | blacklist {{ module_name }} 6 | {%- endif %} 7 | {%- for alias, params in module_content.get('alias', {}) | dictsort %} 8 | {%- if params.get('enabled', true) %} 9 | alias {{ alias }} {{ module_name }} 10 | {%- endif %} 11 | {%- endfor %} 12 | {%- set options = [] %} 13 | {%- for option, params in module_content.get('option', {}) | dictsort %} 14 | {%- if params is mapping %} 15 | {%- if params.get('enabled', true) and params.value is defined %} 16 | {%- do options.append(option ~ '=' ~ params.value) %} 17 | {%- endif %} 18 | {%- else %} 19 | {%- do options.append(option ~ '=' ~ params) %} 20 | {%- endif %} 21 | {%- endfor %} 22 | {%- if options | length > 0 %} 23 | options {{ module_name }} {{ options | join(' ')}} 24 | {%- endif %} 25 | {%- if module_content.install is defined %} 26 | {%- if module_content.install.get('enabled', true) and module_content.install.command is defined %} 27 | install {{ module_name }} {{ module_content.install.command }} 28 | {%- endif %} 29 | {%- endif %} 30 | {%- if module_content.remove is defined %} 31 | {%- if module_content.remove.get('enabled', true) and module_content.remove.command is defined %} 32 | remove {{ module_name }} {{ module_content.remove.command }} 33 | {%- endif %} 34 | {%- endif %} 35 | {%- if module_content.softdep is defined %} 36 | {%- set pre = [] %} 37 | {%- set post = [] %} 38 | {%- for pos, params in module_content.softdep.get('pre', {}) | dictsort %} 39 | {%- if params.get('enabled', true) and params.value is defined %} 40 | {%- do pre.append(params.value) %} 41 | {%- endif %} 42 | {%- endfor %} 43 | {%- for pos, params in module_content.softdep.get('post', {}) | dictsort %} 44 | {%- if params.get('enabled', true) and params.value is defined %} 45 | {%- do post.append(params.value) %} 46 | {%- endif %} 47 | {%- endfor %} 48 | {%- if pre | length + post | length > 0 %} 49 | softdep {{ module_name }}{% if pre | length > 0 %} pre: {{ pre | join(' ') }}{% endif %}{% if post | length > 0 %} post: {{ post | join(' ') }}{% endif %} 50 | {%- endif %} 51 | {%- endif %} 52 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-5-4-4.yml: -------------------------------------------------------------------------------- 1 | # CIS 5.4.4 Ensure default user umask is 027 or more restrictive (Scored) 2 | # 3 | # Description 4 | # =========== 5 | # The default umask determines the permissions of files created by users. 6 | # The user creating the file has the discretion of making their files and 7 | # directories readable by others via the chmod command. Users who wish to 8 | # allow their files and directories to be readable by others by default may 9 | # choose a different default umask by inserting the umask command into the 10 | # standard shell configuration files ( .profile , .bashrc , etc.) in their 11 | # home directories. 12 | # 13 | # Rationale 14 | # ========= 15 | # Setting a very secure default value for umask ensures that users make a 16 | # conscious choice about their file permissions. A default umask setting of 17 | # 077 causes files and directories created by users to not be readable by 18 | # any other user on the system. A umask of 027 would make files and 19 | # directories readable by users in the same Unix group, while a umask of 022 20 | # would make files readable by every user on the system. 21 | # 22 | # Audit 23 | # ===== 24 | # Run the following commands and verify all umask lines returned are 027 or 25 | # more restrictive. 26 | # 27 | # # grep "^umask" /etc/bash.bashrc 28 | # umask 027 29 | # # grep "^umask" /etc/profile 30 | # umask 027 31 | # 32 | # Remediation 33 | # =========== 34 | # Edit the /etc/bash.bashrc and /etc/profile files (and the appropriate files 35 | # for any other shell supported on your system) and add or edit any umask 36 | # parameters as follows: 37 | # 38 | # umask 027 39 | # 40 | # Notes 41 | # ===== 42 | # The audit and remediation in this recommendation apply to bash and shell. 43 | # If other shells are supported on the system, it is recommended that their 44 | # configuration files also are checked. 45 | # 46 | # Other methods of setting a default user umask exist however the shell 47 | # configuration files are the last run and will override other settings if 48 | # they exist therefore our recommendation is to configure in the shell 49 | # configuration files. If other methods are in use in your environment they 50 | # should be audited and the shell configs should be verified to not override. 51 | # 52 | parameters: 53 | linux: 54 | system: 55 | shell: 56 | umask: "027" 57 | 58 | -------------------------------------------------------------------------------- /metadata/service/system/cis/cis-3-2-1.yml: -------------------------------------------------------------------------------- 1 | # 3.2.1 Ensure source routed packets are not accepted 2 | # 3 | # Description 4 | # =========== 5 | # In networking, source routing allows a sender to partially or fully specify 6 | # the route packets take through a network. In contrast, non-source routed 7 | # packets travel a path determined by routers in the network. In some cases, 8 | # systems may not be routable or reachable from some locations (e.g. private 9 | # addresses vs. Internet routable), and so source routed packets would need 10 | # to be used. 11 | # 12 | # Rationale 13 | # ========= 14 | # Setting `net.ipv4.conf.all.accept_source_route` and 15 | # `net.ipv4.conf.default.accept_source_route` to 0 disables the system from 16 | # accepting source routed packets. Assume this system was capable of routing 17 | # packets to Internet routable addresses on one interface and private addresses 18 | # on another interface. Assume that the private addresses were not routable to 19 | # the Internet routable addresses and vice versa. Under normal routing 20 | # circumstances, an attacker from the Internet routable addresses could not use 21 | # the system as a way to reach the private address systems. If, however, source 22 | # routed packets were allowed, they could be used to gain access to the private 23 | # address systems as the route could be specified, rather than rely on routing 24 | # protocols that did not allow this routing. 25 | # 26 | # Audit 27 | # ===== 28 | # 29 | # Run the following commands and verify output matches: 30 | # 31 | # # sysctl net.ipv4.conf.all.accept_source_route 32 | # net.ipv4.conf.all.accept_source_route = 0 33 | # # sysctl net.ipv4.conf.default.accept_source_route 34 | # net.ipv4.conf.default.accept_source_route = 0 35 | # 36 | # Remediation 37 | # =========== 38 | # 39 | # Set the following parameters in the /etc/sysctl.conf file: 40 | # 41 | # net.ipv4.conf.all.accept_source_route = 0 42 | # net.ipv4.conf.default.accept_source_route = 0 43 | # 44 | # Run the following commands to set the active kernel parameters: 45 | # 46 | # # sysctl -w net.ipv4.conf.all.accept_source_route=0 47 | # # sysctl -w net.ipv4.conf.default.accept_source_route=0 48 | # # sysctl -w net.ipv4.route.flush=1 49 | 50 | parameters: 51 | linux: 52 | system: 53 | kernel: 54 | sysctl: 55 | net.ipv4.conf.all.accept_source_route: 0 56 | net.ipv4.conf.default.accept_source_route: 0 57 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7.13" 4 | sudo: required 5 | services: 6 | - docker 7 | 8 | addons: 9 | apt: 10 | packages: 11 | - apt-transport-https 12 | 13 | install: 14 | - pip install PyYAML 15 | - pip install virtualenv 16 | - | 17 | test -e Gemfile || cat < Gemfile 18 | source 'https://rubygems.org' 19 | gem 'rake' 20 | gem 'test-kitchen' 21 | gem 'kitchen-docker' 22 | gem 'kitchen-inspec' 23 | gem 'inspec', '<3.0.0' 24 | #Version was frozen, because of issues in the version of inspec >3.0.0 -- see https://mirantis.jira.com/browse/PROD-24324 for more info 25 | gem 'kitchen-salt', :git => 'https://github.com/salt-formulas/kitchen-salt.git' 26 | - bundle install 27 | 28 | env: 29 | - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2017.7 SUITE=network 30 | - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2017.7 SUITE=system 31 | - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2018.3 SUITE=network 32 | - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2018.3 SUITE=system 33 | - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2018.3 SUITE=duo 34 | - PLATFORM=netmanagers/salt-2019.2-py3:ubuntu-18.04 SUITE=network 35 | # - PLATFORM=netmanagers/salt-2019.2-py3:ubuntu-18.04 SUITE=system 36 | # - PLATFORM=netmanagers/salt-2019.2-py3:ubuntu-18.04 SUITE=duo 37 | # - PLATFORM=epcim/salt:saltstack-ubuntu-bionic-salt-2017.7 SUITE=network 38 | # - PLATFORM=epcim/salt:saltstack-ubuntu-bionic-salt-2017.7 SUITE=system 39 | # - PLATFORM=epcim/salt:saltstack-ubuntu-bionic-salt-2018.3 SUITE=network 40 | # - PLATFORM=epcim/salt:saltstack-ubuntu-bionic-salt-2018.3 SUITE=system 41 | # - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2016.3 SUITE=system 42 | # - PLATFORM=epcim/salt:saltstack-ubuntu-xenial-salt-2016.3 SUITE=network 43 | before_script: 44 | - set -o pipefail 45 | - make test | tail 46 | 47 | script: 48 | - test ! -e .kitchen.yml || bundle exec kitchen converge ${SUITE} || true 49 | - test ! -e .kitchen.yml || bundle exec kitchen verify ${SUITE} -t tests/integration 50 | 51 | notifications: 52 | webhooks: 53 | urls: 54 | - https://webhooks.gitter.im/e/6123573504759330786b 55 | on_success: change # options: [always|never|change] default: always 56 | on_failure: never # options: [always|never|change] default: always 57 | on_start: never # options: [always|never|change] default: always 58 | on_cancel: never # options: [always|never|change] default: always 59 | on_error: never # options: [always|never|change] default: always 60 | email: false 61 | -------------------------------------------------------------------------------- /linux/files/pam-sshd: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import auth with context %} 2 | 3 | # PAM configuration for the Secure Shell service 4 | 5 | {%- if auth.duo.enabled %} 6 | auth required /lib64/security/pam_duo.so 7 | account required pam_nologin.so 8 | 9 | # Standard Un*x authentication. 10 | #@include common-auth 11 | {%- else %} 12 | # Standard Un*x authentication. 13 | @include common-auth 14 | {%- endif %} 15 | 16 | # Disallow non-root logins when /etc/nologin exists. 17 | account required pam_nologin.so 18 | 19 | # Uncomment and edit /etc/security/access.conf if you need to set complex 20 | # access limits that are hard to express in sshd_config. 21 | # account required pam_access.so 22 | 23 | # Standard Un*x authorization. 24 | @include common-account 25 | 26 | # SELinux needs to be the first session rule. This ensures that any 27 | # lingering context has been cleared. Without this it is possible that a 28 | # module could execute code in the wrong domain. 29 | session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close 30 | 31 | # Set the loginuid process attribute. 32 | session required pam_loginuid.so 33 | 34 | # Create a new session keyring. 35 | session optional pam_keyinit.so force revoke 36 | 37 | # Standard Un*x session setup and teardown. 38 | @include common-session 39 | 40 | # Print the message of the day upon successful login. 41 | # This includes a dynamically generated part from /run/motd.dynamic 42 | # and a static (admin-editable) part from /etc/motd. 43 | session optional pam_motd.so motd=/run/motd.dynamic 44 | session optional pam_motd.so noupdate 45 | 46 | # Print the status of the user's mailbox upon successful login. 47 | session optional pam_mail.so standard noenv # [1] 48 | 49 | # Set up user limits from /etc/security/limits.conf. 50 | session required pam_limits.so 51 | 52 | 53 | # Read environment variables from /etc/environment and 54 | # /etc/security/pam_env.conf. 55 | session required pam_env.so # [1] 56 | # In Debian 4.0 (etch), locale-related environment variables were moved to 57 | # /etc/default/locale, so read that as well. 58 | session required pam_env.so user_readenv=1 envfile=/etc/default/locale 59 | 60 | # SELinux needs to intervene at login time to ensure that the process starts 61 | # in the proper default security context. Only sessions which are intended 62 | # to run in the user's context should be run after this. 63 | session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open 64 | 65 | # Standard Un*x password updating. 66 | @include common-password 67 | 68 | -------------------------------------------------------------------------------- /_states/ovs_config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Management of Open vSwitch configuration 4 | ======================================== 5 | 6 | The OVS config can be managed with the ovs_config state module: 7 | 8 | .. code-block:: yaml 9 | 10 | other_config:dpdk-init: 11 | ovs_config.present: 12 | - value: True 13 | 14 | other_config:dpdk-extra: 15 | ovs_config.present: 16 | - value: -n 12 --vhost-owner libvirt-qemu:kvm --vhost-perm 0664 17 | 18 | external_ids: 19 | ovs_config.absent 20 | ''' 21 | 22 | 23 | def __virtual__(): 24 | ''' 25 | Only make these states available if Open vSwitch is installed. 26 | ''' 27 | return 'ovs_config.list' in __salt__ 28 | 29 | 30 | def present(name, value, wait=True): 31 | ''' 32 | Ensures that the named config exists, eventually creates it. 33 | 34 | Args: 35 | name/value: The name/value of the config entry. 36 | wait: Whether wait for ovs-vswitchd to reconfigure itself according to the modified database. 37 | ''' 38 | ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} 39 | ovs_config = __salt__['ovs_config.list']() 40 | 41 | if name in ovs_config and ovs_config[name] == str(value).lower(): 42 | ret['result'] = True 43 | ret['comment'] = '{0} is already set to {1}.'.format(name, value) 44 | else: 45 | config_updated = __salt__['ovs_config.set'](name, value, wait) 46 | if config_updated: 47 | ret['result'] = True 48 | ret['comment'] = '{0} is updated.'.format(name) 49 | ret['changes'] = { name: 'Updated to {0}'.format(value) } 50 | else: 51 | ret['result'] = False 52 | ret['comment'] = 'Unable to update config of {0}.'.format(name) 53 | 54 | return ret 55 | 56 | 57 | def absent(name): 58 | ''' 59 | Ensures that the named config does not exist, eventually deletes it. 60 | 61 | Args: 62 | name: The name of the config entry. 63 | 64 | ''' 65 | ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} 66 | ovs_config = __salt__['ovs_config.list']() 67 | 68 | if ':' in name and name not in ovs_config: 69 | ret['result'] = True 70 | ret['comment'] = '{0} does not exist.'.format(name) 71 | else: 72 | config_removed = __salt__['ovs_config.remove'](name) 73 | if config_removed: 74 | ret['result'] = True 75 | ret['comment'] = '{0} is removed.'.format(name) 76 | ret['changes'] = { name: '{0} removed'.format(name) } 77 | else: 78 | ret['result'] = False 79 | ret['comment'] = 'Unable to delete config of {0}.'.format(name) 80 | 81 | return ret 82 | -------------------------------------------------------------------------------- /linux/meta/graphite.yml: -------------------------------------------------------------------------------- 1 | rewrite_rule: 2 | "\.cpu-idle\.value$": ".idle" 3 | "\.cpu-interrupt\.value$": ".interrupt" 4 | "\.cpu-nice\.value$": ".nice" 5 | "\.cpu-softirq\.value$": ".softirq" 6 | "\.cpu-steal\.value$": ".steal" 7 | "\.cpu-system\.value$": ".system" 8 | "\.cpu-user\.value$": ".user" 9 | "\.cpu-wait\.value$": ".wait" 10 | "\.cpu\.idle$": ".idle" 11 | "\.cpu\.interrupt$": ".interrupt" 12 | "\.cpu\.nice$": ".nice" 13 | "\.cpu\.softirq$": ".softirq" 14 | "\.cpu\.steal$": ".steal" 15 | "\.cpu\.system$": ".system" 16 | "\.cpu\.user$": ".user" 17 | "\.cpu\.wait$": ".wait" 18 | "\.cpu-idle$": ".idle" 19 | "\.cpu-interrupt$": ".interrupt" 20 | "\.cpu-nice$": ".nice" 21 | "\.cpu-softirq$": ".softirq" 22 | "\.cpu-steal$": ".steal" 23 | "\.cpu-system$": ".system" 24 | "\.cpu-user$": ".user" 25 | "\.cpu-wait$": ".wait" 26 | "\.cpu-": ".cpu." 27 | "\.df_complex-free\.value$": ".free" 28 | "\.df_complex-reserved\.value$": ".reserved" 29 | "\.df_complex-used\.value$": ".used" 30 | 31 | "\.df_complex-free$": ".free" 32 | "\.df_complex-reserved$": ".reserved" 33 | "\.df_complex-used$": ".used" 34 | "\.df_complex\.free$": ".free" 35 | "\.df_complex\.reserved$": ".reserved" 36 | "\.df_complex\.used$": ".used" 37 | "\.df-": ".partition." 38 | "\.df\.": ".partition." 39 | 40 | "\.disk_merged\.read$": ".merged_reads" 41 | "\.disk_merged\.write$": ".merged_writes" 42 | "\.disk_octets\.read$": ".read_octets" 43 | "\.disk_octets\.write$": ".write_octets" 44 | "\.disk_ops\.read$": ".read_ops" 45 | "\.disk_ops\.write$": ".write_ops" 46 | "\.disk_time\.read$": ".read_time" 47 | "\.disk_time\.write$": ".write_time" 48 | "\.disk-": ".disk." 49 | 50 | "\.entropy\.entropy\.value$": ".system.entropy" 51 | "\.entropy\.entropy$": ".system.entropy" 52 | 53 | "\.if_errors\.tx": ".tx_errors" 54 | "\.if_errors\.rx": ".rx_errors" 55 | "\.if_octets\.tx": ".tx_octets" 56 | "\.if_octets\.rx": ".rx_octets" 57 | "\.if_packets\.tx": ".tx_packets" 58 | "\.if_packets\.rx": ".rx_packets" 59 | "\.interface-": ".interface." 60 | 61 | "load\.load\.midterm$": "load.mid_term" 62 | "load\.load\.shortterm$": "load.short_term" 63 | "load\.load\.longterm$": "load.long_term" 64 | 65 | "memory\.memory-buffered\.value$": "memory.buffered" 66 | "memory\.memory-cached\.value$": "memory.cached" 67 | "memory\.memory-free\.value$": "memory.free" 68 | "memory\.memory-used\.value$": "memory.used" 69 | "memory\.memory\.buffered$": "memory.buffered" 70 | "memory\.memory\.cached$": "memory.cached" 71 | "memory\.memory\.free$": "memory.free" 72 | "memory\.memory\.used$": "memory.used" 73 | "memory\.memory-buffered$": "memory.buffered" 74 | "memory\.memory-cached$": "memory.cached" 75 | "memory\.memory-free$": "memory.free" 76 | "memory\.memory-used$": "memory.used" 77 | -------------------------------------------------------------------------------- /linux/storage/swap.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import storage with context %} 2 | {%- if storage.enabled %} 3 | 4 | {%- if storage.swap.enabled is not defined or storage.swap.enabled %} 5 | 6 | {%- for swap_name, swap in storage.swap.items() %} 7 | 8 | {%- if swap.enabled %} 9 | 10 | {%- if swap.engine == 'partition' %} 11 | 12 | linux_create_swap_partition_{{ swap.device }}: 13 | cmd.run: 14 | - name: 'mkswap {{ swap.device }}' 15 | - unless: file -L -s {{ swap.device }} | grep -q 'swap file' 16 | 17 | linux_set_swap_partition_{{ swap.device }}: 18 | cmd.run: 19 | - name: 'swapon {{ swap.device }}' 20 | - unless: grep $(readlink -f {{ swap.device }}) /proc/swaps 21 | - require: 22 | - cmd: linux_create_swap_partition_{{ swap.device }} 23 | 24 | {{ swap.device }}: 25 | mount.swap: 26 | - persist: True 27 | - require: 28 | - cmd: linux_set_swap_partition_{{ swap.device }} 29 | 30 | {%- elif swap.engine == 'file' %} 31 | 32 | linux_create_swap_file_{{ swap.device }}: 33 | cmd.run: 34 | - name: 'dd if=/dev/zero of={{ swap.device }} bs=1048576 count={{ swap.size }} && chmod 0600 {{ swap.device }}' 35 | - creates: {{ swap.device }} 36 | 37 | linux_set_swap_file_{{ swap.device }}: 38 | cmd.wait: 39 | - name: 'mkswap {{ swap.device }}' 40 | - watch: 41 | - cmd: linux_create_swap_file_{{ swap.device }} 42 | 43 | linux_set_swap_file_status_{{ swap.device }}: 44 | cmd.run: 45 | - name: 'swapon {{ swap.device }}' 46 | - unless: grep {{ swap.device }} /proc/swaps 47 | - require: 48 | - cmd: linux_set_swap_file_{{ swap.device }} 49 | 50 | {{ swap.device }}: 51 | mount.swap: 52 | - persist: True 53 | - require: 54 | - cmd: linux_set_swap_file_{{ swap.device }} 55 | 56 | {%- endif %} 57 | 58 | {%- else %} 59 | 60 | {{ swap.device }}: 61 | module.run: 62 | {%- if 'module.run' in salt['config.get']('use_superseded', default=[]) %} 63 | - mount.rm_fstab: 64 | - m_name: none 65 | - device: {{ swap.device }} 66 | {%- else %} 67 | - name: mount.rm_fstab 68 | - m_name: none 69 | - device: {{ swap.device }} 70 | {%- endif %} 71 | - onlyif: grep -q {{ swap.device }} /etc/fstab 72 | 73 | linux_disable_swap_{{ swap.engine }}_{{ swap.device }}: 74 | cmd.run: 75 | {%- if swap.engine == 'partition' %} 76 | - name: 'swapoff {{ swap.device }}' 77 | {%- elif swap.engine == 'file' %} 78 | - name: 'swapoff {{ swap.device }} && rm -f {{ swap.device }}' 79 | {%- endif %} 80 | - onlyif: file -L -s {{ swap.device }} | grep -q 'swap file' 81 | 82 | {%- endif %} 83 | 84 | {%- endfor %} 85 | 86 | {%- elif storage.swap.enabled is defined and not storage.swap.enabled %} 87 | 88 | linux_disable_swap: 89 | cmd.run: 90 | - name: 'swapoff -a' 91 | 92 | {%- endif %} 93 | 94 | {%- endif %} 95 | -------------------------------------------------------------------------------- /linux/network/host.sls: -------------------------------------------------------------------------------- 1 | {%- from "linux/map.jinja" import network with context %} 2 | {%- if network.enabled %} 3 | 4 | {%- set host_dict = network.host %} 5 | 6 | {%- if network.mine_dns_records %} 7 | 8 | {%- for node_name, node_grains in salt['mine.get']('*', 'grains.items').items() %} 9 | {%- if node_grains.get('dns_records', []) is iterable %} 10 | {%- for record in node_grains.get('dns_records', []) %} 11 | {%- set record_key = node_name ~ '-' ~ loop.index %} 12 | {%- do host_dict.update({ record_key: {'address': record.address, 'names': record.names} }) %} 13 | {%- endfor %} 14 | {%- endif %} 15 | {%- endfor %} 16 | 17 | {%- endif %} 18 | 19 | {%- if network.get('purge_hosts', false) %} 20 | 21 | linux_hosts: 22 | file.managed: 23 | - name: /etc/hosts 24 | - source: salt://linux/files/hosts 25 | - template: jinja 26 | - defaults: 27 | host_dict: {{ host_dict|yaml }} 28 | 29 | {%- else %} 30 | 31 | {%- for name, host in host_dict.items() %} 32 | 33 | {%- if host.names is defined %} 34 | 35 | {%- set clearers = [] %} 36 | {%- for etc_addr, etc_names in salt.hosts.list_hosts().items() %} 37 | {%- set names_to_clear = [] %} 38 | {%- for host_name in host.names %} 39 | {%- if (host.address != etc_addr) and host_name in etc_names %} 40 | {%- do names_to_clear.append(host_name) %} 41 | {%- endif %} 42 | {%- endfor %} 43 | {%- if names_to_clear != [] %} 44 | {%- set clearer = "linux_host_" + name + "_" + etc_addr + "_clear" %} 45 | {%- do clearers.append(clearer) %} 46 | 47 | {{ clearer }}: 48 | host.absent: 49 | - ip: {{ etc_addr }} 50 | - names: {{ names_to_clear }} 51 | 52 | {%- endif %} 53 | {%- endfor %} 54 | 55 | linux_host_{{ name }}: 56 | host.present: 57 | - ip: {{ host.address }} 58 | - names: {{ host.names }} 59 | - require: {{ clearers }} 60 | 61 | {%- if host.address in grains.ipv4 and host.names|length > 1 %} 62 | 63 | {%- if host.names.1 in host.names.0 %} 64 | {%- set before = host.names.1 + " " + host.names.0 %} 65 | {%- set after = host.names.0 + " " + host.names.1 %} 66 | {%- elif host.names.0 in host.names.1 %} 67 | {%- set before = host.names.0 + " " + host.names.1 %} 68 | {%- set after = host.names.1 + " " + host.names.0 %} 69 | {%- endif %} 70 | 71 | linux_host_{{ name }}_order_fix: 72 | module.run: 73 | {%- if 'module.run' in salt['config.get']('use_superseded', default=[]) %} 74 | - file.replace: 75 | - path: /etc/hosts 76 | - pattern: {{ before }} 77 | - repl: {{ after }} 78 | {%- else %} 79 | - name: file.replace 80 | - path: /etc/hosts 81 | - pattern: {{ before }} 82 | - repl: {{ after }} 83 | {%- endif %} 84 | - watch: 85 | - host: linux_host_{{ name }} 86 | - onlyif: 87 | - grep -q "{{ before }}" /etc/hosts 88 | 89 | {%- endif %} 90 | 91 | {%- endif %} 92 | 93 | {%- endfor %} 94 | 95 | {%- endif %} 96 | 97 | {%- endif %} 98 | -------------------------------------------------------------------------------- /_modules/ovs_config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Support for Open vSwitch database configuration. 4 | 5 | ''' 6 | from __future__ import absolute_import 7 | 8 | import logging 9 | import salt.utils.path 10 | 11 | log = logging.getLogger(__name__) 12 | 13 | 14 | def __virtual__(): 15 | ''' 16 | Only load the module if Open vSwitch is installed 17 | ''' 18 | if salt.utils.path.which('ovs-vsctl'): 19 | return 'ovs_config' 20 | return False 21 | 22 | 23 | def _retcode_to_bool(retcode): 24 | ''' 25 | Evaluates ovs-vsctl command`s retcode value. 26 | 27 | Args: 28 | retcode: Value of retcode field from response. 29 | ''' 30 | return True if retcode == 0 else False 31 | 32 | 33 | def set(cfg, value, wait=True): 34 | ''' 35 | Updates a specified configuration entry. 36 | 37 | Args: 38 | cfg/value: a config entry to update 39 | wait: wait or not for ovs-vswitchd to reconfigure itself before it exits. 40 | 41 | CLI Example: 42 | .. code-block:: bash 43 | 44 | salt '*' ovs_config.set other_config:dpdk-init true 45 | ''' 46 | wait = '' if wait else '--no-wait ' 47 | 48 | cmd = 'ovs-vsctl {0}set Open_vSwitch . {1}="{2}"'.format(wait, cfg, str(value).lower()) 49 | result = __salt__['cmd.run_all'](cmd) 50 | return _retcode_to_bool(result['retcode']) 51 | 52 | 53 | def remove(cfg): 54 | ''' 55 | Removes a specified configuration entry. 56 | 57 | Args: 58 | cfg: a config entry to remove 59 | 60 | CLI Example: 61 | .. code-block:: bash 62 | 63 | salt '*' ovs_config.remove other_config 64 | ''' 65 | if ':' in cfg: 66 | section, key = cfg.split(':') 67 | cmd = 'ovs-vsctl remove Open_vSwitch . {} {}'.format(section, key) 68 | else: 69 | cmd = 'ovs-vsctl clear Open_vSwitch . ' + cfg 70 | 71 | result = __salt__['cmd.run_all'](cmd) 72 | return _retcode_to_bool(result['retcode']) 73 | 74 | 75 | def list(): 76 | ''' 77 | Return a current config of Open vSwitch 78 | 79 | CLI Example: 80 | 81 | .. code-block:: bash 82 | 83 | salt '*' ovs_config.list 84 | ''' 85 | cmd = 'ovs-vsctl list Open_vSwitch .' 86 | result = __salt__['cmd.run_all'](cmd) 87 | 88 | if result['retcode'] == 0: 89 | config = {} 90 | for l in result['stdout'].splitlines(): 91 | cfg, value = map((lambda x: x.strip()), l.split(' : ')) 92 | if value.startswith('{') and len(value) > 2: 93 | for i in value[1:-1].replace('"', '').split(', '): 94 | _k, _v = i.split('=') 95 | config['{}:{}'.format(cfg,_k)] = _v 96 | else: 97 | config[cfg] = value 98 | 99 | return config 100 | else: 101 | return False 102 | --------------------------------------------------------------------------------