Chef Cookbook: OSSEC (ng)
Find a file
2016-07-24 18:14:51 -04:00
attributes Added email_idsname, fixed email_alerts to not require location 2016-07-24 18:12:09 -04:00
libraries Added email_idsname, fixed email_alerts to not require location 2016-07-24 18:12:09 -04:00
recipes Fixed foodcritic issues, added proper cookbook includes per platform 2016-07-24 16:27:10 -04:00
templates/default Moved email_alerts definitions 2016-07-24 18:14:51 -04:00
.gitignore Initial commit as ossec-ng 2016-07-24 16:11:12 -04:00
Berksfile Initial commit as ossec-ng 2016-07-24 16:11:12 -04:00
metadata.rb Added email_idsname, fixed email_alerts to not require location 2016-07-24 18:12:09 -04:00
README.md Added email_idsname, fixed email_alerts to not require location 2016-07-24 18:12:09 -04:00

Description

Fully automated Installation and configuration of ossec-servers and ossec-agents Manage the key generation and distribution between a server and multiple agents Clean queues on the server if needed (rid)

Requirements

Any of:

  • Ubuntu 12.04+
  • Debian 7.0+
  • CentOS 6.0+ (should work with ossec systems if you have the packages)

Attributes

General Attributes

The attributes below follow the same namespace syntax that OSSEC does. Refer to the official OSSEC Documentation for more information.

Default attributes from the cookbook:

default["ossec"]["version"] = "2.8"
default["ossec"]["syslog_output"]["ip"] = "127.0.0.1"
default["ossec"]["syslog_output"]["port"] = "514"
default["ossec"]["syslog_output"]["min_level"] = "5"
default["ossec"]["receiver_port"] = "1514"
default["ossec"]["log_alert_level"] = "1"
default["ossec"]["email_alert_level"] = "7"
default["ossec"]["agents"] = {}

Default attributes from the ossec-server role:

"ossec" => {
  "email_notification" => 'yes',
  "email_to" => [
    'ossec@example.net',
  ],
  "email_from" => 'ossec-server@example.net',
  "email_idsname" => 'ossec',
  "smtp_server" => 'localhost',
  "white_list" => [
    '127.0.0.1',
    '10.1.0.0/16'
  ],
  "email_alerts" => {
    'recipient@example.net' => {
      'level' => '9',
      'group' => 'syscheck',
      'event_location_tag' => 'reputation',
      'event_location_search' => 'roles:*mongodb*',
      'format' => 'sms',
      'rule_id' => '100001',
      'tags' => [
        'do_not_delay',
        'do_not_group'
      ]
    }
  },
  "server" => {
    "service_name" => 'ossec-hids-server'
  },
  "syscheck" => {
    "frequency" => '7200',
    "alert_new_files" => 'yes',
    "auto_ignore" => 'no',
    "directories" => {
      '/bin' => {
        'report_changes' => 'no',
        'realtime' => 'yes'
      },
      '/sbin' => {
        'report_changes' => 'no',
        'realtime' => 'yes'
      },
      '/usr/bin' => {
        'report_changes' => 'no',
        'realtime' => 'yes'
      },
      '/usr/sbin' => {
        'report_changes' => 'no',
        'realtime' => 'yes'
      },
      '/etc' => {
        'report_changes' => 'yes',
        'realtime' => 'yes'
      },
      '/tmp' => {
        'report_changes' => 'yes',
        'realtime' => 'no'
      }
    },
    "ignore" => {
      '/etc/openvpn/openvpn-status.log' => {},
      '/etc/motd' => {},
      '/etc/mcollective/facts.yaml' => {},
      '/etc/blkid.tab' => {},
      '/etc/mtab' => {},
      '/etc/mail/statistics => {}',
      '/etc/random-seed' => {},
      '/etc/adjtime' => {},
      '/etc/prelink.cache' => {},
      '/etc/dnscache/stats' => {},
      '/etc/dnscache/log' => {},
      '/etc/dnscache2/stats' => {},
      '/etc/dnscache2/log' => {},
      '/etc/tinydns/stats' => {},
      '/etc/tinydns/log' => {}
    }
  },
  "syslog_files" => {
    '/var/log/syslog' => {},
    '/var/log/auth.log' => {},
    '/var/log/daemon.log' => {},
    '/var/log/kern.log' => {},
    '/var/log/mail.log' => {},
    '/var/log/user.log' => {},
    '/var/log/cron.log' => {}
  }

email_alerts is a hash of recipients and servers. Each recipient will receive all of the alert for the listed location (the list is a regex). event_location_tag must contain a valid chef tag. All the nodes listed by that tag will generate a separate email_alerts rule. This is additional to the default list email_to and is used to send alert to specific recipients for a limited number of hosts only.

Local Rules Definitions

Rules are defined in Ruby Hash format and replicate the XML format of regular OSSEC Rules Syntax Each rule has a head, a body, tags and info (the last 2 being optional)

head=   <rule id="12345" level="12" frequency="45" timeframe="60">
body=     <description>Test Rule</description>
body=     <match>Big Error</match>
body=     <hostname>server1</hostname>
tags=     <same_source_ip />
tags=     <same_source_port />
info=     <info type="link">http://IjustGotHacked.com</info>
        </rule>

The section below are parsed by the template. The following items are mandatory:

  • head/level
  • body/description
    "ossec" =>
      "rules" => {
        "100001" => {
          "head" => {
            "level" => "7",
            "maxsize" => "65536",
            "frequency" => "100",
            "timeframe" => "3600",
            "ignore" => "5",
            "overwrite" => "68321"
          },
          "body" => {
            "hostname_search" => "recipes:mms-agent",
            "description" => "Super Security Rule for application XYZ",
            "match" => "super dangerous error happened",
            "regex" => "^\d+Hello World$",
            "decoded_as" => "vsftpd",
            "category" => "windows",
            "srcip" => "192.168.1.254",
            "dstip" => "10.1.6.23",
            "user" => "bob",
            "program_name" => "nginx",
            "time" => "09:00-18:00",
            "weekday" => "monday,tuesday",
            "id" => "404",
            "url" => "/changepassword.php",
            "if_sid" => "100238",
            "if_group" => "authentication_success",
            "if_level" => "13",
            "if_matched_sid" => "12037",
            "if_matched_group" => "adduser",
            "if_matched_level" => "7",
            "options" => "no_email_alert",
            "check_diff" => "true",
            "group" => "syscheck"
          },
          "tags" => [
            "same_source_ip",
            "same_source_port",
            "same_dst_port",
            "same_location"
          ],
          "infos" => {
            "link" => "http://trac.example.net/ticket/12345",
            "text" => "the link above contains additional information"
          }
        }
      }

To the exception of hostname_search, all attributes use the same syntax as the ossec rule in XML format does. hostname_search in this cookbook represents a search query that is executed by the server recipe to populate the <hostname> with the proper list of hosts, dynamically pulled from chef. Search criterias can be anything that a chef search can take. Example: recipe:mongodb\:\:replicaset and tags:reputation

Local Decoders Definitions

Decoders are defined in JSON format and replicate the XML format of regular OSSEC Decoder Syntax

"ossec" => {
  "decoders" => {
    'apache-errorlog' => {
      "program_name" => '^httpd|^apache2',
      "prematch" => {
        "parser" => '^\S+ [\w+\s*\d+ \S+ \d+] [\S+] |^[warn] |^[notice] |^[error]'
      },

    },
    'apache-errorlog-ip-custom' => {
      "parent" => 'apache-errorlog',
      "prematch" => {
        "offset" => 'after_parent',
        "parser" => '^[client'
      },
      "regex" => {
        "offset" => 'after_prematch',
        "parser" => '^ (\d+.\d+.\d+.\d+)]'
      },
      "order" => 'srcip'
    },
    'web-accesslog-custom' => {
      "parent" => 'web-accesslog',
      "type" => 'web-log',
      "prematch" => {
        "parser" => '^\d+.\d+.\d+.\d+ |^::ffff:\d+.\d+.\d+.\d+'
      },
      "regex" => {
        "parser" => '^\d+.\d+.\d+.\d+ \S+ (\d+.\d+.\d+.\d+) \S+ \S+ \S+ [\S+ \S\d+] "\w+ (\S+) HTTP\S+ (\d+) \S+ "(\S+)"'
      },
      "order" => 'srcip, url, id, extra_data'
    }
  }
}

prematch and regex are hashes that can have an offset value and always have a parser value. See the ossec documentation for more information.

Local Syslog Files and Syscheck Ignore Files

If you want specific log files to be monitored on specific agents, you can use a syslog_files block in the agent node attributes. The apply_to parameter of this block is a Chef::Search() that will expand to a list of hosts. If the given agent belong to the list of hosts, it will add the logfile to its local ossec configuration. log_format will default to "syslog" if not set.

If you want to ignore specific files in syscheck, similarly you can use the same apply_to option as shown below:

default_attributes(
  "ossec" => {
    "syslog_files" => {
      '/var/log/supervisor/supervisor.log' => {
        'apply_to' => 'supervisor:*',
        'log_format' => 'syslog'
      }
    },
    "syscheck" => {
      "ignore" => {
        '/etc/openvpn/openvpn-status.log' => {
          'apply_to' => 'roles:vpn-server'
        },
        '^/opt/graphite/storage/' => {
          'apply_to' => 'roles:graphite-server OR roles:statsd-server',
          'type' => 'sregex'
        }
      }
    }
  }
)

OSSEC Rules Enable/Disable

You can also choose what rules OSSEC it configured to load. Each of the rules in /var/ossec/rules can be enabled and disabled at will with a very simple format. Note that these do not have an apply_to option and are just boolean values, this is because only the OSSEC server loads the rules into action. The following is an example that will enable the normally disabled symantec-av-rules.xml and disable the web_rules.xml:

default_attributes(
  "ossec" => {
    "load_rules" => {
      'symantec-av_rules.xml' => true,
      'web_rules.xml' => false
    }
  }
)

Commands and Auto-Response

Since OSSEC is an HIDS system, you can also enable and disable commands and active-response functionality and define your own as needed. You can enable or disable existing defined commands and active-responses using the enable flag, and target where they are set using the apply_to flag. You can also define any of the allowed active-response options dynamically, such as adding agent_id, rules_group, etc. Here is an example of enabling the firewall-stop command and its auto-response and changing it from the default local to all hosts and adding a rules_group to the active-response definition so it only triggers on authentication failures:

default_attributes(
  "ossec" => {
    "command" => {
      "firewall-stop" => {
        "enabled" => true
      }
    },
    "auto-response => {
      "firewall-stop" => {
        "enabled" => true,
        "location" => "all",
        "rules_group" => "authentication_failed,authentication_failures"
      }
    }
  }
)

Usage

  • recipe[ossec-server] should be a stand alone installation
  • recipe[ossec-agent] should be added (via role[ossec-agent]) to all the nodes of the environment

Example Roles

ossec-server

This role can be used to provision an ossec server:

name 'ossec-server'
description 'OSSEC Server'
run_list(
  'recipe[ossec-ng::server]',
  'role[postfix]'
)
override_attributes(
  "ossec" => {
    "agent" => {
      "enable" => false
    }
  }
)
default_attributes(
  "ossec" => {
    "email_notification" => 'yes',
    "email_to" => [
      'ossec-alerts@example.net',
    ],
    "email_from" => 'ossec-server',
    "smtp_server" => 'localhost',
    "white_list" => [
      '127.0.0.1',
      '10.0.0.0/0'
    ],
    "email_alerts" => {
      'bob@example.net' => {
        'event_location_tag' => 'project1',
      },
      'alice@example.net' => {
        'event_location_tag' => 'project1',
        'group' => 'developers',
      },
      'eve@example.net' => {
        'event_location_tag' => 'project2',
        'group' => 'developers',
      },
      'mike@example.net' => {
        'event_location_search' => 'tags:project1 OR tags:project2 OR tags:project3',
        'group' => 'developers',
      },
      'group2@example.net' => {
        'event_location_search' => 'roles:application-server AND roles:python-django',
        'group' => 'frontend-group',
      },
    },
    "decoders" => {
      1 => {
        "name" => 'apache-errorlog',
        "program_name" => '^httpd|^apache2',
        "prematch" => {
          "parser" => '^\S+ [\w+\s*\d+ \S+ \d+] [\S+] |^[warn] |^[notice] |^[error]'
        },

      },
      2 => {
        "name" => 'apache-errorlog-ip-custom',
        "parent" => 'apache-errorlog',
        "prematch" => {
          "offset" => 'after_parent',
          "parser" => '^[client'
        },
        "regex" => {
          "offset" => 'after_prematch',
          "parser" => '^ (\d+.\d+.\d+.\d+)]'
        },
        "order" => 'srcip'
      },
      3 => {
        "name" => 'web-accesslog-custom',
        "parent" => 'web-accesslog',
        "type" => 'web-log',
        "prematch" => {
          "parser" => '^\d+.\d+.\d+.\d+ |^::ffff:\d+.\d+.\d+.\d+'
        },
        "regex" => {
          "parser" => '^\d+.\d+.\d+.\d+ \S+ (\d+.\d+.\d+.\d+) \S+ \S+ \S+ [\S+ \S\d+] "\w+ (\S+) HTTP\S+ (\d+) \S+ "(\S+)"'
        },
        "order" => 'srcip, url, id, extra_data'
      }
    },
    "rules" => {
      1002 => {
        "head" => {
          "level" => '2',
          "overwrite" => 'yes'
        },
        "body" => {
          "description" => 'Unknown problem somewhere in the system.',
          "match" => 'core_dumped|failure|error|Error|attack|bad |illegal |denied|refused|unauthorized|fatal|fail|Segmentation Fault|Corrupted|Traceback|raise',
          "options" => 'alert_by_email'
        }
      },
      1003 => {
        "head" => {
          "level" => '6',
          "maxsize" => '16384',
          "overwrite" => 'yes'
        },
        "body" => {
          "description" => 'Non standard syslog message (larger than 16kB).'
        }
      },
      100003 => {
        "head" => {
          "level" => '10'
        },
        "body" => {
          "description" => 'Successful sudo during non-business hours 6pm to 8am',
          "if_sid" => '5402,5403',
          "time" => '10pm - 12am'
        }
      },
      100004 => {
        "head" => {
          "level" => '10'
        },
        "body" => {
          "description" => 'Successful sudo during weekend.',
          "if_sid" => '5402,5403',
          "weekday" => 'weekends'
        }
      },
      100005 => {
        "head" => {
          "level" => '0'
        },
        "body" => {
          "description" => 'Silencing sudo errors from accounts allowed to sudo anytime',
          "if_sid" => '100004,100005',
          "match" => 'nagios'
        }
      },
      100006 => {
        "head" => {
          "level" => '0'
        },
        "body" => {
          "description" => 'Silencing ossec agent stop/start during business hours 8am to 6pm',
          "if_sid" => '502,503,504',
          "time" => '12:00-22:00',
          "weekday" => 'monday,tuesday,wednesday,thursday,friday'
        }
      },
      100007 => {
        "head" => {
          "level" => '8'
        },
        "body" => {
          "description" => 'Login outside of business hours 6pm to 8am',
          "if_sid" => '5501',
          "time" => '22:00-12:00'
        }
      },
      100008 => {
        "head" => {
          "level" => '8'
        },
        "body" => {
          "description" => 'Login during weekend.',
          "if_sid" => '5501',
          "weekday" => 'weekends'
        }
      },
      100009 => {
        "head" => {
          "level" => '0'
        },
        "body" => {
          "description" => 'Ignore logins alerts for systems accounts',
          "if_sid" => '100007,100008',
          "match" => 'ubuntu|nagios'
        }
      }
    }
  }
)

ossec-agent

This role can be used to provision an ossec-agent

name "ossec-agent"
description "OSSEC Agent"
run_list(
  "recipe[ossec-ng::agent]"
)
default_attributes(
  "ossec" => {
    "client" => {
      "service_name" => 'ossec-hids-client'
    },
    "syscheck" => {
      "frequency" => '7200',
      "alert_new_files" => 'yes',
      "auto_ignore" => 'no',
      "directories" => {
        '/bin' => {
          'report_changes' => 'no',
          'realtime' => 'yes'
        },
         '/boot' => {
          'report_changes' => 'no',
          'realtime' => 'no'
        },
        '/etc' => {
          'report_changes' => 'yes',
          'realtime' => 'no'
        },
        '/lib/lsb' => {
          'report_changes' => 'no',
          'realtime' => 'yes'
        },
        '/lib/modules' => {
          'report_changes' => 'no',
          'realtime' => 'yes'
        },
        '/lib/plymouth' => {
          'report_changes' => 'no',
          'realtime' => 'yes'
        },
        '/lib/security' => {
          'report_changes' => 'no',
          'realtime' => 'yes'
        },
        '/lib/terminfo' => {
          'report_changes' => 'no',
          'realtime' => 'yes'
        },
        '/lib/ufw' => {
          'report_changes' => 'no',
          'realtime' => 'yes'
        },
        '/lib/xtables' => {
          'report_changes' => 'no',
          'realtime' => 'no'
        },
        '/media' => {
          'report_changes' => 'no',
          'realtime' => 'no'
        },
        '/opt' => {
          'report_changes' => 'no',
          'realtime' => 'no'
        },
        '/root' => {
          'report_changes' => 'yes',
          'realtime' => 'no'
        },
        '/srv' => {
          'report_changes' => 'no',
          'realtime' => 'no'
        },
        '/sbin' => {
          'report_changes' => 'no',
          'realtime' => 'yes'
        },
        '/usr/' => {
          'report_changes' => 'yes',
          'realtime' => 'yes'
        },
        '/tmp' => {
          'report_changes' => 'no',
          'realtime' => 'no'
        }
      },
      "ignore" => {
        '/etc/openvpn/openvpn-status.log' => {}
        '/etc/motd' => {},
        '/etc/blkid.tab' => {},
        '/etc/mtab' => {},
        '/etc/mail/statistics' => {},
        '/etc/random-seed' => {},
        '/etc/adjtime' => {},
        '/etc/prelink.cache' => {},
        '/root/.bash_history' => {}
        '^/opt/graphite/storage/' => {
          'apply_to' => 'roles:graphite-server OR roles:statsd-server',
          'type' => 'sregex'
        },
        '^/usr/lib/elasticsearch' => {
          'apply_to' => 'roles:elastic-search-cluster',
          'type' => 'sregex'
        },
        '^/etc/chef/cache/checksums/' => {
          'apply_to' => 'roles:chef-client',
          'type' => 'sregex'
        },
        '^/srv/rsyslog/' => {
          'apply_to' => 'roles:rsyslog-server',
          'type' => 'sregex'
        },
        '^/etc/djbdns/public-dnscache/supervise/|^/etc/djbdns/tinydns-internal/supervise/|^/etc/djbdns/public-dnscache/log|^/etc/djbdns/tinydns-internal/log|^/etc/djbdns/tinydns-internal/root/data' => {
          'apply_to' => 'roles:djbdns-server',
          'type' => 'sregex'
        }
      }
    },
    "syslog_files" => {
      '/var/log/syslog' => {},
      '/var/log/auth.log' => {},
      '/var/log/daemon.log' => {},
      '/var/log/kern.log' => {},
      '/var/log/mail.log' => {},
      '/var/log/user.log' => {},
      '/var/log/cron.log' => {},
      '/var/log/chef/client.log' => {},
      '/var/log/supervisor/supervisor.log' => {
        'apply_to' => 'supervisor:*',
        'log_format' => 'syslog'
      },
      '/var/log/rabbitmq/rabbit1.log' => {
        'apply_to' => 'recipes:rabbitmq',
        'log_format' => 'multi-line:3'
      },
      '/var/log/nginx/access.log' => {
        'apply_to' => 'nginx:*',
        'log_format' => 'syslog'
      },
      '/var/log/nginx/error.log' => {
        'apply_to' => 'nginx:*',
        'log_format' => 'syslog'
      },
      '/var/log/nagios3/nagios.log' => {
        'apply_to' => 'roles:nagios-server',
        'log_format' => 'syslog'
      },
      '/var/log/nagios3/apache_access.log' => {
        'apply_to' => 'roles:nagios-server',
        'log_format' => 'syslog'
      },
      '/var/log/nagios3/apache_error.log' => {
        'apply_to' => 'roles:nagios-server',
        'log_format' => 'syslog'
      }
    }
  }
)

Author

Eric Renfro - psi-jack@linux-help.org - https://linux-help.org

Derived from works from: Julien Vehent - julien@linuxwall.info - http://jve.linuxwall.info