Migrating config files to new snort package

@efahl - Congratulations on getting your snort code merged. Can you summarize a quick upgrading guide/process for users of the old config?

I am still using this setup for my snort:

/etc/config/firewall
...
config include
  option enabled '1'
  option type 'script'
  option path '/etc/snort/snort-table.sh'
  option fw4_compatible '1'
/etc/snort/short-table.sh
#!/bin/sh

verbose=false

nft list tables | grep -q 'snort' && nft flush table inet snort

nft -f - <<TABLE
    table inet snort {
        chain IPS {
            type filter hook postrouting priority 225; policy accept;

      ct state invalid drop;

# Add here accept or drop rules to bypass snort or drop traffic that snort not should see
# Note that if nat is enabled, snort will only see the address of the outgoing device for outgoing traffic, 
# for example for wan port the wan ip address or if you are using vpn the device address of the virtual #adapter 

         oifname "eth1" tcp flags ack ct state established counter accept
  
#"eth0" must be changed to the appropriate wan port on the target system. A vpn needs a second rule with the name of the        virtual vpn wan port. 

     counter  queue flags bypass to 4-11


        }
    }
TABLE

$verbose && nft list table inet snort

exit 0
/etc/init.d/snort
#!/bin/sh /etc/rc.common

START=99
STOP=10

USE_PROCD=1
PROG=/usr/bin/snort

validate_snort_section() {
  uci_validate_section snort snort "${1}" \
    'config_dir:string' \
    'interface:string'
}

start_service() {
  local config_file interface

  validate_snort_section snort || {
    echo "validation failed"
    return 1
  }

  procd_open_instance
  procd_set_param env SNORT_LUA_PATH="$config_dir"
  procd_set_param command $PROG -q -c "${config_dir%/}/snort.lua" --tweaks local
  procd_set_param file $CONFIGFILE
  procd_set_param respawn
  procd_close_instance
}

stop_service()
{
  service_stop ${PROG}
}

service_triggers()
{
  procd_add_reload_trigger "snort"
  procd_add_validation validate_snort_section
}
/etc/snort/local.lua
snort  = {
  ['-Q'] = true,
  ['--max-packet-threads'] = 8,
}

suppress = {
  -- this kills stuff in lxc
  {
    gid = 1, sid = 650, track = 'by_dst', ip = '10.9.8.101'
  },
}

network = {
  checksum_eval = 'none',
}

daq = {
  module_dirs = { '/usr/lib/daq' },
  inputs = { '4', '5', '6', '7', '8', '9', '10', '11' },
  snaplen = 65531,
  modules = {
    {
      name = 'nfq',
      mode = 'inline',
      variables = {
        'queue_maxlen=8192',
        'fail_open',
        'fanout_type=LB',
        'device=eth1'
      }
    }
  }
}

ips = {
  mode = inline,
  variables = default_variables,
  action_override = 'drop',
  include = RULE_PATH .. '/snort.rules',
  --include = RULE_PATH .. '/test.rules',
}

output.logdir = '/mnt/data'
alert_fast = {
  file = true,
  packet = false,
}

file_policy = {
  enable_type = true,
  enable_signature = true,
  rules = {
    use = {
      verdict = 'log', enable_file_type = true, enable_file_signature = true
    }
  }
}

search_engine = {
  search_method = "hyperscan",
  offload_search_method ="hyperscan",
  detect_raw_tcp = true,
}

detection = {
  hyperscan_literals = true,
  pcre_to_regex = true,
}
1 Like

Thanks! Now for the fun part, writing all the docs (just when the wiki server gets attacked😟).

The quickest and easiest way to start is just set the manual option in the config to 1 (edit: also enabled to 1) and then make your mod to /etc/init.d/snort to remove that -i "$interface" clause out of the command line:

  procd_set_param command $PROG -q -i "$interface" -c "${config_dir%/}/snort.lua" --tweaks local
becomes
  procd_set_param command $PROG -q -c "${config_dir%/}/snort.lua" --tweaks local

then restart snort.

I stole the idea for the manual option from stubby, it basically just ignores the config (well, actually it uses the two legacy options config_dir and interface) and uses the installed lua config files as before.

I need some time to pick through your existing config, see where I've got holes in the new stuff. One obvious place is that due to the way I'm including the generated snort.lua, snort's --tweaks option is broken, so I need to address that to get things working. Give me a bit, I'll get to you here when I figure some things out...

1 Like

I think I've got everything working locally with your configs (well, without the hyperscan stuff), but I need to submit a PR to get one missing piece rolled in.

There are going to be two parts here

  1. Reproducing your nftables mods. This is already easily accomplished in the released version, so you can try it out now, details below.

  2. Reproducing your local.lua mods, which requires some new code that I'll put in a PR either tonight or in the morning (depending on how I feel about my testing).

To get #1 out of the way,

  1. In /etc/config/snort, change the following values. (There will be a bunch more changes, but these are the ones specifically related to generating the nftables.)
    snort
        method         = 'nfq'
        interface      = 'eth1'
    nfq
        queue_count    = '8'
        chain_type     = 'postrouting'
        chain_priority = '300'
        include        = '/etc/snort/include.nfq'
  1. Copy the guts of your snort-table.sh into /etc/snort/include.nfq. Note that I changed the hardcoded eth1 to {{ snort.interface }}, because your include file is actually a ucode template (the tool jow wrote for implementing fw4). This sucks the value out of the config file for section.option and expands it into the included text. (The snort lua include will be doing the same thing in the upcoming PR.)
    ct state invalid drop;

    # Add here accept or drop rules to bypass snort or drop traffic that snort not should see
    # Note that if nat is enabled, snort will only see the address of the outgoing device for outgoing traffic,
    # for example for wan port the wan ip address or if you are using vpn the device address of the virtual #adapter

    oifname "{{ snort.interface }}" tcp flags ack ct state established counter accept

    #"eth0" must be changed to the appropriate wan port on the target system. A vpn needs a second rule with the name of the        virtual vpn wan port.
  1. When we're ready to deploy this (not right now), you'll need to edit /etc/config/firewall and remove the include ... script section altogether; the creation and deletion of the tables is all done by snort-mgr now.

  2. You can safely do #1 and #2 right now, leaving the manual option set to '1' so we don't mess up your working system, then show the generated table so you can see if it looks right

$ snort-mgr print nftables
... the table spewed to the screen ...

$ snort-mgr print nftables | nft --check --debug netlink -f -
... some debug output if everything works, error messages otherwise...

My pending PR adds some more/better error checking for user include files, too, to make it easier to isolate anything that goes wrong there.

Ok, I got energetic (or maybe the opposite and just gave up? :grin:) and dropped the PR.

Thank you for the HIGHLY DETAILED reply! I will grab your PR and build when I get some time in a day or so as well as incorporate the suggestions you made.

Alrighty then, I've got (I think) all of your config set for use with that PR.

Your values for home_net and external_net should replace mine, they are no longer in a lua file, but in the config:

/etc/config/snort
config snort 'snort'
        option enabled         '1'              # one of [0, 1]
        option manual          '0'              # one of [0, 1]
        option oinkcode        ''               # a string
        option home_net        '10.1.1.0/24 192.168.1.0/24' # a string
        option external_net    'any'            # a string
        option config_dir      '/etc/snort'     # a path string
        option temp_dir        '/var/snort.d'   # a path string
        option log_dir         '/mnt/data'      # a path string
        option logging         '1'              # one of [0, 1]
        option openappid       '0'              # one of [0, 1]
        option mode            'ips'            # one of [ids, ips]
        option method          'nfq'            # one of [pcap, afpacket, nfq]
        option action          'drop'           # one of [alert, block, drop, reject]
        option interface       'eth1'           # a string
        option snaplen         '65531'          # 1518 <= x <= 65535
        option include         '/etc/snort/include.snort'

config nfq 'nfq'
        option queue_count     '8'              # 1 <= x <= 16
        option queue_start     '4'              # 1 <= x <= 32768
        option queue_maxlen    '8192'           # 1024 <= x <= 65536
        option fanout_type     'lb'             # one of [hash, lb, cpu, rollover, rnd, qm]
        option thread_count    '8'              # 0 <= x <= 32
        option chain_type      'postrouting'    # one of [prerouting, input, forward, output, postrouting]
        option chain_priority  '300'            # one of [raw, filter, 300]
        option include         '/etc/snort/include.nfq'

And in the snort config mods, you can disable the event logging by setting to nil, or disable by adding { } with any parameters that seem appropriate (see snort-mgr print snort below, for embedded comments on json). By default, my config generator creates the syslog and alert_json output, and it's extremely minimal. You appeared to be suppressing the syslog output, hence the first line below. I also added back the no-longer-generated alert_text, in case you were using that.

/etc/snort/include.snort
alert_syslog = nil -- Disable output to syslog
alert_fast = {     -- Enable output to alert_fast.txt
  file = true,
  packet = false,
}

-- This section modifies the json output to be compatible with 'snort-mgr report',
--  but includes all the fields you would see when using 'alert_fast'.
--alert_json = {
--  fields = 'timestamp pkt_num proto pkt_gen pkt_len dir src_ap dst_ap rule action msg',
--  file = true,
--}

suppress = {
  -- this kills stuff in lxc
  {
    gid = 1, sid = 650, track = 'by_dst', ip = '10.9.8.101'
  },
}

network = {
  checksum_eval = 'none',
}

search_engine = {
  search_method = "hyperscan",
  offload_search_method ="hyperscan",
  detect_raw_tcp = true,
}

detection = {
  hyperscan_literals = true,
  pcre_to_regex = true,
}

Once you've got a build with that PR, then you should be able to do snort-mgr check to verify that everything looks good. This runs the generated configs through both snort -T and nft --check to verify that they are at least syntactically correct.

You can also use snort-mgr print xxx to see the various generated files.

$ snort-mgr print nftables  # Shows the nftables, as described in prev post.
$ snort-mgr print snort     # Shows the snort_conf.lua, to compare with old snort.lua + local.lua.

$ snort-mgr print config    # Reads /etc/config/snort and spits out a new copy with all defaults filled in.

I found two bugs in the PR, so just pushed a new version a couple minutes ago.

  1. Didn't check for existence of the logging directory, so service would crash on startup if it didn't already exist (default is /var/logs/, so that never triggered the crash). Now we check and create on the fly.
  2. In snort-mgr report I was grabbing the correct logging dir from the config, but then ignoring it and using a hard-coded /var/logs/ as the data source. Duh.

@efahl - I pulled the pending PR and built snort3.

I don't think it's working. If I replace my full /etc/snort/rules/snort.rules with this single rule which drops ping packets (shown below), I still ping and get results:

alert icmp any any <> any any (msg:"TEST ALERT"; icode:0; itype:8; sid:10000010; rev:001;)

For reference:

/etc/config/snort
config snort 'snort'
        option enabled         '1'              # one of [0, 1]
        option manual          '0'              # one of [0, 1]
        option oinkcode        ''               # a string
        option home_net        '10.9.8.0/24 10.9.7.0/24 10.9.6.0/24 10.9.5.0/24 10.200.200.0/24'
        option external_net    '!$HOME_NET'
        option config_dir      '/etc/snort'     # a path string
        option temp_dir        '/var/snort.d'   # a path string
        option log_dir         '/mnt/data'      # a path string
        option logging         '1'              # one of [0, 1]
        option openappid       '0'              # one of [0, 1]
        option mode            'ips'            # one of [ids, ips]
        option method          'nfq'            # one of [pcap, afpacket, nfq]
        option action          'drop'           # one of [alert, block, drop, reject]
        option interface       'eth1'           # a string
        option snaplen         '65531'          # 1518 <= x <= 65535
        option include         '/etc/snort/include.snort'

config nfq 'nfq'
        option queue_count     '8'              # 1 <= x <= 16
        option queue_start     '4'              # 1 <= x <= 32768
        option queue_maxlen    '8192'           # 1024 <= x <= 65536
        option fanout_type     'lb'             # one of [hash, lb, cpu, rollover, rnd, qm]
        option thread_count    '8'              # 0 <= x <= 32
        option chain_type      'postrouting'    # one of [prerouting, input, forward, output, postrouting]
        option chain_priority  '300'            # one of [raw, filter, 300]
        option include         '/etc/snort/include.nfq'
/etc/snort/include.nfq
ct state invalid drop;

# Add here accept or drop rules to bypass snort or drop traffic that snort not should see
# Note that if nat is enabled, snort will only see the address of the outgoing device for outgoing traffic, 
# for example for wan port the wan ip address or if you are using vpn the device address of the virtual #adapter 

oifname "{{ snort.interface }}" tcp flags ack ct state established counter accept
  
#"eth0" must be changed to the appropriate wan port on the target system. A vpn needs a second rule with the name of the virtual vpn wan port. 
/etc/snort/include.snort
-- Disable output to syslog
alert_syslog = nil

-- Enable output to alert_fast.txt
alert_fast = {
  file = true,
  packet = false,
}

-- This section modifies the json output to be compatible with 'snort-mgr report',
--  but includes all the fields you would see when using 'alert_fast'.
--alert_json = {
--  fields = 'timestamp pkt_num proto pkt_gen pkt_len dir src_ap dst_ap rule action msg',
--  file = true,
--}

suppress = {
  -- this kills stuff in lxc
  {
    gid = 1, sid = 650, track = 'by_dst', ip = '10.9.8.101'
  },
}

network = {
  checksum_eval = 'none',
}

search_engine = {
  search_method = "hyperscan",
  offload_search_method ="hyperscan",
  detect_raw_tcp = true,
}

detection = {
  hyperscan_literals = true,
  pcre_to_regex = true,
}

Note that I did comment out the legacy lines in /etc/config/firewall per your instructions:

...
#config include
# option enabled '1'
# option type 'script'
# option path '/etc/snort/snort-table.sh'
# option fw4_compatible '1'

Gaah! Typos in the snort config template. (Plus bugs in the report generation when using > 1 queue with nfq...) Not sure why the lua check didn't flag these as syntax errors, lua is not real great like that.

Try hacking this into the snort.uc file:

$ vi /usr/share/snort/templates/snort.uc

... starting at line 52, there are missing apostrophes around the 
... line_mode and snort.action values:
 52 ips = {
 53   mode            = '{{ line_mode }}',
 54   variables       = default_variables,
 55   action_override = '{{ snort.action }}',

... likewise at line 66:
 66       mode      = '{{ line_mode }}',

... save and exit
$ /etc/init.d/snort restart

I'm going to do some more testing, then push this into the PR.

Thanks a ton for testing this!

Ok, fixes pushed in the PR...

Rather than modifying by hand, I just rebuilt using your PR from c29f544 which you pushed 15 min ago. Now using my rule above, pings are blocked. It seems to be working as expected!

GREAT JOB ON THIS!

Question: How can I disable the creation of json files?

-rw------- 1 root   root       0 Dec  8 12:51 0_alert_fast.txt
-rw------- 1 root   root       0 Dec  8 12:51 0_alert_json.txt
-rw------- 1 root   root       0 Dec  8 12:51 1_alert_fast.txt
-rw------- 1 root   root       0 Dec  8 12:51 1_alert_json.txt
-rw------- 1 root   root     111 Dec  8 12:52 2_alert_fast.txt
-rw------- 1 root   root      92 Dec  8 12:52 2_alert_json.txt
-rw------- 1 root   root       0 Dec  8 12:51 3_alert_fast.txt
-rw------- 1 root   root       0 Dec  8 12:51 3_alert_json.txt
-rw------- 1 root   root       0 Dec  8 12:51 4_alert_fast.txt
-rw------- 1 root   root       0 Dec  8 12:51 4_alert_json.txt
-rw------- 1 root   root       0 Dec  8 12:51 5_alert_fast.txt
-rw------- 1 root   root       0 Dec  8 12:51 5_alert_json.txt
-rw------- 1 root   root    1.1K Dec  8 12:52 6_alert_fast.txt
-rw------- 1 root   root     891 Dec  8 12:52 6_alert_json.txt
-rw------- 1 root   root       0 Dec  8 12:51 7_alert_fast.txt
-rw------- 1 root   root       0 Dec  8 12:51 7_alert_json.txt

Go into /etc/snort/include.snort and add one line:

alert_json = nil

Two notes:

  1. When you restart snort, it appends existing files, so you'll have to manually rm /mnt/data/*json.txt to get rid of them.

  2. The snort-mgr report now uses the json files, but I assume you have your own report/alert-scanner, so this shouldn't matter.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.