├── README.md ├── attributes └── ossec.rb ├── libraries └── core.rb ├── metadata.rb ├── recipes ├── agent.rb ├── default.rb ├── server.rb └── supervisor_logs.rb └── templates └── default ├── .local_rules.xml.erb.swp ├── client.keys.erb ├── internal_options.conf.erb ├── local_decoder.xml.erb ├── local_rules.xml.erb ├── ossec-agent.conf.erb └── ossec-server.conf.erb /README.md: -------------------------------------------------------------------------------- 1 | Description 2 | =========== 3 | Fully automated Installation and configuration of ossec-servers and ossec-agents 4 | Manage the key generation and distribution between a server and multiple agents 5 | Clean queues on the server if needed (rid) 6 | 7 | Requirements 8 | ============ 9 | Ubuntu 10.04+ 10 | (should work with ossec systems if you have the packages) 11 | 12 | Attributes 13 | ========== 14 | # General Attributes 15 | 16 | The attributes below follow the same namespace syntax that OSSEC does. Refer to 17 | the official [OSSEC Documentation](http://www.ossec.net/doc/syntax/ossec_config.html) 18 | for more information. 19 | 20 | Default attributes from the cookbook: 21 | 22 | default[:version] = "2.6" 23 | default[:ossec][:syslog_output][:ip] = "127.0.0.1" 24 | default[:ossec][:syslog_output][:port] = "514" 25 | default[:ossec][:syslog_output][:min_level] = "5" 26 | default[:ossec][:receiver_port] = "1514" 27 | default[:ossec][:log_alert_level] = "1" 28 | default[:ossec][:email_alert_level] = "7" 29 | default[:ossec][:agents] = {} 30 | 31 | Default attributes from the ossec-server role: 32 | 33 | :ossec => { 34 | :email_notification => 'yes', 35 | :email_to => [ 36 | 'ossec@example.net', 37 | ], 38 | :email_from => 'ossec-server@example.net', 39 | :smtp_server => 'localhost', 40 | :white_list => [ 41 | '127.0.0.1', 42 | '10.1.0.0/16' 43 | ], 44 | :email_alerts => { 45 | 'recipient@example.net' => { 46 | 'level' => '9', 47 | 'group' => 'syscheck', 48 | 'event_location_tag' => 'reputation', 49 | 'event_location_search' => 'roles:*mongodb*', 50 | 'format' => 'sms', 51 | 'rule_id' => '100001', 52 | 'tags' => [ 53 | 'do_not_delay', 54 | 'do_not_group' 55 | ] 56 | } 57 | }, 58 | :server => { 59 | :service_name => 'ossec-hids-server' 60 | }, 61 | :syscheck => { 62 | :frequency => '7200', 63 | :alert_new_files => 'yes', 64 | :auto_ignore => 'no', 65 | :directories => { 66 | '/bin' => { 67 | 'report_changes' => 'no', 68 | 'realtime' => 'yes' 69 | }, 70 | '/sbin' => { 71 | 'report_changes' => 'no', 72 | 'realtime' => 'yes' 73 | }, 74 | '/usr/bin' => { 75 | 'report_changes' => 'no', 76 | 'realtime' => 'yes' 77 | }, 78 | '/usr/sbin' => { 79 | 'report_changes' => 'no', 80 | 'realtime' => 'yes' 81 | }, 82 | '/etc' => { 83 | 'report_changes' => 'yes', 84 | 'realtime' => 'yes' 85 | }, 86 | '/tmp' => { 87 | 'report_changes' => 'yes', 88 | 'realtime' => 'no' 89 | } 90 | }, 91 | :ignore => [ 92 | '/etc/openvpn/openvpn-status.log', 93 | '/etc/motd', 94 | '/etc/mcollective/facts.yaml', 95 | '/etc/blkid.tab', 96 | '/etc/mtab', 97 | '/etc/mail/statistics', 98 | '/etc/random-seed', 99 | '/etc/adjtime', 100 | '/etc/prelink.cache', 101 | '/etc/dnscache/stats', 102 | '/etc/dnscache/log', 103 | '/etc/dnscache2/stats', 104 | '/etc/dnscache2/log', 105 | '/etc/tinydns/stats', 106 | '/etc/tinydns/log' 107 | ] 108 | }, 109 | :syslog_files => [ 110 | '/var/log/syslog', 111 | '/var/log/auth.log', 112 | '/var/log/daemon.log', 113 | '/var/log/kern.log', 114 | '/var/log/mail.log', 115 | '/var/log/user.log', 116 | '/var/log/cron.log' 117 | ], 118 | 119 | ```email_alerts``` is a hash of recipients and servers. Each recipient will 120 | receive all of the alert for the listed location (the list is a regex). 121 | ```event_location_tag``` must contain a valid chef tag. All the nodes listed by 122 | that tag will generate a separate ```email_alerts``` rule. 123 | This is additional to the default list ```email_to``` and is used to send alert to 124 | specific recipients for a limited number of hosts only. 125 | 126 | # Local Rules Definitions 127 | Rules are defined in Ruby Hash format and replicate the XML format of regular 128 | [OSSEC Rules Syntax](http://www.ossec.net/doc/syntax/head_rules.html) 129 | Each rule has a head, a body, tags and info (the last 2 being optional) 130 | 131 | head= 132 | body= Test Rule 133 | body= Big Error 134 | body= server1 135 | tags= 136 | tags= 137 | info= http://IjustGotHacked.com 138 | 139 | 140 | The section below are parsed by the template. The following items are mandatory: 141 | * head/level 142 | * body/description 143 | 144 | ``` 145 | :ossec => 146 | :rules => { 147 | "100001" => { 148 | :head => { 149 | :level => "7", 150 | :maxsize => "65536", 151 | :frequency => "100", 152 | :timeframe => "3600", 153 | :ignore => "5", 154 | :overwrite => "68321" 155 | }, 156 | :body => { 157 | :hostname_search => "recipes:mms-agent", 158 | :description => "Super Security Rule for application XYZ", 159 | :match => "super dangerous error happened", 160 | :regex => "^\d+Hello World$", 161 | :decoded_as => "vsftpd", 162 | :category => "windows", 163 | :srcip => "192.168.1.254", 164 | :dstip => "10.1.6.23", 165 | :user => "bob", 166 | :program_name => "nginx", 167 | :time => "09:00-18:00", 168 | :weekday => "monday,tuesday", 169 | :id => "404", 170 | :url => "/changepassword.php", 171 | :if_sid => "100238", 172 | :if_group => "authentication_success", 173 | :if_level => "13", 174 | :if_matched_sid => "12037", 175 | :if_matched_group => "adduser", 176 | :if_matched_level => "7", 177 | :options => "no_email_alert", 178 | :check_diff => "true", 179 | :group => "syscheck" 180 | }, 181 | :tags => [ 182 | "same_source_ip", 183 | "same_source_port", 184 | "same_dst_port", 185 | "same_location" 186 | ], 187 | :infos => { 188 | :link => "http://trac.example.net/ticket/12345", 189 | :text => "the link above contains additional information" 190 | } 191 | } 192 | } 193 | ``` 194 | 195 | ## hostname_search 196 | 197 | To the exception of __hostname_search__, all attributes use the same syntax as the 198 | ossec rule in XML format does. 199 | __hostname_search__ in this cookbook represents a search query that is executed by 200 | the server recipe to populate the `````` with the proper list of hosts, 201 | dynamically pulled from chef. Search criterias can be anything that a chef search 202 | can take. Example: ```recipe:mongodb\:\:replicaset and tags:reputation``` 203 | 204 | # Local Decoders Definitions 205 | Decoders are defined in JSON format and replicate the XML format of regular 206 | [OSSEC Decoder Syntax](http://www.ossec.net/doc/syntax/head_decoders.html) 207 | 208 | :ossec => { 209 | :decoders => { 210 | 'apache-errorlog' => { 211 | :program_name => '^httpd|^apache2', 212 | :prematch => { 213 | :parser => '^\S+ [\w+\s*\d+ \S+ \d+] [\S+] |^[warn] |^[notice] |^[error]' 214 | }, 215 | 216 | }, 217 | 'apache-errorlog-ip-custom' => { 218 | :parent => 'apache-errorlog', 219 | :prematch => { 220 | :offset => 'after_parent', 221 | :parser => '^[client' 222 | }, 223 | :regex => { 224 | :offset => 'after_prematch', 225 | :parser => '^ (\d+.\d+.\d+.\d+)]' 226 | }, 227 | :order => 'srcip' 228 | }, 229 | 'web-accesslog-custom' => { 230 | :parent => 'web-accesslog', 231 | :type => 'web-log', 232 | :prematch => { 233 | :parser => '^\d+.\d+.\d+.\d+ |^::ffff:\d+.\d+.\d+.\d+' 234 | }, 235 | :regex => { 236 | :parser => '^\d+.\d+.\d+.\d+ \S+ (\d+.\d+.\d+.\d+) \S+ \S+ \S+ [\S+ \S\d+] "\w+ (\S+) HTTP\S+ (\d+) \S+ "(\S+)"' 237 | }, 238 | :order => 'srcip, url, id, extra_data' 239 | } 240 | } 241 | } 242 | 243 | ```prematch``` and ```regex``` are hashes that can have an ```offset``` value and 244 | always have a ```parser``` value. See the ossec documentation for more information. 245 | 246 | # Local Syslog Files 247 | If you want specific log files to be monitored on specific agents, you can use 248 | a `local_syslog_files` block in the agent node attributes. The `apply_to` 249 | parameter of this block is a `Chef::Search()` that will expand to a list of 250 | hosts. If the given agent belong to the list of hosts, it will add the logfile 251 | to its local ossec configuration. 252 | 253 | ``` 254 | default_attributes( 255 | :ossec => { 256 | local_syslog_files => { 257 | '/var/log/supervisor/supervisor.log' => { 258 | 'apply_to' => 'supervisor:*', 259 | 'log_format' => 'syslog' 260 | }, 261 | } 262 | } 263 | ) 264 | ``` 265 | 266 | Usage 267 | ===== 268 | * `recipe[ossec-server]` should be a stand alone installation 269 | * `recipe[ossec-agent]` should be added (via role[ossec-agent]) to all the nodes of the 270 | environment 271 | 272 | # Example Roles 273 | ## ossec-server 274 | This role can be used to provision an ossec server: 275 | 276 | ```ruby 277 | name 'ossec-server' 278 | description 'OSSEC Server' 279 | run_list( 280 | 'recipe[ossec::server]', 281 | 'role[postfix]' 282 | ) 283 | override_attributes( 284 | :ossec => { 285 | :agent => { 286 | :enable => false 287 | } 288 | } 289 | ) 290 | default_attributes( 291 | :ossec => { 292 | :email_notification => 'yes', 293 | :email_to => [ 294 | 'ossec-alerts@example.net', 295 | ], 296 | :email_from => 'ossec-server', 297 | :smtp_server => 'localhost', 298 | :white_list => [ 299 | '127.0.0.1', 300 | '10.0.0.0/0' 301 | ], 302 | :email_alerts => { 303 | 'bob@example.net' => { 304 | 'event_location_tag' => 'project1', 305 | }, 306 | 'alice@example.net' => { 307 | 'event_location_tag' => 'project1', 308 | 'group' => 'developers', 309 | }, 310 | 'eve@example.net' => { 311 | 'event_location_tag' => 'project2', 312 | 'group' => 'developers', 313 | }, 314 | 'mike@example.net' => { 315 | 'event_location_search' => 'tags:project1 OR tags:project2 OR tags:project3', 316 | 'group' => 'developers', 317 | }, 318 | 'group2@example.net' => { 319 | 'event_location_search' => 'roles:application-server AND roles:python-django', 320 | 'group' => 'frontend-group', 321 | }, 322 | }, 323 | :decoders => { 324 | 1 => { 325 | :name => 'apache-errorlog', 326 | :program_name => '^httpd|^apache2', 327 | :prematch => { 328 | :parser => '^\S+ [\w+\s*\d+ \S+ \d+] [\S+] |^[warn] |^[notice] |^[error]' 329 | }, 330 | 331 | }, 332 | 2 => { 333 | :name => 'apache-errorlog-ip-custom', 334 | :parent => 'apache-errorlog', 335 | :prematch => { 336 | :offset => 'after_parent', 337 | :parser => '^[client' 338 | }, 339 | :regex => { 340 | :offset => 'after_prematch', 341 | :parser => '^ (\d+.\d+.\d+.\d+)]' 342 | }, 343 | :order => 'srcip' 344 | }, 345 | 3 => { 346 | :name => 'web-accesslog-custom', 347 | :parent => 'web-accesslog', 348 | :type => 'web-log', 349 | :prematch => { 350 | :parser => '^\d+.\d+.\d+.\d+ |^::ffff:\d+.\d+.\d+.\d+' 351 | }, 352 | :regex => { 353 | :parser => '^\d+.\d+.\d+.\d+ \S+ (\d+.\d+.\d+.\d+) \S+ \S+ \S+ [\S+ \S\d+] "\w+ (\S+) HTTP\S+ (\d+) \S+ "(\S+)"' 354 | }, 355 | :order => 'srcip, url, id, extra_data' 356 | } 357 | }, 358 | :rules => { 359 | 1002 => { 360 | :head => { 361 | :level => '2', 362 | :overwrite => 'yes' 363 | }, 364 | :body => { 365 | :description => 'Unknown problem somewhere in the system.', 366 | :match => 'core_dumped|failure|error|Error|attack|bad |illegal |denied|refused|unauthorized|fatal|fail|Segmentation Fault|Corrupted|Traceback|raise', 367 | :options => 'alert_by_email' 368 | } 369 | }, 370 | 1003 => { 371 | :head => { 372 | :level => '6', 373 | :maxsize => '16384', 374 | :overwrite => 'yes' 375 | }, 376 | :body => { 377 | :description => 'Non standard syslog message (larger than 16kB).' 378 | } 379 | }, 380 | 100003 => { 381 | :head => { 382 | :level => '10' 383 | }, 384 | :body => { 385 | :description => 'Successful sudo during non-business hours 6pm to 8am', 386 | :if_sid => '5402,5403', 387 | :time => '10pm - 12am' 388 | } 389 | }, 390 | 100004 => { 391 | :head => { 392 | :level => '10' 393 | }, 394 | :body => { 395 | :description => 'Successful sudo during weekend.', 396 | :if_sid => '5402,5403', 397 | :weekday => 'weekends' 398 | } 399 | }, 400 | 100005 => { 401 | :head => { 402 | :level => '0' 403 | }, 404 | :body => { 405 | :description => 'Silencing sudo errors from accounts allowed to sudo anytime', 406 | :if_sid => '100004,100005', 407 | :match => 'nagios' 408 | } 409 | }, 410 | 100006 => { 411 | :head => { 412 | :level => '0' 413 | }, 414 | :body => { 415 | :description => 'Silencing ossec agent stop/start during business hours 8am to 6pm', 416 | :if_sid => '502,503,504', 417 | :time => '12:00-22:00', 418 | :weekday => 'monday,tuesday,wednesday,thursday,friday' 419 | } 420 | }, 421 | 100007 => { 422 | :head => { 423 | :level => '8' 424 | }, 425 | :body => { 426 | :description => 'Login outside of business hours 6pm to 8am', 427 | :if_sid => '5501', 428 | :time => '22:00-12:00' 429 | } 430 | }, 431 | 100008 => { 432 | :head => { 433 | :level => '8' 434 | }, 435 | :body => { 436 | :description => 'Login during weekend.', 437 | :if_sid => '5501', 438 | :weekday => 'weekends' 439 | } 440 | }, 441 | 100009 => { 442 | :head => { 443 | :level => '0' 444 | }, 445 | :body => { 446 | :description => 'Ignore logins alerts for systems accounts', 447 | :if_sid => '100007,100008', 448 | :match => 'ubuntu|nagios' 449 | } 450 | } 451 | } 452 | } 453 | ) 454 | ``` 455 | 456 | ## ossec-agent 457 | This role can be used to provision an ossec-agent 458 | 459 | ```ruby 460 | name "ossec-agent" 461 | description "OSSEC Agent" 462 | run_list( 463 | "recipe[ossec::agent]" 464 | ) 465 | default_attributes( 466 | :ossec => { 467 | :client => { 468 | :service_name => 'ossec-hids-client' 469 | }, 470 | :syscheck => { 471 | :frequency => '7200', 472 | :alert_new_files => 'yes', 473 | :auto_ignore => 'no', 474 | :directories => { 475 | '/bin' => { 476 | 'report_changes' => 'no', 477 | 'realtime' => 'yes' 478 | }, 479 | '/boot' => { 480 | 'report_changes' => 'no', 481 | 'realtime' => 'no' 482 | }, 483 | '/etc' => { 484 | 'report_changes' => 'yes', 485 | 'realtime' => 'no' 486 | }, 487 | '/lib/lsb' => { 488 | 'report_changes' => 'no', 489 | 'realtime' => 'yes' 490 | }, 491 | '/lib/modules' => { 492 | 'report_changes' => 'no', 493 | 'realtime' => 'yes' 494 | }, 495 | '/lib/plymouth' => { 496 | 'report_changes' => 'no', 497 | 'realtime' => 'yes' 498 | }, 499 | '/lib/security' => { 500 | 'report_changes' => 'no', 501 | 'realtime' => 'yes' 502 | }, 503 | '/lib/terminfo' => { 504 | 'report_changes' => 'no', 505 | 'realtime' => 'yes' 506 | }, 507 | '/lib/ufw' => { 508 | 'report_changes' => 'no', 509 | 'realtime' => 'yes' 510 | }, 511 | '/lib/xtables' => { 512 | 'report_changes' => 'no', 513 | 'realtime' => 'no' 514 | }, 515 | '/media' => { 516 | 'report_changes' => 'no', 517 | 'realtime' => 'no' 518 | }, 519 | '/opt' => { 520 | 'report_changes' => 'no', 521 | 'realtime' => 'no' 522 | }, 523 | '/root' => { 524 | 'report_changes' => 'yes', 525 | 'realtime' => 'no' 526 | }, 527 | '/srv' => { 528 | 'report_changes' => 'no', 529 | 'realtime' => 'no' 530 | }, 531 | '/sbin' => { 532 | 'report_changes' => 'no', 533 | 'realtime' => 'yes' 534 | }, 535 | '/usr/' => { 536 | 'report_changes' => 'yes', 537 | 'realtime' => 'yes' 538 | }, 539 | '/tmp' => { 540 | 'report_changes' => 'no', 541 | 'realtime' => 'no' 542 | } 543 | }, 544 | :ignore => [ 545 | '/etc/openvpn/openvpn-status.log', 546 | '/etc/motd', 547 | '/etc/blkid.tab', 548 | '/etc/mtab', 549 | '/etc/mail/statistics', 550 | '/etc/random-seed', 551 | '/etc/adjtime', 552 | '/etc/prelink.cache', 553 | '/root/.bash_history' 554 | ], 555 | :local_ignore => { 556 | '^/opt/graphite/storage/' => { 557 | 'apply_to' => 'roles:graphite-server OR roles:statsd-server', 558 | 'type' => 'sregex' 559 | }, 560 | '^/usr/lib/elasticsearch' => { 561 | 'apply_to' => 'roles:elastic-search-cluster', 562 | 'type' => 'sregex' 563 | }, 564 | '^/etc/chef/cache/checksums/' => { 565 | 'apply_to' => 'roles:chef-client', 566 | 'type' => 'sregex' 567 | }, 568 | '^/srv/rsyslog/' => { 569 | 'apply_to' => 'roles:rsyslog-server', 570 | 'type' => 'sregex' 571 | }, 572 | '^/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' => { 573 | 'apply_to' => 'roles:djbdns-server', 574 | 'type' => 'sregex' 575 | } 576 | } 577 | }, 578 | :syslog_files => [ 579 | '/var/log/syslog', 580 | '/var/log/auth.log', 581 | '/var/log/daemon.log', 582 | '/var/log/kern.log', 583 | '/var/log/mail.log', 584 | '/var/log/user.log', 585 | '/var/log/cron.log', 586 | '/var/log/chef/client.log' 587 | ], 588 | :local_syslog_files => { 589 | '/var/log/supervisor/supervisor.log' => { 590 | 'apply_to' => 'supervisor:*', 591 | 'log_format' => 'syslog' 592 | }, 593 | '/var/log/rabbitmq/rabbit1.log' => { 594 | 'apply_to' => 'recipes:rabbitmq', 595 | 'log_format' => 'multi-line:3' 596 | }, 597 | '/var/log/nginx/access.log' => { 598 | 'apply_to' => 'nginx:*', 599 | 'log_format' => 'syslog' 600 | }, 601 | '/var/log/nginx/error.log' => { 602 | 'apply_to' => 'nginx:*', 603 | 'log_format' => 'syslog' 604 | }, 605 | '/var/log/nagios3/nagios.log' => { 606 | 'apply_to' => 'roles:nagios-server', 607 | 'log_format' => 'syslog' 608 | }, 609 | '/var/log/nagios3/apache_access.log' => { 610 | 'apply_to' => 'roles:nagios-server', 611 | 'log_format' => 'syslog' 612 | }, 613 | '/var/log/nagios3/apache_error.log' => { 614 | 'apply_to' => 'roles:nagios-server', 615 | 'log_format' => 'syslog' 616 | } 617 | } 618 | } 619 | ) 620 | ``` 621 | 622 | Author 623 | ====== 624 | Julien Vehent - julien@linuxwall.info - http://jve.linuxwall.info 625 | -------------------------------------------------------------------------------- /attributes/ossec.rb: -------------------------------------------------------------------------------- 1 | default[:version] = "2.6" 2 | default[:ossec][:syslog_output][:ip] = "172.16.254.254" 3 | default[:ossec][:syslog_output][:port] = "514" 4 | default[:ossec][:syslog_output][:min_level] = "5" 5 | default[:ossec][:receiver_port] = "1514" 6 | default[:ossec][:log_alert_level] = "1" 7 | default[:ossec][:email_alert_level] = "7" 8 | default[:ossec][:email_maxperhour] = "9999" 9 | default[:ossec][:memory_size] = "100000" 10 | default[:ossec][:remote][:connection] = "secure" 11 | default[:ossec][:agents] = {} 12 | default[:ossec][:rules] = {} 13 | default[:ossec][:email_alerts] = {} 14 | default[:ossec][:agent][:enable] = true 15 | default[:ossec][:server][:service_name] = "ossec-hids-server" 16 | default[:ossec][:client][:service_name] = "ossec-hids-client" 17 | 18 | 19 | # internal options, you probably don't want to touch that 20 | default[:ossec][:internal][:analysisd][:default_timeframe] = "360" 21 | default[:ossec][:internal][:analysisd][:stats_maxdiff] = "25000" 22 | default[:ossec][:internal][:analysisd][:stats_mindiff] = "250" 23 | default[:ossec][:internal][:analysisd][:stats_percent_diff] = "30" 24 | default[:ossec][:internal][:analysisd][:fts_list_size] = "32" 25 | default[:ossec][:internal][:analysisd][:fts_min_size_for_str] = "14" 26 | default[:ossec][:internal][:analysisd][:log_fw] = "1" 27 | default[:ossec][:internal][:logcollector][:loop_timeout] = "2" 28 | default[:ossec][:internal][:logcollector][:open_attempts] = "8" 29 | default[:ossec][:internal][:remoted][:recv_counter_flush] = "128" 30 | default[:ossec][:internal][:remoted][:comp_average_printout] = "19999" 31 | default[:ossec][:internal][:remoted][:verify_msg_id] = "1" 32 | default[:ossec][:internal][:maild][:strict_checking] = "1" 33 | default[:ossec][:internal][:maild][:groupping] = "0" 34 | default[:ossec][:internal][:maild][:full_subject] = "1" 35 | default[:ossec][:internal][:monitord][:compress] = "1" 36 | default[:ossec][:internal][:monitord][:sign] = "1" 37 | default[:ossec][:internal][:monitord][:monitor_agents] = "1" 38 | default[:ossec][:internal][:syscheck][:sleep] = "2" 39 | default[:ossec][:internal][:syscheck][:sleep_after] = "15" 40 | default[:ossec][:internal][:dbd][:reconnect_attempts] = "10" 41 | default[:ossec][:internal][:window][:debug] = "0" 42 | default[:ossec][:internal][:syscheck][:debug] = "0" 43 | default[:ossec][:internal][:remoted][:debug] = "0" 44 | default[:ossec][:internal][:analysisd][:debug] = "0" 45 | default[:ossec][:internal][:logcollector][:debug] = "0" 46 | default[:ossec][:internal][:agent][:debug] = "0" 47 | -------------------------------------------------------------------------------- /libraries/core.rb: -------------------------------------------------------------------------------- 1 | # Core function for OSSEC 2 | # Used by the server and agent recipes 3 | 4 | module OssecCore 5 | 6 | def ossec_hostname_search() 7 | # resolve the hostname_search of a rule to a list of hosts 8 | node[:ossec][:rules].each do |id,params| 9 | if not params.nil? 10 | params[:body].each do |key, value| 11 | if key.eql?('hostname_search') 12 | hosts_list = search(:node, 13 | "(#{value}) AND roles:ossec-agent "\ 14 | " AND chef_environment:#{node.chef_environment}" 15 | ).map {|n| n.hostname} 16 | if hosts_list.empty? 17 | # search didn't return anything 18 | # store a dummy value in the attributes 19 | Chef::Log.info("OSSEC: Hostname search returned empty result. " + 20 | "'#{value}'") 21 | params[:body][:hostname] = "invalid-search-returned-empty-result" 22 | else 23 | # store in the node params but discard the last char 24 | params[:body][:hostname] = hosts_list.join('|') 25 | end 26 | end 27 | end 28 | end 29 | end 30 | end 31 | 32 | 33 | def ossec_event_location_search() 34 | # resolve the location search of an email_alert block to a hostname 35 | node[:ossec][:email_alerts].each do|recipient,params| 36 | if params.has_key?('event_location_search') 37 | dest = search(:node, 38 | "(#{params[:event_location_search]}) " \ 39 | "AND chef_environment:#{node.chef_environment}" 40 | ).map {|n| n.hostname} 41 | node.default[:ossec][:email_alerts][recipient][:resolved_search] = dest 42 | end 43 | end 44 | end 45 | 46 | 47 | def ossec_set_local_syslog_file_ignore_flags!() 48 | # go through the list of local logfile and check the ones that 49 | # apply to this node 50 | unless node[:ossec][:local_syslog_files].nil? 51 | node[:ossec][:local_syslog_files].each do |logfile,params| 52 | locations = search(:node, 53 | "(#{params[:apply_to]}) " \ 54 | "AND chef_environment:#{node.chef_environment}" 55 | ).map {|n| n.ipaddress} 56 | if locations.include?(node.ipaddress) 57 | node.default[:ossec][:local_syslog_files][logfile][:use_here] = "true" 58 | else 59 | node.default[:ossec][:local_syslog_files][logfile][:use_here] = "false" 60 | end 61 | end 62 | end 63 | end 64 | 65 | 66 | def ossec_set_local_file_ignore_flags!() 67 | # go through the list of locally ignored and check the ones that 68 | # apply to this node 69 | if not node[:ossec][:syscheck][:local_ignore].nil? 70 | node[:ossec][:syscheck][:local_ignore].each do |file,params| 71 | locations = search(:node, 72 | "(#{params[:apply_to]}) " \ 73 | "AND chef_environment:#{node.chef_environment}" 74 | ).map {|n| n.ipaddress} 75 | if locations.include?(node.ipaddress) 76 | node.default[:ossec][:syscheck][:local_ignore][file][:use_here] = "true" 77 | else 78 | node.default[:ossec][:syscheck][:local_ignore][file][:use_here] = "false" 79 | end 80 | end 81 | end 82 | end 83 | 84 | 85 | def ossec_agent_create_parameters(agent, server) 86 | # Returns a hash with the identiers for this agent 87 | agent_hash = {} 88 | # IP is defined by lanip, if available (ohai plugin network_addr) 89 | # or by the default ipaddress otherwise 90 | agent_hash[:ip] = agent[:network][:lanip] || agent.ipaddress 91 | 92 | # Ossec limits the agents name length to 32 characters, so to avoid 93 | # names collisions, we concatenate the agent_ip with the first characters 94 | # of the hostname 95 | name = agent_hash[:ip] + "_" + agent[:hostname] 96 | agent_hash[:name] = name[0,31] 97 | 98 | # ossec agent id is an integer used to identify an agent. we force that ID 99 | # to be the IP address without the dots (10.1.2.3 becomes 10123) 100 | agent_hash[:id] = agent_hash[:ip].gsub(".", "") 101 | 102 | agent_hash[:key] = "undef" 103 | if server[:ossec][:agents].key?(agent_hash[:id]) 104 | if server[:ossec][:agents][agent_hash[:id]].key?('key') 105 | agent_hash[:key] = server[:ossec][:agents][agent_hash[:id]][:key] 106 | end 107 | end 108 | agent_hash[:rid] = "none" 109 | 110 | return agent_hash 111 | end 112 | 113 | 114 | def ossec_generate_agent_key(agent_hash) 115 | # Returns a 64 characters double md5 hash, used as a symetric key 116 | seed1 = rand(100000000000).to_s 117 | seed2 = rand(100000000000).to_s 118 | str1 = Digest::MD5.hexdigest(seed1 + \ 119 | agent_hash[:id] + \ 120 | agent_hash[:ip] + \ 121 | seed2) 122 | str2 = Digest::MD5.hexdigest(seed2 + \ 123 | agent_hash[:name] + \ 124 | seed1 + \ 125 | agent_hash[:ip] + \ 126 | agent_hash[:id]) 127 | key = str1 + str2 128 | return key 129 | end 130 | 131 | 132 | def ossec_agent_has_valid_key?(agent_hash, server) 133 | # Does the server have a valid key for this agent ? 134 | if server[:ossec][:agents].key?(agent_hash[:id]) 135 | if server[:ossec][:agents][agent_hash[:id]].key?('key') 136 | if server[:ossec][:agents][agent_hash[:id]][:key].length == 64 137 | return true 138 | end 139 | end 140 | end 141 | return false 142 | end 143 | 144 | 145 | def ossec_agent_knows_key?(agent_hash, agent) 146 | # Does the agent have a key that matches the server ? 147 | if agent.key?('ossec') 148 | if agent[:ossec].key?('agents') 149 | if agent[:ossec][:agents].key?(agent_hash[:id]) 150 | if agent[:ossec][:agents][agent_hash[:id]].key?('key') 151 | if agent[:ossec][:agents][agent_hash[:id]][:key] == agent_hash[:key] 152 | return true 153 | end 154 | end 155 | end 156 | end 157 | end 158 | return false 159 | end 160 | 161 | 162 | def ossec_verify_agent(agent_hash, server) 163 | # check if this agent (id, name, ip) is defined on the server 164 | if server[:ossec][:agents].key?(agent_hash[:id]) 165 | agent_srv_data = server[:ossec][:agents][agent_hash[:id]] 166 | if agent_srv_data[:name].eql?(agent_hash[:name]) 167 | if agent_srv_data[:ip].eql?(agent_hash[:ip]) 168 | return true 169 | else 170 | Chef::Log.info("OSSEC: agent ip mismatch. " + 171 | "server has '#{agent_srv_data[:ip]}' " + 172 | "agent has '#{agent_hash[:ip]}'") 173 | end 174 | else 175 | Chef::Log.info("OSSEC: agent name mismatch. " + 176 | "server has '#{agent_srv_data[:name]}' " + 177 | "agent has '#{agent_hash[:name]}'") 178 | end 179 | else 180 | Chef::Log.info("OSSEC: agent name '#{agent_hash[:name]}' " + 181 | " ip '#{agent_hash[:ip]}'" + 182 | " configuration not found on server.") 183 | end 184 | return false 185 | end 186 | 187 | 188 | def ossec_agent_is_active?(id) 189 | if File.exists?("/var/ossec/bin/agent_control") 190 | cmd = Chef::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}") 191 | cmd_ret = cmd.run_command 192 | status = cmd.stdout.split(",") 193 | if status[3] && status[3].eql?("Active") 194 | return true 195 | end 196 | end 197 | return false 198 | end 199 | 200 | 201 | def ossec_agent_is_zombie?(id) 202 | if File.exists?("/var/ossec/bin/agent_control") 203 | cmd = Chef::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}") 204 | cmd_ret = cmd.run_command 205 | status = cmd.stdout.split(",") 206 | if not status[6] || status[3] =~ /(Never connected|)/ 207 | return true 208 | elsif status[6] !~ /Unknown/ 209 | last_keep_alive = Time.parse(status[6]) 210 | three_days_ago = (Time.now - (24*60*60*3)) 211 | if three_days_ago > last_keep_alive 212 | return true 213 | end 214 | end 215 | end 216 | return false 217 | end 218 | 219 | 220 | def ossec_agent_should_be_removed?(id) 221 | if File.exists?("/var/ossec/bin/agent_control") 222 | cmd = Chef::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}") 223 | cmd_ret = cmd.run_command 224 | status = cmd.stdout.split(",") 225 | if not status[6] or status[6] =~ /Unknown/ 226 | return true 227 | else 228 | last_keep_alive = Time.parse(status[6]) 229 | seven_days_ago = (Time.now - (24*60*60*7)) 230 | if seven_days_ago > last_keep_alive 231 | return true 232 | end 233 | end 234 | end 235 | return false 236 | end 237 | 238 | 239 | def ossec_agent_needs_rid?(id, agent) 240 | # Check if the agent queue needs to be removed, either because the server 241 | # said so, or because the agent asked for it 242 | if agent[:ossec][:agents][id][:rid].eql?("todo") \ 243 | or node[:ossec][:agents][id][:rid].eql?("todo") 244 | return true 245 | else 246 | return false 247 | end 248 | end 249 | end 250 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name "autossec" 2 | maintainer "Julien Vehent" 3 | maintainer_email "julien@linuxwall.info" 4 | license "GPLv2" 5 | description "Installs/Configures ossec" 6 | long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) 7 | version "1.0.0" 8 | 9 | depends "postfix" 10 | -------------------------------------------------------------------------------- /recipes/agent.rb: -------------------------------------------------------------------------------- 1 | # Ossec Agent provisioning recipe 2 | # install the ossec-hids-client package, push the global 3 | # and role specific configuration for the node 4 | # get a key from the ossec-server if there's one 5 | 6 | if not node['lsb']['codename'].eql?('lucid') 7 | return true 8 | end 9 | 10 | class Chef::Recipe 11 | include OssecCore 12 | end 13 | 14 | 15 | # Run this recipe if the node is an agent. Since the ossec::agent recipe is 16 | # added to the base role, ossec-servers will run it as well, making this check 17 | # necessary 18 | if not node[:ossec][:agent][:enable] 19 | # return will exit this recipe 20 | # and continue the chef provisioning 21 | Chef::Log.info("OSSEC: agent is not enabled on this node") 22 | return true 23 | end 24 | 25 | # Search for the ossec server, and do nothing if there's none 26 | ossec_server = search(:node, 27 | "role:ossec-server " \ 28 | "AND chef_environment:#{node.chef_environment}" 29 | ).first 30 | if ossec_server.nil? 31 | Chef::Log.info("OSSEC: No ossec server available. Agent will not be provisionned") 32 | return true 33 | end 34 | 35 | # install the agent package 36 | package "ossec-hids-client" 37 | 38 | # define the agent parameters 39 | agent_hash = ossec_agent_create_parameters(node, ossec_server) 40 | 41 | # check for the agent configuration on the server. if the server has none, do 42 | # not continue the provisioning. If the server has a configuration for this 43 | # agent, store the parameters on the node and continue 44 | if ossec_verify_agent(agent_hash, ossec_server) 45 | node.set[:ossec][:agents][agent_hash[:id]] = ossec_server[:ossec][:agents][agent_hash[:id]].to_hash 46 | else 47 | Chef::Log.info("OSSEC: this agent is unknown on the ossec server") 48 | return true 49 | end 50 | 51 | # Make sure that the server prepared a key for us 52 | unless ossec_agent_has_valid_key?(agent_hash, ossec_server) 53 | Chef::Log.info("OSSEC: Server doesn't have a valid key for agent.") 54 | return true 55 | end 56 | 57 | service "ossec-agent" do 58 | provider Chef::Provider::Service::Init 59 | service_name node[:ossec][:client][:service_name] 60 | supports :start => true, :stop => true, :restart => true, :status => true 61 | action [ :start ] 62 | only_if "test -e /var/ossec/etc/ossec.conf && test -e /var/ossec/etc/client.keys" 63 | end 64 | 65 | # Get the IP of the ossec server 66 | ossec_server_ip = ossec_server[:network][:lanip] || ossec_server.ipaddress 67 | # Expand the local syslog files searches from the node attributes 68 | ossec_set_local_syslog_file_ignore_flags!() 69 | ossec_set_local_file_ignore_flags!() 70 | template "/var/ossec/etc/ossec.conf" do 71 | source "ossec-agent.conf.erb" 72 | owner "ossec" 73 | group "ossec" 74 | variables(:ossec_server_ip => ossec_server_ip ) 75 | notifies :restart, "service[ossec-agent]" 76 | end 77 | 78 | # If client.keys is modified, ask for a queue rid on the server 79 | template "/var/ossec/etc/client.keys" do 80 | mode 0440 81 | owner "root" 82 | group "ossec" 83 | notifies :create, "ruby_block[set-rid-flag]" 84 | notifies :restart, "service[ossec-agent]" 85 | end 86 | 87 | # "set-rid-flag" is not run by default, but called when the agent's key 88 | # is modified (or created) 89 | ruby_block "set-rid-flag" do 90 | block do 91 | # if the server side rid flag is not set to "done", 92 | # request a queue rid by setting the agent side flag to "todo" 93 | if ossec_server[:ossec][:agents][agent_hash[:id]][:rid].eql?("none") 94 | node.set[:ossec][:agents][agent_hash[:id]][:rid] = "todo" 95 | Chef::Log.info "Setting Queue Rid Flag on" 96 | end 97 | end 98 | action :nothing 99 | end 100 | 101 | # unset rid flag if necessary, check that at every run 102 | if node[:ossec][:agents][agent_hash[:id]][:rid].eql?("todo") \ 103 | and ossec_server[:ossec][:agents][agent_hash[:id]][:rid].eql?("done") 104 | ruby_block "unset rid flag" do 105 | block do 106 | node.set[:ossec][:agents][agent_hash[:id]][:rid] = "none" 107 | Chef::Log.info "Setting Queue Rid Flag off" 108 | end 109 | notifies :restart, "service[ossec-agent]" 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: ossec 3 | # Recipe:: default 4 | # 5 | include_recipe "ossec::agent" 6 | 7 | -------------------------------------------------------------------------------- /recipes/server.rb: -------------------------------------------------------------------------------- 1 | # Ossec server provisioning recipe 2 | # install the ossec-hids-server package and push the 3 | # default configuration from the templates 4 | 5 | include_recipe "postfix::default" 6 | 7 | class Chef::Recipe 8 | include OssecCore 9 | end 10 | 11 | 12 | package "ossec-hids-server" 13 | 14 | service "ossec-server" do 15 | provider Chef::Provider::Service::Init 16 | service_name node[:ossec][:server][:service_name] 17 | supports :start => true, :stop => true, :restart => true, :status => true 18 | action [ :start ] 19 | only_if "test -e /var/ossec/etc/ossec.conf" 20 | end 21 | 22 | # Get all the agents at once, more efficient 23 | ossec_agents = search(:node, 24 | "roles:ossec-agent "\ 25 | "AND chef_environment:#{node.chef_environment}") 26 | 27 | # resolve searches in server rules 28 | ossec_hostname_search() 29 | 30 | # resolve email_alerts location searches 31 | ossec_event_location_search() 32 | 33 | # initialize the agent hash on the first run 34 | if node[:ossec][:agents].nil? 35 | node.set[:ossec][:agents] = {} 36 | end 37 | 38 | ossec_agents.each do |agent| 39 | # don't process thy self 40 | if agent.ipaddress == node.ipaddress 41 | next 42 | end 43 | 44 | agent_hash = ossec_agent_create_parameters(agent, node) 45 | 46 | # this agent is running fine, go to the next one 47 | if ossec_agent_is_active?(agent_hash[:id]) 48 | node.set[:ossec][:agents][agent_hash[:id]][:status] = "active" 49 | next 50 | end 51 | 52 | # check that the agent ID still point to the same IP and hostname 53 | # otherwise, delete the record from the ossec server 54 | if not ossec_verify_agent(agent_hash, node) 55 | Chef::Log.info("OSSEC: deleting server record for agent '#{agent_hash[:id]}'") 56 | node[:ossec][:agents].delete(agent_hash[:id]) 57 | end 58 | 59 | # if this agent doesn't have a valid key, generate one 60 | if not ossec_agent_has_valid_key?(agent_hash, node) 61 | Chef::Log.info("OSSEC: agent '#{agent_hash[:id]}' needs a key. Generating one.") 62 | agent_hash[:key] = ossec_generate_agent_key(agent_hash) 63 | agent_hash[:rid] = "todo" 64 | agent_hash[:status] = "key_exists" 65 | end 66 | 67 | # save agent parameters 68 | agent_hash.each do |k,v| 69 | node.set[:ossec][:agents][agent_hash[:id]][k] = v 70 | end 71 | 72 | # Don't continue if the agent has a valid key but doesn't know it yet 73 | if not ossec_agent_knows_key?(agent_hash, agent) 74 | Chef::Log.info("OSSEC: agent '#{agent_hash[:id]}' didn't pick up its key yet.") 75 | next 76 | end 77 | 78 | # Check if it needs a queue cleanup 79 | if ossec_agent_needs_rid?(agent_hash[:id], agent) 80 | ruby_block "ossec queue rid" do 81 | block do 82 | if File.exists?("/var/ossec/queue/rids/#{agent_hash[:id]}") 83 | File.delete("/var/ossec/queue/rids/#{agent_hash[:id]}") 84 | Chef::Log.info("OSSEC: deleted queue for agent '#{agent_hash[:id]}'") 85 | else 86 | Chef::Log.info("OSSEC: No queue for agent '#{agent_hash[:id]}.'") 87 | end 88 | node.set[:ossec][:agents][agent_hash[:id]][:rid] = "done" 89 | end 90 | notifies :restart, "service[ossec-server]" 91 | end 92 | # done with this agent, go to the next one 93 | next 94 | end 95 | 96 | # If after all that, the agent is still not active, mark it as so 97 | if not ossec_agent_is_active?(agent_hash[:id]) 98 | if ossec_agent_is_zombie?(agent_hash[:id]) 99 | node.set[:ossec][:agents][agent_hash[:id]][:status] = "zombie" 100 | # if the agent is a zombie, perform a rid of its queue on the next run 101 | Chef::Log.info("OSSEC: agent #{agent_hash[:id]} is a zombie. " + 102 | "Request queue deletion") 103 | node.set[:ossec][:agents][agent_hash[:id]][:rid] = "todo" 104 | else 105 | node.set[:ossec][:agents][agent_hash[:id]][:status] = "disconnected" 106 | Chef::Log.info("OSSEC: agent #{agent_hash[:id]} connection failed. " + 107 | "Performing restart") 108 | cmd = Chef::ShellOut.new("/var/ossec/bin/agent_control -R #{agent_hash[:id]}") 109 | cmd_ret = cmd.run_command 110 | end 111 | end 112 | end 113 | 114 | # Remove the attributes of an agent from the ossec server if the agent doesn't 115 | # exist on Chef and the last keep_alive is more than 7 days old 116 | node[:ossec][:agents].each do |agent_id, params| 117 | if params[:status].eql?('key_exists') 118 | next 119 | end 120 | 121 | agent = ossec_agents.select{ |n| (n[:ossec][:agents].has_key?(agent_id) \ 122 | && n[:ossec][:agent][:enable]) 123 | }.first 124 | 125 | if not agent.nil? 126 | next 127 | end 128 | if ossec_agent_should_be_removed?(agent_id) 129 | Chef::Log.info("OSSEC: Removing old agent '#{agent_id}' - '#{params[:name]}'") 130 | node[:ossec][:agents].delete(agent_id) 131 | else 132 | Chef::Log.info("OSSEC: agent '#{agent_id}' - '#{params[:name]}' is candidate for removal") 133 | node.set[:ossec][:agents][agent_id][:status] = 'candidate_for_removal' 134 | end 135 | end 136 | 137 | template "/var/ossec/etc/client.keys" do 138 | mode 0440 139 | owner "root" 140 | group "ossec" 141 | end 142 | 143 | template "/var/ossec/rules/local_rules.xml" do 144 | owner "root" 145 | group "root" 146 | notifies :restart, "service[ossec-server]" 147 | end 148 | 149 | template "/var/ossec/etc/local_decoder.xml" do 150 | owner "root" 151 | group "root" 152 | notifies :restart, "service[ossec-server]" 153 | end 154 | 155 | template "/var/ossec/etc/ossec.conf" do 156 | source "ossec-server.conf.erb" 157 | owner "ossec" 158 | group "ossec" 159 | variables( :ossec_agents => ossec_agents ) 160 | notifies :restart, "service[ossec-server]" 161 | end 162 | 163 | template "/var/ossec/etc/internal_options.conf" do 164 | mode 0444 165 | owner "root" 166 | group "root" 167 | notifies :restart, "service[ossec-server]" 168 | end 169 | -------------------------------------------------------------------------------- /recipes/supervisor_logs.rb: -------------------------------------------------------------------------------- 1 | # This recipe will list the log file created by supervisor 2 | # and add the necessary attributes to have those monitored 3 | # by the local ossec-agent 4 | 5 | # cleanup, before recreation 6 | node[:ossec][:local_syslog_files].each do |logfile, params| 7 | if logfile =~ /supervisor/ 8 | node[:ossec][:local_syslog_files].delete(logfile) 9 | end 10 | end 11 | 12 | # each program run by supervisor has a set of logfiles 13 | node[:supervisor][:programs].each do |program_name, config| 14 | total_procs = config[:numprocs] || 1 15 | # each process of a program has its own log file 16 | 0.upto(total_procs.to_i - 1) do |numproc| 17 | logfile = "#{node[:supervisor][:log_path]}/#{program_name}_#{numproc}_stdout.log" 18 | # add the stdout supervisor log file 19 | node[:ossec][:local_syslog_files][logfile] = { 20 | 'apply_to' => "fqdn:#{node.fqdn}", 21 | 'log_format' => 'syslog', 22 | 'use_here' => 'true' 23 | } 24 | log("Ossec::Supervisor: Adding '#{logfile}' to monitored log files") 25 | 26 | logfile = "#{node[:supervisor][:log_path]}/#{program_name}_#{numproc}_stderr.log" 27 | # add the stderr supervisor log file 28 | node[:ossec][:local_syslog_files][logfile] = { 29 | 'apply_to' => "fqdn:#{node.fqdn}", 30 | 'log_format' => 'syslog', 31 | 'use_here' => 'true' 32 | } 33 | log("Ossec::Supervisor: Adding '#{logfile}' to monitored log files") 34 | 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /templates/default/.local_rules.xml.erb.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jvehent/AutOssec/a9ff216430d6b6fb3af965aebb3e3af29eca46c3/templates/default/.local_rules.xml.erb.swp -------------------------------------------------------------------------------- /templates/default/client.keys.erb: -------------------------------------------------------------------------------- 1 | <% if not node[:ossec][:agents].nil? 2 | node[:ossec][:agents].sort.each do |ip,fields| 3 | if not fields[:key].nil? 4 | %> 5 | <%= fields[:id] %> <%= fields[:name] %> <%= fields[:ip] %> <%= fields[:key]%> 6 | <% end 7 | end 8 | end 9 | -%> 10 | -------------------------------------------------------------------------------- /templates/default/internal_options.conf.erb: -------------------------------------------------------------------------------- 1 | # internal_options.conf, Daniel B. Cid (dcid @ ossec.net). 2 | # 3 | # DO NOT TOUCH THIS FILE. The default configuration 4 | # is at ossec.conf. More information at: 5 | # http://www.ossec.net/en/manual.html 6 | # 7 | # This file should be handled with care. It contain 8 | # run time modifications that can affect the use 9 | # of ossec. Only change it if you know what you 10 | # are doing. Again, look first at ossec.conf 11 | # for most of the things you want to change. 12 | 13 | 14 | # Analysisd default rule timeframe. 15 | analysisd.default_timeframe=<%= node[:ossec][:internal][:analysisd][:default_timeframe] %> 16 | # Analysisd stats maximum diff. 17 | analysisd.stats_maxdiff=<%= node[:ossec][:internal][:analysisd][:stats_maxdiff] %> 18 | # Analysisd stats minimum diff. 19 | analysisd.stats_mindiff=<%= node[:ossec][:internal][:analysisd][:stats_mindiff] %> 20 | # Analysisd stats percentage (how much to differ from average) 21 | analysisd.stats_percent_diff=<%= node[:ossec][:internal][:analysisd][:stats_percent_diff] %> 22 | # Analysisd FTS list size. 23 | analysisd.fts_list_size=<%= node[:ossec][:internal][:analysisd][:fts_list_size] %> 24 | # Analysisd FTS minimum string size. 25 | analysisd.fts_min_size_for_str=<%= node[:ossec][:internal][:analysisd][:fts_min_size_for_str] %> 26 | # Analysisd Enable the firewall log (at logs/firewall/firewall.log) 27 | # 1 to enable, 0 to disable. 28 | analysisd.log_fw=<%= node[:ossec][:internal][:analysisd][:log_fw] %> 29 | 30 | 31 | # Logcollector file loop timeout (check every 2 seconds for file changes) 32 | logcollector.loop_timeout=<%= node[:ossec][:internal][:logcollector][:loop_timeout] %> 33 | 34 | # Logcollector number of attempts to open a log file. 35 | logcollector.open_attempts=<%= node[:ossec][:internal][:logcollector][:open_attempts] %> 36 | 37 | 38 | # Remoted counter io flush. 39 | remoted.recv_counter_flush=<%= node[:ossec][:internal][:remoted][:recv_counter_flush] %> 40 | 41 | # Remoted compression averages printout. 42 | remoted.comp_average_printout=<%= node[:ossec][:internal][:remoted][:comp_average_printout] %> 43 | 44 | # Verify msg id (set to 0 to disable it) 45 | remoted.verify_msg_id=<%= node[:ossec][:internal][:remoted][:verify_msg_id] %> 46 | 47 | 48 | # Maild strict checking (0=disabled, 1=enabled) 49 | maild.strict_checking=<%= node[:ossec][:internal][:maild][:strict_checking] %> 50 | 51 | # Maild grouping (0=disabled, 1=enabled) 52 | # Groups alerts within the same e-mail. 53 | maild.groupping=<%= node[:ossec][:internal][:maild][:groupping] %> 54 | 55 | # Maild full subject (0=disabled, 1=enabled) 56 | maild.full_subject=<%= node[:ossec][:internal][:maild][:full_subject] %> 57 | 58 | 59 | # Monitord day_wait. Ammount of seconds to wait before compressing/signing 60 | # the files. 61 | monitord.day_wait=10<%= node[:ossec][:internal][:monitord][:day_wait] %> 62 | 63 | # Monitord compress. (0=do not compress, 1=compress) 64 | monitord.compress=<%= node[:ossec][:internal][:monitord][:compress] %> 65 | 66 | # Monitord sign. (0=do not sign, 1=sign) 67 | monitord.sign=<%= node[:ossec][:internal][:monitord][:sign] %> 68 | 69 | # Monitord monitor_agents. (0=do not monitor, 1=monitor) 70 | monitord.monitor_agents=<%= node[:ossec][:internal][:monitord][:monitor_agents] %> 71 | 72 | 73 | # Syscheck checking/usage speed. To avoid large cpu/memory 74 | # usage, you can specify how much to sleep after generating 75 | # the checksum of X files. The default is to sleep 2 seconds 76 | # after reading 15 files. 77 | syscheck.sleep=<%= node[:ossec][:internal][:syscheck][:sleep] %> 78 | syscheck.sleep_after=<%= node[:ossec][:internal][:syscheck][:sleep_after] %> 79 | 80 | 81 | # Database - maximum number of reconnect attempts 82 | dbd.reconnect_attempts=<%= node[:ossec][:internal][:dbd][:reconnect_attempts] %> 83 | 84 | 85 | # Debug options. 86 | # Debug 0 -> no debug 87 | # Debug 1 -> first level of debug 88 | # Debug 2 -> full debugging 89 | 90 | # Windows debug (used by the windows agent) 91 | windows.debug=<%= node[:ossec][:internal][:window][:debug] %> 92 | 93 | # Syscheck (local, server and unix agent) 94 | syscheck.debug=<%= node[:ossec][:internal][:syscheck][:debug] %> 95 | 96 | # Remoted (server debug) 97 | remoted.debug=<%= node[:ossec][:internal][:remoted][:debug] %> 98 | 99 | # Analysisd (server or local) 100 | analysisd.debug=<%= node[:ossec][:internal][:analysisd][:debug] %> 101 | 102 | # Log collector (server, local or unix agent) 103 | logcollector.debug=<%= node[:ossec][:internal][:logcollector][:debug] %> 104 | 105 | # Unix agentd 106 | agent.debug=<%= node[:ossec][:internal][:agent][:debug] %> 107 | 108 | 109 | # EOF 110 | -------------------------------------------------------------------------------- /templates/default/local_decoder.xml.erb: -------------------------------------------------------------------------------- 1 | <% node[:ossec][:decoders].sort_by{|k,v| k.to_i}.each do |id,params| 2 | if not params.nil? 3 | -%> 4 | 5 | <% 6 | params.sort.each do |key, value| 7 | unless key.eql?('name') 8 | if params[key].is_a?(Hash) 9 | if key.eql?('prematch') or key.eql?('regex') 10 | if params[key].has_key?('offset') 11 | -%> 12 | <<%=key%> offset="<%=params[key][:offset]%>"><%=params[key][:parser]%>> 13 | <% 14 | else 15 | -%> 16 | <<%=key%>><%=params[key][:parser]%>> 17 | <% end 18 | end 19 | else 20 | -%> 21 | <<%=key%>><%=value%>> 22 | <% end 23 | end 24 | end 25 | -%> 26 | 27 | <% 28 | end 29 | end 30 | -%> 31 | -------------------------------------------------------------------------------- /templates/default/local_rules.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | <% 3 | node[:ossec][:rules].sort_by{|k,v| k.to_i}.each do |id,params| 4 | headers = "" 5 | if params.has_key?('head') 6 | params[:head].sort.each do |key, value| 7 | headers += " " + key + "=\"" + value + "\"" 8 | end 9 | end 10 | -%> 11 | > 12 | <%if params.has_key?('body') 13 | params[:body].sort.each do |key, value| 14 | if not key.eql?('hostname_search') 15 | -%> 16 | <<%=key%>><%=value%>> 17 | <% 18 | end 19 | end 20 | end 21 | if params.has_key?('tags') 22 | params[:tags].sort.each do |tag| 23 | -%> 24 | <<%=tag%> /> 25 | <% end 26 | end 27 | if params.has_key?('info') 28 | params[:info].sort.each do |key, value| 29 | -%> 30 | <%=value%> 31 | <% end 32 | end 33 | -%> 34 | 35 | <% 36 | end 37 | -%> 38 | 39 | -------------------------------------------------------------------------------- /templates/default/ossec-agent.conf.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%=@ossec_server_ip%> 5 | 6 | 7 | 8 | 9 | <%=node[:ossec][:syscheck][:frequency]%> 10 | 11 | 12 | <%node[:ossec][:syscheck][:directories].sort_by {|k,v| k}.each do |directory,params| -%> 13 | <%=directory%> 14 | <%end-%> 15 | 16 | <%=node[:ossec][:syscheck][:alert_new_files]%> 17 | <%=node[:ossec][:syscheck][:auto_ignore]%> 18 | 19 | 20 | <%node[:ossec][:syscheck][:ignore].sort_by {|k| k}.each do |path| -%> 21 | <%=path%> 22 | <%end-%> 23 | 24 | <% 25 | if not node[:ossec][:syscheck][:local_ignore].nil? 26 | node[:ossec][:syscheck][:local_ignore].sort_by {|k,v| k}.each do |file,params| 27 | if params[:use_here] == "true" 28 | type = 'simple' 29 | unless params[:type].nil? 30 | if params[:type] == 'sregex' 31 | type = 'sregex' 32 | end 33 | end 34 | if type == 'sregex' 35 | -%> 36 | <%=file%> 37 | <% else -%> 38 | <%=file%> 39 | <% end 40 | end 41 | end 42 | end 43 | -%> 44 | 45 | 46 | 47 | /var/ossec/etc/shared/rootkit_files.txt 48 | /var/ossec/etc/shared/rootkit_trojans.txt 49 | 50 | 51 | 52 | <%node[:ossec][:syslog_files].sort_by {|k| k}.each do |file| -%> 53 | 54 | syslog 55 | <%=file%> 56 | 57 | <%end-%> 58 | 59 | <% 60 | if not node[:ossec][:local_syslog_files].nil? 61 | node[:ossec][:local_syslog_files].sort_by {|k,v| k}.each do |logfile,params| 62 | if params[:use_here] == "true" 63 | -%> 64 | 65 | <%=params[:log_format]%> 66 | <%=logfile%> 67 | 68 | <% 69 | end 70 | end 71 | end 72 | -%> 73 | 74 | -------------------------------------------------------------------------------- /templates/default/ossec-server.conf.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= node[:ossec][:email_notification] %> 5 | <% node[:ossec][:email_to].sort_by {|k| k}.each do |recipient| -%> 6 | <%= recipient %> 7 | <% end -%> 8 | <%= node[:ossec][:smtp_server] %> 9 | <%= node[:ossec][:email_from]%> 10 | <%=node[:ossec][:email_maxperhour]%> 11 | <%=node[:ossec][:memory_size]%> 12 | <% node[:ossec][:white_list].sort_by {|k| k}.each do |ip| -%> 13 | <%= ip %> 14 | <% end -%> 15 | 16 | 17 | 18 | 19 | rules_config.xml 20 | pam_rules.xml 21 | sshd_rules.xml 22 | syslog_rules.xml 23 | arpwatch_rules.xml 24 | symantec-av_rules.xml 25 | pix_rules.xml 26 | named_rules.xml 27 | smbd_rules.xml 28 | pure-ftpd_rules.xml 29 | proftpd_rules.xml 30 | ms_ftpd_rules.xml 31 | hordeimp_rules.xml 32 | vpopmail_rules.xml 33 | ids_rules.xml 34 | squid_rules.xml 35 | firewall_rules.xml 36 | netscreenfw_rules.xml 37 | postfix_rules.xml 38 | sendmail_rules.xml 39 | imapd_rules.xml 40 | mailscanner_rules.xml 41 | ms-exchange_rules.xml 42 | racoon_rules.xml 43 | vpn_concentrator_rules.xml 44 | spamd_rules.xml 45 | msauth_rules.xml 46 | attack_rules.xml 47 | ossec_rules.xml 48 | apache_rules.xml 49 | web_rules.xml 50 | local_rules.xml 51 | 52 | 53 | 54 | <%=node[:ossec][:remote][:connection]%> 55 | <%= node.ipaddress %> 56 | 57 | 58 | 59 | <%= node[:ossec][:syslog_output][:ip] %> 60 | <%= node[:ossec][:syslog_output][:port] %> 61 | <%= node[:ossec][:syslog_output][:min_level] %> 62 | 63 | 64 | 65 | <%= node[:ossec][:log_alert_level] %> 66 | <%= node[:ossec][:email_alert_level]%> 67 | 68 | 69 | 70 | authentication_success 71 | srcip 72 | Daily report: Successful logins 73 | <% node[:ossec][:email_to].sort_by {|k| k}.each do |recipient| -%> 74 | <%= recipient %> 75 | <% end -%> 76 | 77 | 78 | 79 | web 80 | Daily report: Web 81 | <% node[:ossec][:email_to].sort_by {|k| k}.each do |recipient| -%> 82 | <%= recipient %> 83 | <% end -%> 84 | 85 | 86 | 87 | Daily report: Level 7 88 | 7 89 | level 90 | <% node[:ossec][:email_to].sort_by {|k| k}.each do |recipient| -%> 91 | <%= recipient %> 92 | <% end -%> 93 | 94 | 95 | 96 | Daily report: Level 12 97 | 12 98 | level 99 | <% node[:ossec][:email_to].sort_by {|k| k}.each do |recipient| -%> 100 | <%= recipient %> 101 | <% end -%> 102 | 103 | 104 | 105 | syscheck 106 | Daily report: File changes 107 | filename 108 | <% node[:ossec][:email_to].sort_by {|k| k}.each do |recipient| -%> 109 | <%= recipient %> 110 | <% end -%> 111 | 112 | 113 | <% 114 | node[:ossec][:email_alerts].sort_by {|k,v| k}.each do |recipient,params| 115 | locations = [] 116 | if params.has_key?('event_location_tag') 117 | locations = @ossec_agents.select{ 118 | |n| n[:tags].include?( 119 | params[:event_location_tag] 120 | ) 121 | }.map {|n2| n2.network.lanip || '172.172.172.172'} 122 | elsif params.has_key?('resolved_search') 123 | locations = params[:resolved_search] 124 | end 125 | locations.sort_by {|k| k}.each do |location| 126 | -%> 127 | 128 | <%=recipient%> 129 | <%=location%> 130 | <% params.sort_by {|k,v| k}.each do |key, value| 131 | unless key =~ /event_location_tag|event_location_search|resolved_search/ 132 | if key.eql?('tags') 133 | value.sort_by {|k| k}.each do |tag| 134 | -%> 135 | <<%=tag%> /> 136 | <% end 137 | else 138 | -%> 139 | <<%=key%>><%=value%>> 140 | <% end 141 | end 142 | end 143 | -%> 144 | 145 | <% 146 | end 147 | end 148 | -%> 149 | 150 | 151 | 152 | <%=node[:ossec][:syscheck][:frequency]%> 153 | 154 | 155 | <%node[:ossec][:syscheck][:directories].sort_by {|k,v| k}.each do |directory,params| -%> 156 | <%=directory%> 157 | <%end-%> 158 | 159 | <%=node[:ossec][:syscheck][:alert_new_files]%> 160 | <%=node[:ossec][:syscheck][:auto_ignore]%> 161 | 162 | 163 | <%node[:ossec][:syscheck][:ignore].sort_by {|k| k}.each do |path| -%> 164 | <%=path%> 165 | <%end-%> 166 | 167 | <% 168 | if not node[:ossec][:syscheck][:local_ignore].nil? 169 | node[:ossec][:syscheck][:local_ignore].sort_by {|k| k}.each do |file,params| 170 | if params[:use_here] == "true" 171 | type = 'simple' 172 | unless params[:type].nil? 173 | if params[:type] == 'sregex' 174 | type = 'sregex' 175 | end 176 | end 177 | if type == 'sregex' 178 | -%> 179 | <%=file%> 180 | <% else -%> 181 | <%=file%> 182 | <% end 183 | end 184 | end 185 | end 186 | -%> 187 | 188 | 189 | 190 | 191 | /var/ossec/etc/shared/rootkit_files.txt 192 | /var/ossec/etc/shared/rootkit_trojans.txt 193 | 194 | 195 | 196 | <%node[:ossec][:syslog_files].sort_by {|k| k}.each do |file| -%> 197 | 198 | syslog 199 | <%=file%> 200 | 201 | <%end-%> 202 | 203 | <% 204 | if not node[:ossec][:local_syslog_files].nil? 205 | node[:ossec][:local_syslog_files].sort_by {|k,v| k}.each do |logfile,params| 206 | if params[:use_here] == "true" 207 | -%> 208 | 209 | <%=params[:log_format]%> 210 | <%=logfile%> 211 | 212 | <% 213 | end 214 | end 215 | end 216 | -%> 217 | 218 | --------------------------------------------------------------------------------