Example: Security Hardening

Security Hardening

SSH hardening, firewall rules (UFW), fail2ban, and audit logging configuration.

Directory Structure

cookbooks/
  security/
    default.rb
    templates/
      sshd_config.erb
      jail.local.erb
      audit.rules.erb
nodes/
  secure01.json

Node Attributes

{
  "security": {
    "ssh_port": 2222,
    "permit_root_login": "no",
    "password_authentication": "no",
    "allowed_users": ["alice", "bob", "deploy"],
    "ufw_allowed_ports": [2222, 80, 443],
    "fail2ban_maxretry": 3,
    "fail2ban_bantime": 3600,
    "fail2ban_findtime": 600
  }
}

Recipe

# cookbooks/security/default.rb

sec = node['security']

# --- SSH Hardening ---

template '/etc/ssh/sshd_config' do
  source 'templates/sshd_config.erb'
  owner 'root'
  group 'root'
  mode '0600'
  variables(
    port: sec['ssh_port'],
    permit_root_login: sec['permit_root_login'],
    password_authentication: sec['password_authentication'],
    allowed_users: sec['allowed_users']
  )
  notifies :restart, 'service[sshd]'
end

service 'sshd' do
  action [:enable, :start]
end

# --- Firewall (UFW) ---

package 'ufw' do
  action :install
end

execute 'ufw-default-deny' do
  command 'ufw default deny incoming'
  not_if 'ufw status | grep -q "Default: deny (incoming)"'
end

sec['ufw_allowed_ports'].each do |port|
  execute "ufw-allow-#{port}" do
    command "ufw allow #{port}/tcp"
    not_if "ufw status | grep -q '#{port}/tcp'"
  end
end

execute 'ufw-enable' do
  command 'ufw --force enable'
  not_if 'ufw status | grep -q "Status: active"'
end

# --- Fail2ban ---

package 'fail2ban' do
  action :install
end

template '/etc/fail2ban/jail.local' do
  source 'templates/jail.local.erb'
  owner 'root'
  group 'root'
  mode '0644'
  variables(
    ssh_port: sec['ssh_port'],
    maxretry: sec['fail2ban_maxretry'],
    bantime: sec['fail2ban_bantime'],
    findtime: sec['fail2ban_findtime']
  )
  notifies :restart, 'service[fail2ban]'
end

service 'fail2ban' do
  action [:enable, :start]
end

# --- Audit Logging ---

package 'auditd' do
  action :install
end

directory '/etc/audit/rules.d' do
  owner 'root'
  group 'root'
  mode '0750'
end

template '/etc/audit/rules.d/itamae.rules' do
  source 'templates/audit.rules.erb'
  owner 'root'
  group 'root'
  mode '0640'
  notifies :restart, 'service[auditd]'
end

service 'auditd' do
  action [:enable, :start]
end

Templates

sshd_config.erb

Port <%= @port %>
PermitRootLogin <%= @permit_root_login %>
PasswordAuthentication <%= @password_authentication %>
AllowUsers <%= @allowed_users.join(' ') %>
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2

jail.local.erb

[DEFAULT]
bantime = <%= @bantime %>
findtime = <%= @findtime %>
maxretry = <%= @maxretry %>

[sshd]
enabled = true
port = <%= @ssh_port %>
filter = sshd
logpath = /var/log/auth.log

audit.rules.erb

# Monitor authentication logs
-w /var/log/auth.log -p wa -k auth_log
# Monitor sudoers
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
# Monitor SSH config
-w /etc/ssh/sshd_config -p wa -k sshd_config

Running

itamae ssh -j nodes/secure01.json -h server01.example.com cookbooks/security/default.rb