├── .gitignore ├── Dockerfile_host ├── Dockerfile_inclusive ├── Dockerfile_show ├── LICENSE ├── README.md ├── config.go ├── globals.go ├── graphics ├── Netgate.png └── tnsrids.png ├── parser.go ├── restconf.go ├── server.go ├── snort.conf ├── tnsr_snort_setup.md ├── tnsrids-docker.service ├── tnsrids.conf ├── tnsrids.go ├── tnsrids.service └── tnsrids_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | tnsrids 2 | tnsrids.log 3 | ca.crt 4 | tnsr.crt 5 | tnsr.key 6 | 7 | -------------------------------------------------------------------------------- /Dockerfile_host: -------------------------------------------------------------------------------- 1 | # The container is run specifying a directory on the host as a container mount. THe config file may then be read from that mount and should contain 2 | # resource specifications (ca, cert, key) that are also located in the mounted directory 3 | 4 | FROM ubuntu:latest 5 | 6 | COPY tnsrids /usr/local/sbin/ 7 | 8 | CMD /bin/bash /usr/local/sbin/tnsrids -------------------------------------------------------------------------------- /Dockerfile_inclusive: -------------------------------------------------------------------------------- 1 | # All of the resources required by tnsrids are copied to the container from the directory in which the docker build command is run. 2 | 3 | FROM ubuntu:latest 4 | 5 | COPY tnsrids /usr/local/sbin/ 6 | COPY tnsrids.conf /etc/tnsrids/ 7 | COPY ca.crt /etc/tnsrids/.tls/ 8 | COPY tnsr.crt /etc/tnsrids/.tls/ 9 | COPY tnsr.key /etc/tnsrids/.tls/ 10 | 11 | # CMD ["/tnsrids"] -------------------------------------------------------------------------------- /Dockerfile_show: -------------------------------------------------------------------------------- 1 | # This Dockerfile creates an image that uses tnsrids to print the current firewall ACL list then quit. 2 | # It is useful for testing the Docker configuration 3 | 4 | FROM ubuntu:latest 5 | 6 | COPY tnsrids /usr/local/sbin/ 7 | COPY tnsrids.conf /etc/tnsrids/ 8 | COPY ca.crt /etc/tnsrids/.tls/ 9 | COPY tnsr.crt /etc/tnsrids/.tls/ 10 | COPY tnsr.key /etc/tnsrids/.tls/ 11 | 12 | CMD ["tnsrids", "-show"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018-2019 Rubicon Communications, LLC (Netgate) 2 | * All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TNSR-IDS Project 2 | ## Introduction 3 | Netgate's TNSR is a high-speed packet processing engine. It enables secure networking at speeds up to 100+ Gbps - at a fraction of the price of proprietary alternatives. You can learn more about TNSR here: https://www.netgate.com/products/tnsr/ 4 | 5 | This project was developed as a solution to those deployments where 'wire speed' was not possible because the IDS/IPS consumed too much CPU on the firewall. IPS vs firewall need not be a zero-sum game. 6 | 7 | As a software-defined device, TNSR operations are defined by YANG models and it is configured either from a CLI or NETCONF/RESTCONF APIs. 8 | 9 | The tnsrids project demonstrates remote configuration via RESTCONF by automatically adding Access Control List (ACL) block rules in response to alert messages received from an instance of Snort running in intrusion detection mode. 10 | 11 | ![Netgate](graphics/tnsrids.png) 12 | 13 | ## Setup 14 | ERSPAN is configured to forward a copy of all WAN port traffic to SNORT. SNORT analyzes the traffic and produces alert messages in syslog format when intrusions are detected. Those alert messages are tramsmitted to tnsrids which emulates a syslog server, listening on a configurable UDP port. 15 | 16 | When tnsrids receives an alert message, the source IP address is extracted and a new ACL rule is constructed to block traffic from that source. The program then installs the rule in the TNSR ACL causing traffic from that source to be blocked. 17 | 18 | The block rules added in this way have a lifetime of 60 minutes (configurable) and are automatically deleted after that time 19 | tnsrids/TNSR/SNORT can be used in several topologies: All on one machine, each on a different machine, multiple instances of SNORT all feeding the same tnsrids, or any permutation thereof. 20 | 21 | Instructions for setting up TNSR and SNORT to accompany tnsrids can be found here: [tnsr_snort_setup.md](tnsr_snort_setup.md) 22 | 23 | ## References 24 | * SNORT: https://www.snort.org 25 | * ERSPAN: https://packetpushers.net/erspan-new-favorite-packet-capturing-trick/ 26 | * RESTCONF: http://sdntutorials.com/what-is-restconf/ 27 | * YANG: https://en.wikipedia.org/wiki/YANG 28 | 29 | # Use and Configuration of tnsrids 30 | 31 | ## Running 32 | Several command line switches are supported: 33 | * `-v` Verbose mode. Outputs useful (maybe) messages while running 34 | * `-p` UDP port to listen on for Syslog messages (Defaults to 12345) 35 | * `-h` Specify TNSR RESTCONF address (Defaults to localhost) 36 | * `-c` Configuration file location (Defaults to /etc/tnsrids/tnsrids.conf) 37 | * `-m` Maximum age of added rules in minutes before deletion (Defaults to 60, 0 = never) 38 | * `-show` Display the current ACL in table format and quit 39 | * `-reap` Delete ACL rules older than minutes (default=60) 40 | * `-ca` TLS Certificate authority file path (Defaults to /etc/tnsrids/.tls/ca.crt) 41 | * `-cert` TLS Certificate file path (Defaults to /etc/tnsrids/.tls/tnsr.crt) 42 | * `-key` TLS key file path (Defaults to /etc/tnsrids/.tls/tnsr.key) 43 | 44 | ## Configuration file 45 | Several options may be set via configuration file. The default location is **/etc/tnsrids/tnsrids.conf**, but that can be overridden on the command line with the `-c` switch 46 | 47 | Currently these values may be set. More will follow: 48 | * `host` (location of TNSR instance - including protocol) 49 | * `port` (UDP port to listen on) 50 | * `maxage` (Maximum age of rules before they are reaped, 0 = never) 51 | * `ca` (Location ofcertificate authority file) 52 | * `cert` (Location of TLS client certificate) 53 | * `key` (Location of TLS key) 54 | 55 | The configuration keys are case insensitive. See the sample tnsrids.conf for more details 56 | 57 | When a configuration value is provided on the command line AND in the config file, the command line wins. 58 | 59 | ## Building 60 | ### To create go.mod 61 | go mod init gitlab.netgate.com/TNSR/tnsr_ids 62 | 63 | ### To build natively 64 | go build 65 | 66 | ### To cross-compile: 67 | GOOS=linux GOARCH=amd64 go build 68 | 69 | Or other OS/architectures as needed. A list of available target OS/ARCH can be found here: 70 | https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63 71 | 72 | ### To build without debug info or labels 73 | Append `-ldflags "-s -w"` to the build command 74 | 75 | ## Required external packages 76 | Two external packages are required. 77 | * github.com/robfig/cron 78 | * gopkg.in/natefinch/lumberjack.v2 79 | 80 | [Cron](http://github.com/robfig/cron) provides timed execution (obviously) and [Lumberjack](https://gopkg.in/natefinch/lumberjack.v2) provides log rotation and pruning. 81 | 82 | ## Installation 83 | **tnsrids** can be run as an application or a service. To install it as a service, copy **tnsrids** to **/usr/local/sbin**, 84 | copy the file **tnsrids.service** to **/lib/systemd/system**, then type: 85 | 86 | sudo systemctl enable tnsrids 87 | sudo systemctl start tnsrids 88 | 89 | ## Firewall considerations 90 | TNSR version 19.02 and later ships with nftables enabled and configured. If the TNSR-IDS utility is run on the same machine as the TNSR instance a rule MUST be added to allow TNSR-IDS to receive the UDP datagrams produced by Snort. Specifying the UDP port you have configured TNSR-IDS to listen on (12345 used in this example) add a rule like so: 91 | 92 | sudo nft add rule inet tnsr_filter tnsr_input_mgmt_default udp dport 12345 accept 93 | 94 | You can verify that the rule has been added by listing all tables: 95 | 96 | sudo nft list table inet tnsr_filter -a 97 | 98 | ## TLS authentication 99 | Best practices dictate that TLS authentication is used to connect to the TNSR RESTCONF interface. Three files are required to authenticate in this way: A certificate authority, a client certificate and a key. The location of those files may be specified in the config file or on the command line. The default location is **/etc/tnsrids/.tls/** - The full path and filename is required for each file. 100 | 101 | If tnsrids is running on the same machine as TNSR TLS authentication may not needed. In that case, specifying a TNSR address with "http://" rather than "HTTPS://" will disable TLS negotiation 102 | 103 | ## Testing 104 | The command `go test -v` will exectute the program unit tests (in **tnsrids_test.go**) - Tests are provided for various utility functions. It would be possible to provide Go tests for the network pieces too, but "standard" test tools such as cURL and netcat are simpler and "standard" is a good thing. 105 | 106 | ## Running tnsrids in a Docker container 107 | There are a number of ways in which tnsrids can be run in a Docker container. These include: 108 | * Include all of the resources required by tnsrids (config file and certificates) in the Docker image 109 | * Leave the resource files in a directory on the host machine, mounting the directory inside the container and passing in configuration options via command line options 110 | 111 | ### Inclusive method 112 | The "include the resources in the image" method is the most simple. Assuming that Docker has been installed on the host system (`sudo yum install docker` or equivalent), perform the following steps: 113 | 1. Build a copy of the tnsrids utility for a Linux target `GOOS=linux GOARCH=amd64 go build tnsrids` 114 | 2. Create a directory to hold everything required by the Docker image and place the following files therein: 115 | * The newly built Linux tnsrids 116 | * The configuration file tnsrids.conf 117 | * ca.crt 118 | * tnsrids.crt 119 | * tnsrids.key 120 | * The file Dockerfile_inclusive from this repository, which MUST be renamed to simply "Dockerfile" 121 | 3. Now change directories to the new directory and build the Docker image with `docker build --tag tnsrids .` 122 | 123 | **IMPORTANT NOTE** 124 | The file names used for the configuration file, ca, certificate and key must all match the names used in the Dockerfile and in the configuration file. 125 | 126 | To run the Docker image use the command `docker run -p 12345:12345/udp tnsrids:latest` where the published port corresponds to the UDP port in the tnsrids configuration file. 127 | 128 | 129 | ### Host based configuration 130 | 1. Build a copy of the tnsrids utility for a Linux target `GOOS=linux GOARCH=amd64 go build tnsrids` 131 | 2. Create a directory to hold everything required by the Docker image and place the following files therein: 132 | * The newly built Linux tnsrids 133 | * The configuration file tnsrids.conf 134 | * ca.crt 135 | * tnsrids.crt 136 | * tnsrids.key 137 | * The file Dockerfile_host from this repository, which MUST be renamed to simply "Dockerfile" 138 | 3. Edit the Dockerfile changing the location of the certificate, ca and key files to /mnt/tnsrids 139 | 4. Now change directories to the new directory and build the Docker image with `docker build --tag tnsrids .` 140 | 141 | To run the Docker image use the command: 142 | `docker run :/mnt/tnsrids -p 12345:12345/udp tnsrids:latest tnsrids -c /mnt/tnsrids.conf` 143 | 144 | This command tells Docker to mount the directory you made inside the container as /mnt/tnsrids and then to read the configuration file from that directory. 145 | 146 | ### Running a Docker container as a service at start-up 147 | Please refer to this [Red Hat systemd guide](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_atomic_host/7/html/managing_containers/running_containers_as_systemd_services_with_podman#starting_containers_with_systemd) 148 | The tnsrids-docker.service file in this repository is correctly configured per the website example 149 | 150 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018-2019 Rubicon Communications, LLC (Netgate) 2 | * All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // config.go contaains all of the funtions required to process the comamnd line arguments, config file values and defaults 18 | // This was started as an exercise to learn Go flags, methods, structures and maps, but has turned out to be useful here 19 | // This file can be moved to its own package, or incorporated in another project as here. 20 | package main 21 | 22 | import ( 23 | "bufio" 24 | "errors" 25 | "flag" 26 | "fmt" 27 | "log" 28 | "os" 29 | "strings" 30 | ) 31 | 32 | // Configuration defaults 33 | const dfltConf string = "/etc/tnsrids/tnsrids.conf" 34 | const dfltHost string = "https://localhost" // Address of TNSR instance 35 | const dfltMaxage string = "60" // Maximum age of rules before they are reap()-ed 36 | const dfltPort string = "12345" // Default UDP port on whic alert messages are received 37 | const dfltCA string = "/etc/tnsrids/.tls/ca.crt" // Default location of TLS ertificates 38 | const dfltCert string = "/etc/tnsrids/.tls/tnsr.crt" 39 | const dfltKey string = "/etc/tnsrids/.tls/tnsr.key" 40 | 41 | // A Config is a list of configuration items that specify the option details 42 | type Config struct { 43 | // filename string 44 | items []ConfigItem 45 | } 46 | 47 | type ConfigItem struct { 48 | name string // The name of this config item (used as a map key) 49 | arg string // Command line argument that sets it 50 | hasval bool // Does this command line flag have an associated value string 51 | descr string // Description of the item used in constructing usage/help 52 | dflt string // Default value for this item 53 | } 54 | 55 | // Add a new config item specification to the configuration parser 56 | func (cfg *Config) addOption(name string, arg string, hasval bool, descr string, dflt string) { 57 | cfg.items = append(cfg.items, ConfigItem{name, arg, hasval, descr, dflt}) 58 | } 59 | 60 | // Print a table of options and help strings 61 | func (cfg Config) printUsage(title string) { 62 | option := "" 63 | 64 | fmt.Println(title) 65 | for idx := 0; idx < len(cfg.items); idx++ { 66 | if len(cfg.items[idx].arg) == 0 { 67 | continue 68 | } 69 | 70 | if cfg.items[idx].hasval { 71 | option = fmt.Sprintf(" -%s <%s>", cfg.items[idx].arg, cfg.items[idx].name) 72 | } else { 73 | option = fmt.Sprintf(" -%s", cfg.items[idx].arg) 74 | } 75 | 76 | fmt.Printf(" %-20s : %s\n", option, cfg.items[idx].descr) 77 | } 78 | } 79 | 80 | // Read the command line arguments 81 | // Read the config file values 82 | // Combine the two plus the defaults 83 | func (cfg *Config) read() map[string]string { 84 | cfgpath := "" 85 | 86 | // These two options are added by default so the program knows where to find the config file 87 | // and can provide help 88 | cfg.addOption("help", "help", false, "Output usage information to the console", "no") 89 | cfg.addOption("cfgpath", "c", true, "Path to configuration file", dfltConf) 90 | 91 | argmap := cfg.readArgs() 92 | 93 | if len(argmap["cfgpath"]) > 0 { 94 | cfgpath = argmap["cfgpath"] 95 | } else { 96 | cfgpath = dfltConf 97 | } 98 | 99 | confmap, err := readConfigFile(cfgpath) 100 | if err != nil { 101 | log.Printf("%v", err) 102 | } 103 | 104 | return cfg.mergeItems(argmap, confmap) 105 | } 106 | 107 | // Read the command line arguments by creating a flag entry for each option, then parsing the flags 108 | func (cfg Config) readArgs() map[string]string { 109 | args := make(map[string]*string) 110 | boolargs := make(map[string]*bool) 111 | combo := make(map[string]string) 112 | 113 | // Options expecting sting arguments, and boolean options (which do not) are added differently 114 | for idx := 0; idx < len(cfg.items); idx++ { 115 | if cfg.items[idx].hasval { 116 | args[cfg.items[idx].name] = flag.String(cfg.items[idx].arg, "", cfg.items[idx].descr) 117 | } else { 118 | boolargs[cfg.items[idx].name] = flag.Bool(cfg.items[idx].arg, false, cfg.items[idx].descr) 119 | } 120 | } 121 | 122 | flag.Parse() 123 | 124 | // Now that there is a map of pointers to command line options, translate that to a map of strings 125 | for k, v := range boolargs { 126 | if *v { 127 | combo[k] = "yes" 128 | } else { 129 | combo[k] = "no" 130 | } 131 | } 132 | 133 | for k, v := range args { 134 | combo[k] = *v 135 | } 136 | 137 | return combo 138 | } 139 | 140 | // If a command line argument is provided, use it, otherwise use the config file value or the default 141 | func merge(arg string, conf string, dflt string) string { 142 | if len(arg) == 0 { 143 | if len(conf) != 0 { 144 | return conf 145 | } else { 146 | return dflt 147 | } 148 | } 149 | 150 | return arg 151 | } 152 | 153 | // Iterate over the list of options, merging the command line, config file and defaults 154 | func (cfg Config) mergeItems(args map[string]string, conf map[string]string) map[string]string { 155 | mergedmap := make(map[string]string) 156 | 157 | for _, ci := range cfg.items { 158 | mergedmap[ci.name] = merge(args[ci.name], conf[ci.name], ci.dflt) 159 | } 160 | 161 | return mergedmap 162 | } 163 | 164 | // Debug func to print the current options 165 | func (cfg Config) printOpts() { 166 | args := cfg.read() 167 | 168 | for k, v := range args { 169 | fmt.Printf("%s : %s\n", k, v) 170 | } 171 | } 172 | 173 | // Read a config file and return its contents in a map 174 | // There are many Go config file packages available, but most are more complicated than needed here 175 | func readConfigFile(filename string) (map[string]string, error) { 176 | cfg := make(map[string]string) 177 | 178 | file, err := os.Open(filename) 179 | if err != nil { 180 | return cfg, errors.New("Unable to open configuration file. Using default values") 181 | } 182 | 183 | defer file.Close() 184 | 185 | scanner := bufio.NewScanner(file) 186 | 187 | for scanner.Scan() { 188 | // Ignore comment lines 189 | if strings.HasPrefix(scanner.Text(), "#") { 190 | continue 191 | } 192 | 193 | s := strings.SplitN(scanner.Text(), "=", 2) 194 | // Ignore mal-formed lines 195 | if len(s) != 2 { 196 | continue 197 | } 198 | 199 | // Trim white space from front and back, delete any quotes and make the key lower case 200 | cfg[strings.ToLower(strings.TrimSpace(s[0]))] = strings.Replace(strings.TrimSpace(s[1]), "\"", "", -1) 201 | } 202 | 203 | if err := scanner.Err(); err != nil { 204 | log.Fatal(err) 205 | } 206 | 207 | return cfg, nil 208 | } 209 | -------------------------------------------------------------------------------- /globals.go: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018-2019 Rubicon Communications, LLC (Netgate) 2 | * All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "crypto/tls" 21 | "sync" 22 | ) 23 | 24 | const version string = "0.42" 25 | 26 | /* Use these for TNSR versions >= 19.02 */ 27 | const ACL_WriteRule = "/restconf/data/netgate-acl:acl-config/acl-table/acl-list=snortblock/acl-rules" 28 | const ACL_ReadRules = "/restconf/data/netgate-acl:acl-config/acl-table/acl-list=snortblock/acl-rules" 29 | const ACL_Delete = "/restconf/data/netgate-acl:acl-config/acl-table/acl-list=snortblock/acl-rules/acl-rule=" 30 | 31 | /* Use these for TNSR versions earlier than 19.02 32 | const ACL_WriteRule = "/restconf/data/acl-config/acl-table/acl-list=snortblock/acl-rules" 33 | const ACL_ReadRules = "/restconf/data/acl-config/acl-table/acl-list=snortblock/acl-rules/acl-rule" 34 | const ACL_Delete = "/restconf/data/acl-config/acl-table/acl-list=snortblock/acl-rules/acl-rule=" 35 | */ 36 | const MAXCACHEAGE uint64 = 5 // Maximum permitted age of the cached rules after which it must be refreshed 37 | const reapPeriod string = "@every 5m" 38 | const maxSeqNum uint64 = 2147483645 39 | const dfltLogpath = "/var/log/tnsrids/tnsrids.log" 40 | 41 | // The ACL structure is nested in a way that allows access each layer 42 | // ACL table structure. This is the next level down after the "acl-config" node 43 | // It contains a list of ACLs 44 | type ACLTable struct { 45 | AclList []ACL `json:"acl-list"` 46 | } 47 | 48 | // Each ACL contains a name and a ACLRuleList 49 | type ACL struct { 50 | AclName string `json:"acl-name"` 51 | AclRules ACLRuleList `json:"acl-rules"` 52 | } 53 | 54 | // An ACLRuleList contains a list of rules 55 | type ACLRuleList struct { 56 | AclRule []AAclRule `json:"netgate-acl:acl-rule"` 57 | } 58 | 59 | // Each rule contains a sequence #, description, action and URI (those are all we care about anyway) 60 | type AAclRule struct { 61 | Sequence uint64 `json:"sequence"` 62 | AclRuleDescription string `json:"acl-rule-description"` 63 | Action string `json:"action"` 64 | Version string `json:"ip-version"` 65 | DstIPPrefix string `json:"dst-ip-prefix,omitempty"` 66 | SrcIPPrefix string `json:"src-ip-prefix,omitempty"` 67 | 68 | /* In theory the rule could contain these elements, but we are only interested in simple block rules so we don't care 69 | SrcLastPort int64 `json:"src-last-port"` 70 | ICMPFirstCode int64 `json:"icmp-first-code"` 71 | ICMPLastCode int64 `json:"icmp-last-code"` 72 | ICMPFirstType int64 `json:"icmp-first-type"` 73 | TCPFlagsMask int64 `json:"tcp-flags-mask"` 74 | SrcFirstPort int64 `json:"src-first-port"` 75 | Protocol int64 `json:"protocol"` 76 | DstLastPort int64 `json:"dst-last-port"` 77 | SrcIPPrefix string `json:"src-ip-prefix,omitempty"` 78 | TCPFlagsValue int64 `json:"tcp-flags-value"` 79 | ICMPLastType int64 `json:"icmp-last-type"` 80 | DstFirstPort int64 `json:"dst-first-port"` 81 | */ 82 | } 83 | 84 | // Some simple globals 85 | var verbose = false // Enable verbose logging to stdout 86 | var lastupdate uint64 // When was the cache last updated from TNSR 87 | var tnsrMutex sync.Mutex // Mutex so addRule() and reapACLs() don't collide 88 | var tnsrhost string // Address or hostname of TNSR instance 89 | 90 | // A local copy of the ACL rules. Certain operations are performed on the cache, which is updated automatically 91 | // when older that MAXCACHEAGE. 92 | // Checking whether a rule exists and calculating the next free sequence number could otherwise require thousands 93 | // of RESTCONF calls 94 | var aclcache ACLRuleList 95 | 96 | // Maximum permitted age of the TNSR ACL rules in seconds after which they are removed via reap() 97 | var maxruleage uint64 98 | 99 | // Making these global allows the TLS stuff to be set up once, then used on every ESTCONF call 100 | var useTLS bool 101 | 102 | // TLS configuration 103 | var tlsConfig *tls.Config 104 | 105 | // COnfiguration map. Used only while parsing the command line and config file 106 | var config map[string]string 107 | -------------------------------------------------------------------------------- /graphics/Netgate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netgate/TNSR_IDS/7ba503529899572999fbfe9bd01546ccf014e8bb/graphics/Netgate.png -------------------------------------------------------------------------------- /graphics/tnsrids.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netgate/TNSR_IDS/7ba503529899572999fbfe9bd01546ccf014e8bb/graphics/tnsrids.png -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | // parser.go extracts the host we need to block from a Snort alert message and puts it onto channel 2 | // The Go routine processHosts() reads the hosts from the channel and updates the ACL 3 | 4 | /* Copyright (c) 2018-2019 Rubicon Communications, LLC (Netgate) 5 | * All rights reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package main 21 | 22 | import ( 23 | // "fmt" 24 | "regexp" 25 | // "time" 26 | ) 27 | 28 | // Go routine to continuously reads host names channel and pass them to the ACL updater 29 | func processHosts(hf <-chan string) { 30 | for { 31 | addRule(<-hf, true) 32 | } 33 | } 34 | 35 | // Extract the first IPv4 address from a string 36 | func findIP(input string) string { 37 | numBlock := "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])" 38 | regexPattern := numBlock + "\\." + numBlock + "\\." + numBlock + "\\." + numBlock 39 | 40 | regEx := regexp.MustCompile(regexPattern) 41 | return regEx.FindString(input) 42 | } 43 | 44 | // parseAlerts processes incoming syslog records and pushes the host to block into a channel read by peocessHosts 45 | func parseAlerts(alert string, hf chan<- string) { 46 | addr := findIP(alert) 47 | hf <- addr + "/32" 48 | } 49 | -------------------------------------------------------------------------------- /restconf.go: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018-2019 Rubicon Communications, LLC (Netgate) 2 | * All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "bytes" 21 | "crypto/tls" 22 | "crypto/x509" 23 | "encoding/json" 24 | "errors" 25 | "fmt" 26 | "io/ioutil" 27 | "log" 28 | "net/http" 29 | "strconv" 30 | "strings" 31 | "time" 32 | ) 33 | 34 | // Update the cached rules from the "snortblock" ACL in TNSR 35 | // If the cache is < MAXCACHEAGE minutes old, don't bother UNLESS force is true 36 | func getSnortBlockACL(force bool) error { 37 | now := time.Now() 38 | 39 | if !force && (lastupdate+(MAXCACHEAGE*60)) > uint64(now.Unix()) { 40 | return nil 41 | } 42 | 43 | if verbose { 44 | fmt.Println("Updating ACL cache") 45 | } 46 | 47 | response, err := rest("GET", tnsrhost+ACL_ReadRules, "") 48 | if err != nil { 49 | return err 50 | } 51 | 52 | // Write the received JSON rule list to the local cache 53 | err = json.Unmarshal(response, &aclcache) 54 | 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | 59 | // And remember when 60 | lastupdate = uint64(now.Unix()) 61 | return nil 62 | } 63 | 64 | // Print a pretty list of the rules in an ACL 65 | func (c ACLRuleList) listACLs() { 66 | log.Printf("INFO: Listing ACL block rules for snortblock ACL") 67 | 68 | idx := 0 69 | var dstsrc string 70 | var addr string 71 | 72 | for _, v := range c.AclRule { 73 | var r AAclRule = v 74 | 75 | if len(r.DstIPPrefix) == 0 { 76 | dstsrc = "Src IP" 77 | addr = r.SrcIPPrefix 78 | } else { 79 | dstsrc = "Dst IP" 80 | addr = r.DstIPPrefix 81 | } 82 | 83 | fmt.Printf("%3d Sequence #: %10d, %s %18s, Action: %7s, Description: %s\n", 84 | idx, r.Sequence, dstsrc, addr, r.Action, r.AclRuleDescription) 85 | 86 | idx++ 87 | } 88 | } 89 | 90 | // Retrieve the rules from the snortblock ACL and print them to the console 91 | func showACLs() error { 92 | err := getSnortBlockACL(false) 93 | if err != nil { 94 | return err 95 | } 96 | 97 | fmt.Println("\nCurrently installed rules in ACL list \"snortblock\"\n--------------------------------------------------") 98 | aclcache.listACLs() 99 | return nil 100 | } 101 | 102 | // Add a rule to the snortblock ACL in TNSR and in local cache. src indicates source rule or destination 103 | func addRule(host string, src bool) { 104 | 105 | var rule AAclRule 106 | 107 | tnsrMutex.Lock() 108 | defer tnsrMutex.Unlock() 109 | 110 | err := getSnortBlockACL(false) 111 | if err != nil { 112 | log.Fatal("Unable to snortblock read rues from TNSR\n") 113 | } 114 | 115 | // Don't duplicate rules 116 | if ruleExists(host) { 117 | if verbose { 118 | fmt.Printf("Duplicate rule: %s\n", host) 119 | } 120 | 121 | return 122 | } 123 | 124 | now := time.Now() 125 | 126 | // Compose a new rule 127 | rule.AclRuleDescription = fmt.Sprintf("%d, Added by tnsrids", now.Unix()) 128 | rule.Sequence = getNextSeqNum() 129 | rule.Action = "deny" 130 | rule.Version = "ipv4" 131 | 132 | //Source rule or destination? 133 | if src { 134 | rule.SrcIPPrefix = host 135 | rule.DstIPPrefix = "" 136 | } else { 137 | rule.DstIPPrefix = host 138 | rule.SrcIPPrefix = "" 139 | } 140 | 141 | b, err := json.Marshal(rule) 142 | if err != nil { 143 | log.Printf("Error: %v", err) 144 | return 145 | } 146 | 147 | // Compose the JSON formatting 148 | cmd := "{\"netgate-acl:acl-rule\":" + string(b) + "}" 149 | 150 | if verbose { 151 | fmt.Printf("Adding rule for host: %s\n", host) 152 | } 153 | 154 | log.Printf("INFO: Adding block rule for \"%s\"", host) 155 | 156 | // Add the new rule to TNSR via RESTCONF 157 | _, err = rest("PUT", fmt.Sprintf("%s%s%s%d", tnsrhost, ACL_WriteRule, "/acl-rule=", getNextSeqNum()), cmd) 158 | if err != nil { 159 | log.Printf("Error: %v", err) 160 | } else { 161 | // Add the new rule to the cached rule list 162 | aclcache.AclRule = append(aclcache.AclRule, rule) 163 | } 164 | } 165 | 166 | // Make an HTTP REST call 167 | // Requires the operator (PUT, POST, GET, DELETE etc), the complete URL (including the protocol) and an optional payload 168 | func rest(oper string, url string, payload string) ([]byte, error) { 169 | var err error 170 | var req *http.Request 171 | var client *http.Client 172 | var resp *http.Response 173 | 174 | if len(payload) == 0 { 175 | req, err = http.NewRequest(oper, url, nil) 176 | } else { 177 | var jsonStr = []byte(payload) 178 | 179 | req, err = http.NewRequest(oper, url, bytes.NewBuffer(jsonStr)) 180 | } 181 | 182 | // This content-type is required for TNSR > 19-12 and specifically to use the HTTP PATCH mthod 183 | req.Header.Set("Content-Type", "application/yang-data+json") 184 | 185 | if useTLS { 186 | transport := &http.Transport{TLSClientConfig: tlsConfig} 187 | client = &http.Client{Transport: transport} 188 | } else { 189 | client = &http.Client{} 190 | } 191 | 192 | resp, err = client.Do(req) 193 | if err != nil { 194 | fmt.Printf("%v", err) 195 | return nil, err 196 | } 197 | 198 | defer resp.Body.Close() 199 | contents, _ := ioutil.ReadAll(resp.Body) 200 | 201 | // fmt.Println("response Status:", resp.Status) 202 | 203 | // 204 code is valid if no response is expected. Currently 404 is returned if the configuration item is currently empty 204 | // which is not really an error, but sionce the body contains an error message, 204 is not really appropriate. 205 | if resp.StatusCode != 200 && resp.StatusCode != 204 && !(resp.StatusCode == 404 && extractErrorMsg(contents) != "Instance does not exist") { 206 | return nil, errors.New("RESTCONF operation failed (" + string(resp.Status) + ")") 207 | } 208 | 209 | return contents, nil 210 | } 211 | 212 | // Returns true if a rule exists for the specified host in the local cache 213 | // Called from functions that have updated the cache already 214 | func ruleExists(host string) bool { 215 | for _, v := range aclcache.AclRule { 216 | if host == v.DstIPPrefix || host == v.SrcIPPrefix { 217 | return true 218 | } 219 | } 220 | 221 | return false 222 | } 223 | 224 | // Find the lowest unused sequence number in the cached rule list 225 | // This may be a gap in the sequece from a previously deleted rule, ot it may be the next highest number 226 | func getNextSeqNum() uint64 { 227 | var idx uint64 228 | var numRules int64 = int64(len(aclcache.AclRule)) 229 | var ruleCnt int64 = 0 230 | var max uint64 = 0 231 | 232 | // Find the highest sequence number in use 233 | for _, v := range aclcache.AclRule { 234 | if v.Sequence > max && v.Action == "deny" { 235 | max = v.Sequence 236 | } 237 | } 238 | 239 | // For every possible number <= max 240 | for idx = 1; idx < max; idx++ { 241 | ruleCnt = 0 242 | // See if there is a rule that uses it as a sequence number 243 | for _, v := range aclcache.AclRule { 244 | ruleCnt++ 245 | if idx == v.Sequence { 246 | break 247 | } 248 | 249 | } 250 | 251 | // If there was a gap, resuse it 252 | if ruleCnt == numRules { 253 | return idx 254 | } 255 | } 256 | 257 | // If there was no gap, return the next number 258 | return max + 1 259 | } 260 | 261 | // Delete the rule with the specified sequece number 262 | func deleteRule(seq uint64) error { 263 | 264 | var url string = fmt.Sprintf("%s%s%d", tnsrhost, ACL_Delete, seq) 265 | 266 | _, err := rest("DELETE", url, "") 267 | 268 | if err != nil { 269 | return (err) 270 | } 271 | 272 | return nil 273 | } 274 | 275 | // Clean out any rules that have a timestamp older than MAXAGEMINS minutes, no timestamp at all 276 | // Ignore the defalut permit rule (which has a seq # > maxSeqNum) 277 | func reapACLs() error { 278 | deletedSome := false 279 | 280 | if verbose { 281 | fmt.Println("Cleaning out the old rules") 282 | } 283 | 284 | // Lock the mutex so that it is not possible to write new rule while reaping old ones 285 | tnsrMutex.Lock() 286 | defer tnsrMutex.Unlock() 287 | 288 | var i uint64 289 | var err error 290 | 291 | err = getSnortBlockACL(false) 292 | if err != nil { 293 | return errors.New("Unable to read snortblock rules from TNSR\n") 294 | } 295 | 296 | now := time.Now() 297 | epoch := uint64(now.Unix()) 298 | var zapit bool 299 | 300 | for _, v := range aclcache.AclRule { 301 | // Leave the default permit rule alone 302 | if v.Sequence > maxSeqNum { 303 | continue 304 | } 305 | 306 | zapit = false 307 | if !strings.Contains(v.AclRuleDescription, ",") { 308 | log.Printf("INFO: Unable to read timestamp from description. Deleting rule") 309 | zapit = true 310 | } else { 311 | s := strings.Split(v.AclRuleDescription, ",") 312 | 313 | i, err = strconv.ParseUint(s[0], 10, 64) 314 | 315 | if err != nil || i == 0 { 316 | log.Printf("INFO: Unable to read timestamp from description. Deleting rule") 317 | zapit = true 318 | } 319 | } 320 | 321 | if zapit || (i+maxruleage) < epoch { 322 | if verbose { 323 | fmt.Printf("Deleting rule with sequence %v\n", v.Sequence) 324 | } 325 | 326 | log.Printf("INFO: Reaping rule with sequence %v\n", v.Sequence) 327 | deleteRule(v.Sequence) 328 | deletedSome = true 329 | } 330 | } 331 | 332 | // Re-read the ACL so that the cache is up to date 333 | if deletedSome { 334 | err = getSnortBlockACL(true) 335 | if err != nil { 336 | return errors.New("Unable to re-read snortblock rues from TNSR\n") 337 | } 338 | } 339 | 340 | return nil 341 | } 342 | 343 | // Set up the TLS configuration from the provided ca, certificate and key 344 | func TLSSetup(ca string, certificate string, key string) error { 345 | 346 | if len(ca) == 0 || len(certificate) == 0 || len(key) == 0 { 347 | return nil 348 | } 349 | 350 | if verbose { 351 | fmt.Println("Attempting TLS initialization") 352 | } 353 | 354 | // Load client cert 355 | cert, err := tls.LoadX509KeyPair(certificate, key) 356 | if err != nil { 357 | return err 358 | } 359 | 360 | // Load CA cert 361 | caCert, err := ioutil.ReadFile(ca) 362 | if err != nil { 363 | return err 364 | } 365 | 366 | caCertPool := x509.NewCertPool() 367 | caCertPool.AppendCertsFromPEM(caCert) 368 | 369 | // Setup HTTPS client 370 | tlsConfig = &tls.Config{ 371 | Certificates: []tls.Certificate{cert}, 372 | RootCAs: caCertPool, 373 | } 374 | 375 | tlsConfig.BuildNameToCertificate() 376 | useTLS = true 377 | 378 | return nil 379 | } 380 | 381 | type IETF_RESTCONF_ERRORS struct { 382 | Errors IETF_RESTCONF_ERROR `json:"ietf-restconf:errors"` 383 | } 384 | 385 | type IETF_RESTCONF_ERROR struct { 386 | Error IETF_RPC_ERROR `json:"error"` 387 | } 388 | 389 | type IETF_RPC_ERROR struct { 390 | RPCError TNSR_ERROR `json:"rpc-error"` 391 | } 392 | 393 | type TNSR_ERROR struct { 394 | Type string `json:"error-type"` 395 | Tag string `json:"error-tag"` 396 | Severity string `json:"error-severity"` 397 | Message string `json:"error-message"` 398 | } 399 | 400 | // extractErrorMsg() - take the JSON error response string received from a bad RESTCONF call and extract the error-message 401 | func extractErrorMsg(response []byte) string { 402 | var ietfErr IETF_RESTCONF_ERRORS 403 | 404 | json.Unmarshal(response, &ietfErr) 405 | return ietfErr.Errors.Error.RPCError.Message 406 | } 407 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018-2019 Rubicon Communications, LLC (Netgate) 2 | * All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "fmt" 21 | "log" 22 | "net" 23 | ) 24 | 25 | // startServer is a very simplistic UDP server that listens on the specified port and passes received messages 26 | // to a parser, without regard for where they came from. In production, it would be better to set up a rx channel 27 | // for each source 28 | func startServer(port string) { 29 | 30 | host := ":" + port 31 | proto := "udp" 32 | 33 | if verbose { 34 | fmt.Printf("Starting server %s %s\n", proto, host) 35 | } 36 | 37 | log.Printf("tnsrids version %s started. Listening on UDP %s", version, host) 38 | 39 | // Start a listener 40 | listener, error := net.ListenPacket(proto, host) 41 | if error != nil { 42 | log.Fatal("Unable to start UDP listener") 43 | return 44 | } 45 | 46 | defer listener.Close() 47 | 48 | // channel acts like a FIFO providing a 4096 string buffer between reading hosts via UDP and updating TNSR via RESTCONF 49 | hf := make(chan string, 4096) 50 | 51 | // Start the go routine that reads from the channel and processes the syslog messages 52 | go processHosts(hf) 53 | 54 | // Read incoming syslog messages and push them into the FIFO 55 | for { 56 | message := make([]byte, 4096) 57 | length, _, err := listener.ReadFrom(message) 58 | if err != nil { 59 | log.Fatal("Unable to read from UDP listener") 60 | return 61 | } 62 | 63 | if length > 0 { 64 | parseAlerts(string(message[0:length]), hf) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /snort.conf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------- 2 | # VRT Rule Packages Snort.conf 3 | # 4 | # For more information visit us at: 5 | # http://www.snort.org Snort Website 6 | # http://vrt-blog.snort.org/ Sourcefire VRT Blog 7 | # 8 | # Mailing list Contact: snort-sigs@lists.sourceforge.net 9 | # False Positive reports: fp@sourcefire.com 10 | # Snort bugs: bugs@snort.org 11 | # 12 | # Compatible with Snort Versions: 13 | # VERSIONS : 2.9.12 14 | # 15 | # Snort build options: 16 | # OPTIONS : --enable-gre --enable-mpls --enable-targetbased --enable-ppm --enable-perfprofiling --enable-zlib --enable-active-response --enable-normalizer --enable-reload --enable-react --enable-flexresp3 17 | # 18 | # Additional information: 19 | # This configuration file enables active response, to run snort in 20 | # test mode -T you are required to supply an interface -i 21 | # or test mode will fail to fully validate the configuration and 22 | # exit with a FATAL error 23 | #-------------------------------------------------- 24 | 25 | ################################################### 26 | # This file contains a sample snort configuration. 27 | # You should take the following steps to create your own custom configuration: 28 | # 29 | # 1) Set the network variables. 30 | # 2) Configure the decoder 31 | # 3) Configure the base detection engine 32 | # 4) Configure dynamic loaded libraries 33 | # 5) Configure preprocessors 34 | # 6) Configure output plugins 35 | # 7) Customize your rule set 36 | # 8) Customize preprocessor and decoder rule set 37 | # 9) Customize shared object rule set 38 | ################################################### 39 | 40 | ################################################### 41 | # Step #1: Set the network variables. For more information, see README.variables 42 | ################################################### 43 | 44 | # Setup the network addresses you are protecting 45 | ipvar HOME_NET [192.0.2.0/24,203.0.113.2/32] 46 | 47 | # Set up the external network addresses. Leave as "any" in most situations 48 | ipvar EXTERNAL_NET any 49 | 50 | # List of DNS servers on your network 51 | ipvar DNS_SERVERS $HOME_NET 52 | 53 | # List of SMTP servers on your network 54 | ipvar SMTP_SERVERS $HOME_NET 55 | 56 | # List of web servers on your network 57 | ipvar HTTP_SERVERS $HOME_NET 58 | 59 | # List of sql servers on your network 60 | ipvar SQL_SERVERS $HOME_NET 61 | 62 | # List of telnet servers on your network 63 | ipvar TELNET_SERVERS $HOME_NET 64 | 65 | # List of ssh servers on your network 66 | ipvar SSH_SERVERS $HOME_NET 67 | 68 | # List of ftp servers on your network 69 | ipvar FTP_SERVERS $HOME_NET 70 | 71 | # List of sip servers on your network 72 | ipvar SIP_SERVERS $HOME_NET 73 | 74 | # List of ports you run web servers on 75 | portvar HTTP_PORTS [80,81,311,383,591,593,901,1220,1414,1741,1830,2301,2381,2809,3037,3128,3702,4343,4848,5250,6988,7000,7001,7144,7145,7510,7777,7779,8000,8008,8014,8028,8080,8085,8088,8090,8118,8123,8180,8181,8243,8280,8300,8800,8888,8899,9000,9060,9080,9090,9091,9443,9999,11371,34443,34444,41080,50002,55555] 76 | 77 | # List of ports you want to look for SHELLCODE on. 78 | portvar SHELLCODE_PORTS !80 79 | 80 | # List of ports you might see oracle attacks on 81 | portvar ORACLE_PORTS 1024: 82 | 83 | # List of ports you want to look for SSH connections on: 84 | portvar SSH_PORTS 22 85 | 86 | # List of ports you run ftp servers on 87 | portvar FTP_PORTS [21,2100,3535] 88 | 89 | # List of ports you run SIP servers on 90 | portvar SIP_PORTS [5060,5061,5600] 91 | 92 | # List of file data ports for file inspection 93 | portvar FILE_DATA_PORTS [$HTTP_PORTS,110,143] 94 | 95 | # List of GTP ports for GTP preprocessor 96 | portvar GTP_PORTS [2123,2152,3386] 97 | 98 | # other variables, these should not be modified 99 | ipvar AIM_SERVERS [64.12.24.0/23,64.12.28.0/23,64.12.161.0/24,64.12.163.0/24,64.12.200.0/24,205.188.3.0/24,205.188.5.0/24,205.188.7.0/24,205.188.9.0/24,205.188.153.0/24,205.188.179.0/24,205.188.248.0/24] 100 | 101 | # Path to your rules files (this can be a relative path) 102 | # Note for Windows users: You are advised to make this an absolute path, 103 | # such as: c:\snort\rules 104 | var RULE_PATH /etc/snort/rules 105 | var SO_RULE_PATH /etc/snort/so_rules 106 | var PREPROC_RULE_PATH /etc/snort/preproc_rules 107 | 108 | # If you are using reputation preprocessor set these 109 | # Currently there is a bug with relative paths, they are relative to where snort is 110 | # not relative to snort.conf like the above variables 111 | # This is completely inconsistent with how other vars work, BUG 89986 112 | # Set the absolute path appropriately 113 | var WHITE_LIST_PATH /etc/snort/rules 114 | var BLACK_LIST_PATH /etc/snort/rules 115 | 116 | ################################################### 117 | # Step #2: Configure the decoder. For more information, see README.decode 118 | ################################################### 119 | 120 | # Stop generic decode events: 121 | config disable_decode_alerts 122 | 123 | # Stop Alerts on experimental TCP options 124 | config disable_tcpopt_experimental_alerts 125 | 126 | # Stop Alerts on obsolete TCP options 127 | config disable_tcpopt_obsolete_alerts 128 | 129 | # Stop Alerts on T/TCP alerts 130 | config disable_tcpopt_ttcp_alerts 131 | 132 | # Stop Alerts on all other TCPOption type events: 133 | config disable_tcpopt_alerts 134 | 135 | # Stop Alerts on invalid ip options 136 | config disable_ipopt_alerts 137 | 138 | # Alert if value in length field (IP, TCP, UDP) is greater th elength of the packet 139 | # config enable_decode_oversized_alerts 140 | 141 | # Same as above, but drop packet if in Inline mode (requires enable_decode_oversized_alerts) 142 | # config enable_decode_oversized_drops 143 | 144 | # Configure IP / TCP checksum mode 145 | config checksum_mode: all 146 | 147 | # Configure maximum number of flowbit references. For more information, see README.flowbits 148 | # config flowbits_size: 64 149 | 150 | # Configure ports to ignore 151 | # config ignore_ports: tcp 21 6667:6671 1356 152 | # config ignore_ports: udp 1:17 53 153 | 154 | # Configure active response for non inline operation. For more information, see REAMDE.active 155 | # config response: eth0 attempts 2 156 | 157 | # Configure DAQ related options for inline operation. For more information, see README.daq 158 | # 159 | # config daq: 160 | # config daq_dir: 161 | # config daq_mode: 162 | # config daq_var: 163 | # 164 | # ::= pcap | afpacket | dump | nfq | ipq | ipfw 165 | # ::= read-file | passive | inline 166 | # ::= arbitrary = ::= path as to where to look for DAQ module so's 168 | 169 | # Configure specific UID and GID to run snort as after dropping privs. For more information see snort -h command line options 170 | # 171 | # config set_gid: 172 | # config set_uid: 173 | 174 | # Configure default snaplen. Snort defaults to MTU of in use interface. For more information see README 175 | # 176 | # config snaplen: 177 | # 178 | 179 | # Configure default bpf_file to use for filtering what traffic reaches snort. For more information see snort -h command line options (-F) 180 | # 181 | # config bpf_file: 182 | # 183 | 184 | # Configure default log directory for snort to log to. For more information see snort -h command line options (-l) 185 | # 186 | # config logdir: 187 | 188 | 189 | ################################################### 190 | # Step #3: Configure the base detection engine. For more information, see README.decode 191 | ################################################### 192 | 193 | # Configure PCRE match limitations 194 | config pcre_match_limit: 3500 195 | config pcre_match_limit_recursion: 1500 196 | 197 | # Configure the detection engine See the Snort Manual, Configuring Snort - Includes - Config 198 | config detection: search-method ac-split search-optimize max-pattern-len 20 199 | 200 | # Configure the event queue. For more information, see README.event_queue 201 | config event_queue: max_queue 8 log 5 order_events content_length 202 | 203 | ################################################### 204 | ## Configure GTP if it is to be used. 205 | ## For more information, see README.GTP 206 | #################################################### 207 | 208 | # config enable_gtp 209 | 210 | ################################################### 211 | # Per packet and rule latency enforcement 212 | # For more information see README.ppm 213 | ################################################### 214 | 215 | # Per Packet latency configuration 216 | #config ppm: max-pkt-time 250, \ 217 | # fastpath-expensive-packets, \ 218 | # pkt-log 219 | 220 | # Per Rule latency configuration 221 | #config ppm: max-rule-time 200, \ 222 | # threshold 3, \ 223 | # suspend-expensive-rules, \ 224 | # suspend-timeout 20, \ 225 | # rule-log alert 226 | 227 | ################################################### 228 | # Configure Perf Profiling for debugging 229 | # For more information see README.PerfProfiling 230 | ################################################### 231 | 232 | #config profile_rules: print all, sort avg_ticks 233 | #config profile_preprocs: print all, sort avg_ticks 234 | 235 | ################################################### 236 | # Configure protocol aware flushing 237 | # For more information see README.stream5 238 | ################################################### 239 | config paf_max: 16000 240 | 241 | ################################################### 242 | # Step #4: Configure dynamic loaded libraries. 243 | # For more information, see Snort Manual, Configuring Snort - Dynamic Modules 244 | ################################################### 245 | 246 | # path to dynamic preprocessor libraries 247 | dynamicpreprocessor directory /usr/local/lib/snort_dynamicpreprocessor/ 248 | 249 | # path to base preprocessor engine 250 | dynamicengine /usr/local/lib/snort_dynamicengine/libsf_engine.so 251 | 252 | # path to dynamic rules libraries 253 | dynamicdetection directory /usr/local/lib/snort_dynamicrules 254 | 255 | ################################################### 256 | # Step #5: Configure preprocessors 257 | # For more information, see the Snort Manual, Configuring Snort - Preprocessors 258 | ################################################### 259 | 260 | # GTP Control Channle Preprocessor. For more information, see README.GTP 261 | # preprocessor gtp: ports { 2123 3386 2152 } 262 | 263 | # Inline packet normalization. For more information, see README.normalize 264 | # Does nothing in IDS mode 265 | preprocessor normalize_ip4 266 | preprocessor normalize_tcp: ips ecn stream 267 | preprocessor normalize_icmp4 268 | preprocessor normalize_ip6 269 | preprocessor normalize_icmp6 270 | 271 | # Target-based IP defragmentation. For more inforation, see README.frag3 272 | preprocessor frag3_global: max_frags 65536 273 | preprocessor frag3_engine: policy windows detect_anomalies overlap_limit 10 min_fragment_length 100 timeout 180 274 | 275 | # Target-Based stateful inspection/stream reassembly. For more inforation, see README.stream5 276 | preprocessor stream5_global: track_tcp yes, \ 277 | track_udp yes, \ 278 | track_icmp no, \ 279 | max_tcp 262144, \ 280 | max_udp 131072, \ 281 | max_active_responses 2, \ 282 | min_response_seconds 5 283 | preprocessor stream5_tcp: log_asymmetric_traffic no, policy windows, \ 284 | detect_anomalies, require_3whs 180, \ 285 | overlap_limit 10, small_segments 3 bytes 150, timeout 180, \ 286 | ports client 21 22 23 25 42 53 79 109 110 111 113 119 135 136 137 139 143 \ 287 | 161 445 513 514 587 593 691 1433 1521 1741 2100 3306 6070 6665 6666 6667 6668 6669 \ 288 | 7000 8181 32770 32771 32772 32773 32774 32775 32776 32777 32778 32779, \ 289 | ports both 80 81 311 383 443 465 563 591 593 636 901 989 992 993 994 995 1220 1414 1830 2301 2381 2809 3037 3128 3702 4343 4848 5250 6988 7907 7000 7001 7144 7145 7510 7802 7777 7779 \ 290 | 7801 7900 7901 7902 7903 7904 7905 7906 7908 7909 7910 7911 7912 7913 7914 7915 7916 \ 291 | 7917 7918 7919 7920 8000 8008 8014 8028 8080 8085 8088 8090 8118 8123 8180 8243 8280 8300 8800 8888 8899 9000 9060 9080 9090 9091 9443 9999 11371 34443 34444 41080 50002 55555 292 | preprocessor stream5_udp: timeout 180 293 | 294 | # performance statistics. For more information, see the Snort Manual, Configuring Snort - Preprocessors - Performance Monitor 295 | # preprocessor perfmonitor: time 300 file /var/snort/snort.stats pktcnt 10000 296 | 297 | # HTTP normalization and anomaly detection. For more information, see README.http_inspect 298 | preprocessor http_inspect: global iis_unicode_map unicode.map 1252 compress_depth 65535 decompress_depth 65535 299 | preprocessor http_inspect_server: server default \ 300 | http_methods { GET POST PUT SEARCH MKCOL COPY MOVE LOCK UNLOCK NOTIFY POLL BCOPY BDELETE BMOVE LINK UNLINK OPTIONS HEAD DELETE TRACE TRACK CONNECT SOURCE SUBSCRIBE UNSUBSCRIBE PROPFIND PROPPATCH BPROPFIND BPROPPATCH RPC_CONNECT PROXY_SUCCESS BITS_POST CCM_POST SMS_POST RPC_IN_DATA RPC_OUT_DATA RPC_ECHO_DATA } \ 301 | chunk_length 500000 \ 302 | server_flow_depth 0 \ 303 | client_flow_depth 0 \ 304 | post_depth 65495 \ 305 | oversize_dir_length 500 \ 306 | max_header_length 750 \ 307 | max_headers 100 \ 308 | max_spaces 200 \ 309 | small_chunk_length { 10 5 } \ 310 | ports { 80 81 311 383 591 593 901 1220 1414 1741 1830 2301 2381 2809 3037 3128 3702 4343 4848 5250 6988 7000 7001 7144 7145 7510 7777 7779 8000 8008 8014 8028 8080 8085 8088 8090 8118 8123 8180 8181 8243 8280 8300 8800 8888 8899 9000 9060 9080 9090 9091 9443 9999 11371 34443 34444 41080 50002 55555 } \ 311 | non_rfc_char { 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 } \ 312 | enable_cookie \ 313 | extended_response_inspection \ 314 | inspect_gzip \ 315 | normalize_utf \ 316 | unlimited_decompress \ 317 | normalize_javascript \ 318 | apache_whitespace no \ 319 | ascii no \ 320 | bare_byte no \ 321 | directory no \ 322 | double_decode no \ 323 | iis_backslash no \ 324 | iis_delimiter no \ 325 | iis_unicode no \ 326 | multi_slash no \ 327 | utf_8 no \ 328 | u_encode yes \ 329 | webroot no 330 | 331 | # ONC-RPC normalization and anomaly detection. For more information, see the Snort Manual, Configuring Snort - Preprocessors - RPC Decode 332 | preprocessor rpc_decode: 111 32770 32771 32772 32773 32774 32775 32776 32777 32778 32779 no_alert_multiple_requests no_alert_large_fragments no_alert_incomplete 333 | 334 | # Back Orifice detection. 335 | preprocessor bo 336 | 337 | # FTP / Telnet normalization and anomaly detection. For more information, see README.ftptelnet 338 | preprocessor ftp_telnet: global inspection_type stateful encrypted_traffic no check_encrypted 339 | preprocessor ftp_telnet_protocol: telnet \ 340 | ayt_attack_thresh 20 \ 341 | normalize ports { 23 } \ 342 | detect_anomalies 343 | preprocessor ftp_telnet_protocol: ftp server default \ 344 | def_max_param_len 100 \ 345 | ports { 21 2100 3535 } \ 346 | telnet_cmds yes \ 347 | ignore_telnet_erase_cmds yes \ 348 | ftp_cmds { ABOR ACCT ADAT ALLO APPE AUTH CCC CDUP } \ 349 | ftp_cmds { CEL CLNT CMD CONF CWD DELE ENC EPRT } \ 350 | ftp_cmds { EPSV ESTA ESTP FEAT HELP LANG LIST LPRT } \ 351 | ftp_cmds { LPSV MACB MAIL MDTM MIC MKD MLSD MLST } \ 352 | ftp_cmds { MODE NLST NOOP OPTS PASS PASV PBSZ PORT } \ 353 | ftp_cmds { PROT PWD QUIT REIN REST RETR RMD RNFR } \ 354 | ftp_cmds { RNTO SDUP SITE SIZE SMNT STAT STOR STOU } \ 355 | ftp_cmds { STRU SYST TEST TYPE USER XCUP XCRC XCWD } \ 356 | ftp_cmds { XMAS XMD5 XMKD XPWD XRCP XRMD XRSQ XSEM } \ 357 | ftp_cmds { XSEN XSHA1 XSHA256 } \ 358 | alt_max_param_len 0 { ABOR CCC CDUP ESTA FEAT LPSV NOOP PASV PWD QUIT REIN STOU SYST XCUP XPWD } \ 359 | alt_max_param_len 200 { ALLO APPE CMD HELP NLST RETR RNFR STOR STOU XMKD } \ 360 | alt_max_param_len 256 { CWD RNTO } \ 361 | alt_max_param_len 400 { PORT } \ 362 | alt_max_param_len 512 { SIZE } \ 363 | chk_str_fmt { ACCT ADAT ALLO APPE AUTH CEL CLNT CMD } \ 364 | chk_str_fmt { CONF CWD DELE ENC EPRT EPSV ESTP HELP } \ 365 | chk_str_fmt { LANG LIST LPRT MACB MAIL MDTM MIC MKD } \ 366 | chk_str_fmt { MLSD MLST MODE NLST OPTS PASS PBSZ PORT } \ 367 | chk_str_fmt { PROT REST RETR RMD RNFR RNTO SDUP SITE } \ 368 | chk_str_fmt { SIZE SMNT STAT STOR STRU TEST TYPE USER } \ 369 | chk_str_fmt { XCRC XCWD XMAS XMD5 XMKD XRCP XRMD XRSQ } \ 370 | chk_str_fmt { XSEM XSEN XSHA1 XSHA256 } \ 371 | cmd_validity ALLO < int [ char R int ] > \ 372 | cmd_validity EPSV < [ { char 12 | char A char L char L } ] > \ 373 | cmd_validity MACB < string > \ 374 | cmd_validity MDTM < [ date nnnnnnnnnnnnnn[.n[n[n]]] ] string > \ 375 | cmd_validity MODE < char ASBCZ > \ 376 | cmd_validity PORT < host_port > \ 377 | cmd_validity PROT < char CSEP > \ 378 | cmd_validity STRU < char FRPO [ string ] > \ 379 | cmd_validity TYPE < { char AE [ char NTC ] | char I | char L [ number ] } > 380 | preprocessor ftp_telnet_protocol: ftp client default \ 381 | max_resp_len 256 \ 382 | bounce yes \ 383 | ignore_telnet_erase_cmds yes \ 384 | telnet_cmds yes 385 | 386 | 387 | # SMTP normalization and anomaly detection. For more information, see README.SMTP 388 | preprocessor smtp: ports { 25 465 587 691 } \ 389 | inspection_type stateful \ 390 | b64_decode_depth 0 \ 391 | qp_decode_depth 0 \ 392 | bitenc_decode_depth 0 \ 393 | uu_decode_depth 0 \ 394 | log_mailfrom \ 395 | log_rcptto \ 396 | log_filename \ 397 | log_email_hdrs \ 398 | normalize cmds \ 399 | normalize_cmds { ATRN AUTH BDAT CHUNKING DATA DEBUG EHLO EMAL ESAM ESND ESOM ETRN EVFY } \ 400 | normalize_cmds { EXPN HELO HELP IDENT MAIL NOOP ONEX QUEU QUIT RCPT RSET SAML SEND SOML } \ 401 | normalize_cmds { STARTTLS TICK TIME TURN TURNME VERB VRFY X-ADAT X-DRCP X-ERCP X-EXCH50 } \ 402 | normalize_cmds { X-EXPS X-LINK2STATE XADR XAUTH XCIR XEXCH50 XGEN XLICENSE XQUE XSTA XTRN XUSR } \ 403 | max_command_line_len 512 \ 404 | max_header_line_len 1000 \ 405 | max_response_line_len 512 \ 406 | alt_max_command_line_len 260 { MAIL } \ 407 | alt_max_command_line_len 300 { RCPT } \ 408 | alt_max_command_line_len 500 { HELP HELO ETRN EHLO } \ 409 | alt_max_command_line_len 255 { EXPN VRFY ATRN SIZE BDAT DEBUG EMAL ESAM ESND ESOM EVFY IDENT NOOP RSET } \ 410 | alt_max_command_line_len 246 { SEND SAML SOML AUTH TURN ETRN DATA RSET QUIT ONEX QUEU STARTTLS TICK TIME TURNME VERB X-EXPS X-LINK2STATE XADR XAUTH XCIR XEXCH50 XGEN XLICENSE XQUE XSTA XTRN XUSR } \ 411 | valid_cmds { ATRN AUTH BDAT CHUNKING DATA DEBUG EHLO EMAL ESAM ESND ESOM ETRN EVFY } \ 412 | valid_cmds { EXPN HELO HELP IDENT MAIL NOOP ONEX QUEU QUIT RCPT RSET SAML SEND SOML } \ 413 | valid_cmds { STARTTLS TICK TIME TURN TURNME VERB VRFY X-ADAT X-DRCP X-ERCP X-EXCH50 } \ 414 | valid_cmds { X-EXPS X-LINK2STATE XADR XAUTH XCIR XEXCH50 XGEN XLICENSE XQUE XSTA XTRN XUSR } \ 415 | xlink2state { enabled } 416 | 417 | # Portscan detection. For more information, see README.sfportscan 418 | # preprocessor sfportscan: proto { all } memcap { 10000000 } sense_level { low } 419 | 420 | # ARP spoof detection. For more information, see the Snort Manual - Configuring Snort - Preprocessors - ARP Spoof Preprocessor 421 | # preprocessor arpspoof 422 | # preprocessor arpspoof_detect_host: 192.168.40.1 f0:0f:00:f0:0f:00 423 | 424 | # SSH anomaly detection. For more information, see README.ssh 425 | preprocessor ssh: server_ports { 22 } \ 426 | autodetect \ 427 | max_client_bytes 19600 \ 428 | max_encrypted_packets 20 \ 429 | max_server_version_len 100 \ 430 | enable_respoverflow enable_ssh1crc32 \ 431 | enable_srvoverflow enable_protomismatch 432 | 433 | # SMB / DCE-RPC normalization and anomaly detection. For more information, see README.dcerpc2 434 | preprocessor dcerpc2: memcap 102400, events [co ] 435 | preprocessor dcerpc2_server: default, policy WinXP, \ 436 | detect [smb [139,445], tcp 135, udp 135, rpc-over-http-server 593], \ 437 | autodetect [tcp 1025:, udp 1025:, rpc-over-http-server 1025:], \ 438 | smb_max_chain 3, smb_invalid_shares ["C$", "D$", "ADMIN$"] 439 | 440 | # DNS anomaly detection. For more information, see README.dns 441 | preprocessor dns: ports { 53 } enable_rdata_overflow 442 | 443 | # SSL anomaly detection and traffic bypass. For more information, see README.ssl 444 | preprocessor ssl: ports { 443 465 563 636 989 992 993 994 995 7801 7802 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 }, trustservers, noinspect_encrypted 445 | 446 | # SDF sensitive data preprocessor. For more information see README.sensitive_data 447 | preprocessor sensitive_data: alert_threshold 25 448 | 449 | # SIP Session Initiation Protocol preprocessor. For more information see README.sip 450 | preprocessor sip: max_sessions 40000, \ 451 | ports { 5060 5061 5600 }, \ 452 | methods { invite \ 453 | cancel \ 454 | ack \ 455 | bye \ 456 | register \ 457 | options \ 458 | refer \ 459 | subscribe \ 460 | update \ 461 | join \ 462 | info \ 463 | message \ 464 | notify \ 465 | benotify \ 466 | do \ 467 | qauth \ 468 | sprack \ 469 | publish \ 470 | service \ 471 | unsubscribe \ 472 | prack }, \ 473 | max_uri_len 512, \ 474 | max_call_id_len 80, \ 475 | max_requestName_len 20, \ 476 | max_from_len 256, \ 477 | max_to_len 256, \ 478 | max_via_len 1024, \ 479 | max_contact_len 512, \ 480 | max_content_len 2048 481 | 482 | # IMAP preprocessor. For more information see README.imap 483 | preprocessor imap: \ 484 | ports { 143 } \ 485 | b64_decode_depth 0 \ 486 | qp_decode_depth 0 \ 487 | bitenc_decode_depth 0 \ 488 | uu_decode_depth 0 489 | 490 | # POP preprocessor. For more information see README.pop 491 | preprocessor pop: \ 492 | ports { 110 } \ 493 | b64_decode_depth 0 \ 494 | qp_decode_depth 0 \ 495 | bitenc_decode_depth 0 \ 496 | uu_decode_depth 0 497 | 498 | # Modbus preprocessor. For more information see README.modbus 499 | preprocessor modbus: ports { 502 } 500 | 501 | # DNP3 preprocessor. For more information see README.dnp3 502 | preprocessor dnp3: ports { 20000 } \ 503 | memcap 262144 \ 504 | check_crc 505 | 506 | # Reputation preprocessor. For more information see README.reputation 507 | preprocessor reputation: \ 508 | memcap 500, \ 509 | priority whitelist, \ 510 | nested_ip inner, \ 511 | whitelist $WHITE_LIST_PATH/white_list.rules, \ 512 | blacklist $BLACK_LIST_PATH/black_list.rules 513 | 514 | ################################################### 515 | # Step #6: Configure output plugins 516 | # For more information, see Snort Manual, Configuring Snort - Output Modules 517 | ################################################### 518 | 519 | # unified2 520 | # Recommended for most installs 521 | # output unified2: filename merged.log, limit 128, nostamp, mpls_event_types, vlan_event_types 522 | 523 | # Additional configuration for specific types of installs 524 | # output alert_unified2: filename snort.alert, limit 128, nostamp 525 | # output log_unified2: filename snort.log, limit 128, nostamp 526 | 527 | # syslog 528 | # output alert_syslog: LOG_AUTH LOG_ALERT 529 | output alert_syslog: LOG_LOCAL5 LOG_ALERT 530 | 531 | # pcap 532 | # output log_tcpdump: tcpdump.log 533 | 534 | # metadata reference data. do not modify these lines 535 | include classification.config 536 | include reference.config 537 | 538 | 539 | ################################################### 540 | # Step #7: Customize your rule set 541 | # For more information, see Snort Manual, Writing Snort Rules 542 | # 543 | # NOTE: All categories are enabled in this conf file 544 | ################################################### 545 | 546 | # site specific rules 547 | include $RULE_PATH/local.rules 548 | include $RULE_PATH/community.rules 549 | 550 | #include $RULE_PATH/app-detect.rules 551 | #include $RULE_PATH/attack-responses.rules 552 | #include $RULE_PATH/backdoor.rules 553 | #include $RULE_PATH/bad-traffic.rules 554 | #include $RULE_PATH/blacklist.rules 555 | #include $RULE_PATH/botnet-cnc.rules 556 | #include $RULE_PATH/browser-chrome.rules 557 | #include $RULE_PATH/browser-firefox.rules 558 | #include $RULE_PATH/browser-ie.rules 559 | #include $RULE_PATH/browser-other.rules 560 | #include $RULE_PATH/browser-plugins.rules 561 | #include $RULE_PATH/browser-webkit.rules 562 | #include $RULE_PATH/chat.rules 563 | #include $RULE_PATH/content-replace.rules 564 | #include $RULE_PATH/ddos.rules 565 | #include $RULE_PATH/dns.rules 566 | #include $RULE_PATH/dos.rules 567 | #include $RULE_PATH/experimental.rules 568 | #include $RULE_PATH/exploit-kit.rules 569 | #include $RULE_PATH/exploit.rules 570 | #include $RULE_PATH/file-executable.rules 571 | #include $RULE_PATH/file-flash.rules 572 | #include $RULE_PATH/file-identify.rules 573 | #include $RULE_PATH/file-image.rules 574 | #include $RULE_PATH/file-multimedia.rules 575 | #include $RULE_PATH/file-office.rules 576 | #include $RULE_PATH/file-other.rules 577 | #include $RULE_PATH/file-pdf.rules 578 | #include $RULE_PATH/finger.rules 579 | #include $RULE_PATH/ftp.rules 580 | #include $RULE_PATH/icmp-info.rules 581 | #include $RULE_PATH/icmp.rules 582 | #include $RULE_PATH/imap.rules 583 | #include $RULE_PATH/indicator-compromise.rules 584 | #include $RULE_PATH/indicator-obfuscation.rules 585 | #include $RULE_PATH/indicator-shellcode.rules 586 | #include $RULE_PATH/info.rules 587 | #include $RULE_PATH/malware-backdoor.rules 588 | #include $RULE_PATH/malware-cnc.rules 589 | #include $RULE_PATH/malware-other.rules 590 | #include $RULE_PATH/malware-tools.rules 591 | #include $RULE_PATH/misc.rules 592 | #include $RULE_PATH/multimedia.rules 593 | #include $RULE_PATH/mysql.rules 594 | #include $RULE_PATH/netbios.rules 595 | #include $RULE_PATH/nntp.rules 596 | #include $RULE_PATH/oracle.rules 597 | #include $RULE_PATH/os-linux.rules 598 | #include $RULE_PATH/os-other.rules 599 | #include $RULE_PATH/os-solaris.rules 600 | #include $RULE_PATH/os-windows.rules 601 | #include $RULE_PATH/other-ids.rules 602 | #include $RULE_PATH/p2p.rules 603 | #include $RULE_PATH/phishing-spam.rules 604 | #include $RULE_PATH/policy-multimedia.rules 605 | #include $RULE_PATH/policy-other.rules 606 | #include $RULE_PATH/policy.rules 607 | #include $RULE_PATH/policy-social.rules 608 | #include $RULE_PATH/policy-spam.rules 609 | #include $RULE_PATH/pop2.rules 610 | #include $RULE_PATH/pop3.rules 611 | #include $RULE_PATH/protocol-finger.rules 612 | #include $RULE_PATH/protocol-ftp.rules 613 | #include $RULE_PATH/protocol-icmp.rules 614 | #include $RULE_PATH/protocol-imap.rules 615 | #include $RULE_PATH/protocol-pop.rules 616 | #include $RULE_PATH/protocol-services.rules 617 | #include $RULE_PATH/protocol-voip.rules 618 | #include $RULE_PATH/pua-adware.rules 619 | #include $RULE_PATH/pua-other.rules 620 | #include $RULE_PATH/pua-p2p.rules 621 | #include $RULE_PATH/pua-toolbars.rules 622 | #include $RULE_PATH/rpc.rules 623 | #include $RULE_PATH/rservices.rules 624 | #include $RULE_PATH/scada.rules 625 | #include $RULE_PATH/scan.rules 626 | #include $RULE_PATH/server-apache.rules 627 | #include $RULE_PATH/server-iis.rules 628 | #include $RULE_PATH/server-mail.rules 629 | #include $RULE_PATH/server-mssql.rules 630 | #include $RULE_PATH/server-mysql.rules 631 | #include $RULE_PATH/server-oracle.rules 632 | #include $RULE_PATH/server-other.rules 633 | #include $RULE_PATH/server-webapp.rules 634 | #include $RULE_PATH/shellcode.rules 635 | #include $RULE_PATH/smtp.rules 636 | #include $RULE_PATH/snmp.rules 637 | #include $RULE_PATH/specific-threats.rules 638 | #include $RULE_PATH/spyware-put.rules 639 | #include $RULE_PATH/sql.rules 640 | #include $RULE_PATH/telnet.rules 641 | #include $RULE_PATH/tftp.rules 642 | #include $RULE_PATH/virus.rules 643 | #include $RULE_PATH/voip.rules 644 | #include $RULE_PATH/web-activex.rules 645 | #include $RULE_PATH/web-attacks.rules 646 | #include $RULE_PATH/web-cgi.rules 647 | #include $RULE_PATH/web-client.rules 648 | #include $RULE_PATH/web-coldfusion.rules 649 | #include $RULE_PATH/web-frontpage.rules 650 | #include $RULE_PATH/web-iis.rules 651 | #include $RULE_PATH/web-misc.rules 652 | #include $RULE_PATH/web-php.rules 653 | #include $RULE_PATH/x11.rules 654 | 655 | ################################################### 656 | # Step #8: Customize your preprocessor and decoder alerts 657 | # For more information, see README.decoder_preproc_rules 658 | ################################################### 659 | 660 | # decoder and preprocessor event rules 661 | # include $PREPROC_RULE_PATH/preprocessor.rules 662 | # include $PREPROC_RULE_PATH/decoder.rules 663 | # include $PREPROC_RULE_PATH/sensitive-data.rules 664 | 665 | ################################################### 666 | # Step #9: Customize your Shared Object Snort Rules 667 | # For more information, see http://vrt-blog.snort.org/2009/01/using-vrt-certified-shared-object-rules.html 668 | ################################################### 669 | 670 | # dynamic library rules 671 | # include $SO_RULE_PATH/bad-traffic.rules 672 | # include $SO_RULE_PATH/chat.rules 673 | # include $SO_RULE_PATH/dos.rules 674 | # include $SO_RULE_PATH/exploit.rules 675 | # include $SO_RULE_PATH/icmp.rules 676 | # include $SO_RULE_PATH/imap.rules 677 | # include $SO_RULE_PATH/misc.rules 678 | # include $SO_RULE_PATH/multimedia.rules 679 | # include $SO_RULE_PATH/netbios.rules 680 | # include $SO_RULE_PATH/nntp.rules 681 | # include $SO_RULE_PATH/p2p.rules 682 | # include $SO_RULE_PATH/smtp.rules 683 | # include $SO_RULE_PATH/snmp.rules 684 | # include $SO_RULE_PATH/specific-threats.rules 685 | # include $SO_RULE_PATH/web-activex.rules 686 | # include $SO_RULE_PATH/web-client.rules 687 | # include $SO_RULE_PATH/web-iis.rules 688 | # include $SO_RULE_PATH/web-misc.rules 689 | 690 | # Event thresholding or suppression commands. See threshold.conf 691 | include threshold.conf 692 | -------------------------------------------------------------------------------- /tnsr_snort_setup.md: -------------------------------------------------------------------------------- 1 | # TNSR IDS Setup using ERSPAN 2 | 3 | ### Test Layout: 4 | 5 | | Role | IP Address | Interface | 6 | |--|--|--| 7 | | Traffic source VM | 203.0.113.10/24 | ens224 | 8 | | TNSR WAN | 203.0.113.2/24 | GigabitEthernet13/0/0 | 9 | | TNSR LAN | 192.0.2.1/24 | GigabitEthernet1b/0/0 | 10 | | IDS/Target VM | 192.0.2.5/24 | ens256 | 11 | 12 | ### GRE/ERSPAN Layout: 13 | 14 | | GRE Role | Value | 15 | |--|--| 16 | | GRE/ERSPAN Source | 192.0.2.1 | 17 | | GRE/ERSPAN Receiver | 192.0.2.5 | 18 | | Interface spanned to GRE | GigabitEthernet13/0/0 | 19 | 20 | ## TNSR Setup 21 | 22 | Add the ACL which will be filled by `tnsrids`, it needs a permit rule that 23 | will always come last: 24 | 25 | configure 26 | acl snortblock 27 | rule 2147483646 28 | action permit 29 | ip-version ipv4 30 | exit 31 | exit 32 | 33 | Configure the external interface, and add the ACL from above: 34 | 35 | interface GigabitEthernet13/0/0 36 | ip address 203.0.113.2/24 37 | description External/WAN 38 | access-list input acl snortblock sequence 10 39 | enable 40 | exit 41 | 42 | Configure the internal interface: 43 | 44 | interface GigabitEthernet1b/0/0 45 | ip address 192.0.2.1/24 46 | description Internal/LAN 47 | enable 48 | exit 49 | 50 | Configure the GRE/ERSPAN interface and corresponding SPAN: 51 | 52 | gre gre1 53 | dest 192.0.2.5 54 | source 192.0.2.1 55 | tunnel-type erspan session-id 1 56 | instance 1 57 | exit 58 | int gre1 59 | enable 60 | exit 61 | span GigabitEthernet13/0/0 62 | onto gre1 hw both 63 | exit 64 | 65 | Enable the RESTCONF server: 66 | 67 | restconf 68 | enable true 69 | global authentication-type client-certificate 70 | global server-certificate SRV 71 | global server-key SRV 72 | global server-ca-cert-path TNSR 73 | server dataplane 192.0.2.1 443 true 74 | 75 | **WARNING**: You are able to use previously created certificate or to create it through the TNSR 76 | 77 | ## Setup tnsrids Daemon 78 | 79 | See [README.md](README.md) 80 | 81 | ## IDS Receiver Setup 82 | 83 | Setup the network interface used between the IDS Receiver and TNSR 84 | 85 | $ sudo vi /etc/sysconfig/network-scripts/ifcfg-ens256 86 | TYPE=Ethernet 87 | PROXY_METHOD=none 88 | BROWSER_ONLY=no 89 | BOOTPROTO=none 90 | DEFROUTE=yes 91 | IPV4_FAILURE_FATAL=no 92 | IPV6INIT=yes 93 | IPV6_AUTOCONF=yes 94 | IPV6_DEFROUTE=yes 95 | IPV6_FAILURE_FATAL=no 96 | IPV6_ADDR_GEN_MODE=stable-privacy 97 | NAME=ens256 98 | UUID=b2953c4e-5fac-411b-b357-6dd50260062f 99 | DEVICE=ens256 100 | ONBOOT=yes 101 | IPADDR=192.0.2.5 102 | PREFIX=24 103 | #GATEWAY=192.0.2.1 104 | DNS1=192.0.2.1 105 | DOMAIN="example.com" 106 | IPV6_PRIVACY=no 107 | 108 | This route is added for testing to/from hosts on the TNSR WAN side: 109 | 110 | $ sudo vi /etc/sysconfig/network-scripts/route-ens256 111 | 203.0.113.0/24 via 192.0.2.1 dev ens256 112 | 113 | Use this command to temporarily add a the route instead of making it permanent: 114 | 115 | $ sudo route add -net 203.0.113.0/24 gw 192.0.2.1 116 | 117 | Traffic arriving on this system will need to be passed through the host firewall 118 | which is likely `firewalld` if this is a CentOS system. Alternately, disable 119 | `firewalld` since this system should be isolated on the network and only receiving traffic: 120 | 121 | $ sudo systemctl stop firewalld 122 | $ sudo systemctl disable firewalld 123 | 124 | Restart the network and pick up the new interface configuration:: 125 | 126 | $ sudo systemctl restart network 127 | 128 | This next step is likely optional. There is no need to process or handle the 129 | received traffic on an interface in this way, it only needs to arrive at the 130 | interface snort is bound to, it can decode the GRE directly. 131 | 132 | $ sudo modprobe ip_gre 133 | sudo vi /etc/sysconfig/network-scripts/ifcfg-tun0 134 | DEVICE=tun0 135 | BOOTPROTO=none 136 | ONBOOT=yes 137 | TYPE=GRE 138 | #PEER_INNER_IPADDR=198.18.0.1 139 | PEER_OUTER_IPADDR=192.0.2.1 140 | #MY_INNER_IPADDR=198.18.0.2 141 | MY_OUTER_IPADDR=192.0.2.5 142 | 143 | $ sudo ifup tun0 144 | 145 | Setup snort, for example on CentOS by following 146 | https://upcloud.com/resources/tutorials/installing-snort-on-centos 147 | 148 | Snort **DOES NOT** need to run on the GRE interface/tun, only on the interface with the address used to receive the GRE/ERSPAN traffic. Snort will see and decapsulate the GRE traffic internally. 149 | 150 | Snort `HOME_NET` should include addresses from TNSR to alert on: 151 | 152 | ipvar HOME_NET [192.0.2.0/24,203.0.113.2/32] 153 | 154 | In the `snort` configuration, use `alert_syslog` output: 155 | 156 | output alert_syslog: LOG_LOCAL5 LOG_ALERT 157 | 158 | Run snort: 159 | 160 | $ sudo snort -i ens256 -u snort -g snort -c /etc/snort/snort.conf 161 | 162 | Configure `rsyslog` to transport messages to the host where `tnsrids` is running:: 163 | 164 | $ sudo vi /etc/rsyslog.conf 165 | local5.* @172.27.10.36:12345 166 | 167 | Restart rsyslog: 168 | 169 | $ sudo systemctl restart rsyslog 170 | 171 | A rule to flag any ICMP traffic is a good test to generate alerts quickly. 172 | 173 | $ sudo vi /etc/snort/rules/local.rules 174 | alert icmp !$HOME_NET any -> $HOME_NET any (msg:"ICMP test"; sid:10000001; rev:001; 175 | 176 | ## Traffic Generator Setup (Testing) 177 | 178 | $ sudo vi /etc/sysconfig/network-scripts/ifcfg-ens224 179 | TYPE=Ethernet 180 | PROXY_METHOD=none 181 | BROWSER_ONLY=no 182 | BOOTPROTO=none 183 | DEFROUTE=yes 184 | IPV4_FAILURE_FATAL=no 185 | IPV6INIT=yes 186 | IPV6_AUTOCONF=yes 187 | IPV6_DEFROUTE=yes 188 | IPV6_FAILURE_FATAL=no 189 | IPV6_ADDR_GEN_MODE=stable-privacy 190 | NAME=ens224 191 | UUID=5a2a73ce-3c7b-4cf0-b32c-309c50247b77 192 | DEVICE=ens224 193 | ONBOOT=yes 194 | IPADDR=203.0.113.10 195 | PREFIX=24 196 | #GATEWAY=172.27.44.1 197 | #DNS1=172.27.32.5 198 | #DNS2=172.27.32.6 199 | DOMAIN="example.com" 200 | IPV6_PRIVACY=no 201 | 202 | Add a route similar to the IDS box, to reach the other side: 203 | 204 | $ sudo vi /etc/sysconfig/network-scripts/route-ens224 205 | 192.0.2.0/24 via 203.0.113.2 dev ens224 206 | 207 | Or temporarily add the route: 208 | 209 | $ sudo route add -net 192.0.2.0/24 gw 203.0.113.2 210 | 211 | Restart the network services to pick up new interface settings:: 212 | 213 | $ sudo systemctl restart network 214 | 215 | When testing, use `ping` or `hping` to source traffic from other addresses, to see if alerts trigger ACL entries as expected: 216 | 217 | $ ping -I ens224 203.0.113.2 218 | $ sudo hping 203.0.113.2 -I ens224 --icmp --spoof 203.0.113.66 219 | $ nc -vz -s 203.0.113.10 203.0.113.2 22 220 | 221 | Install `hping` from FreeBSD packages/ports or using your distribution's package manager (on CentOS it is `hping3`, available with `epel-release`). 222 | Similarly, `nmap` and its included utility `nc` are useful for generating traffic that can tickle snort to alert. 223 | 224 | CentOS 7: 225 | 226 | $ sudo yum install epel-release 227 | $ sudo yum install hping3 nmap 228 | 229 | ## Resources 230 | 231 | IDS configuration used during testing: [snort.conf](snort.conf) 232 | -------------------------------------------------------------------------------- /tnsrids-docker.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=tnsrids container 3 | After=docker.service 4 | 5 | [Service] 6 | Restart=always 7 | ExecStart=/usr/bin/docker start -a tnsrids_latest 8 | ExecStop=/usr/bin/docker stop -t 2 tnsrids_latest 9 | 10 | [Install] 11 | WantedBy=local.target 12 | -------------------------------------------------------------------------------- /tnsrids.conf: -------------------------------------------------------------------------------- 1 | # tnsrids configuration file 2 | # Supported options are: 3 | # host =
Defaults to localhost 4 | # port = Defaults to 12345 5 | # maxage = after which they will be deleted> 6 | # Default = 60 mins, 0 = never delete 7 | # TLS options 8 | # ca = Defaults to /etc/tnsrids/.tls/ca.crt 9 | # cert = Defaults to /etc/tnsrids/.tls/tnsr.crt 10 | # key = Defaults to /etc/tnsrids/.tls/tnsr.key 11 | 12 | host = https://test-tnsr.netgate.com 13 | maxage = 60 14 | port = 12345 15 | ca = /etc/tnsrids/.tls/ca.crt 16 | cert = /etc/tnsrids/.tls/tnsr.crt 17 | key = /etc/tnsrids/.tls/tnsr.key 18 | 19 | -------------------------------------------------------------------------------- /tnsrids.go: -------------------------------------------------------------------------------- 1 | // Package provides a bridge bwtween Snort hand the TNSR ACL syste./ Snort alert messages are reveived either via 2 | // a TCP socket on localhost, or a Unix socket. The alerts are parsed and used to create RESTCONF calls to TNSR 3 | // which manipulate the ACL 4 | 5 | /* Copyright (c) 2018-2019 Rubicon Communications, LLC (Netgate) 6 | * All rights reserved. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "github.com/robfig/cron" 26 | "gopkg.in/natefinch/lumberjack.v2" // Log writer/rotator 27 | "log" 28 | "os" 29 | "os/signal" 30 | "strconv" 31 | "strings" 32 | ) 33 | 34 | func main() { 35 | var err error 36 | 37 | // Setup logging 38 | log.SetOutput(&lumberjack.Logger{ 39 | Filename: dfltLogpath, // Log file path 40 | MaxSize: 1, // megabytes 41 | MaxBackups: 10, 42 | MaxAge: 28, // days 43 | }) 44 | 45 | // Set up the options/arguments parser 46 | // Create a configuration structure 47 | var tconfig Config 48 | 49 | // Tell it what options and arguments to look for 50 | // name, cmd line arg, string vs bool, help text 51 | tconfig.addOption("verbose", "v", false, "Output log messages to the console", "no") 52 | tconfig.addOption("show", "show", false, "List the current block rules and exit", "no") 53 | tconfig.addOption("reap", "reap", false, "Delete block rules older than minutes and exit", "no") 54 | tconfig.addOption("host", "h", true, "Host name of TNSR instance (including protocol prefix", dfltHost) 55 | tconfig.addOption("port", "p", true, "UDP port on which to listen for alert messages", dfltPort) 56 | tconfig.addOption("capath", "ca", true, "TLS certificate authority file path", dfltCA) 57 | tconfig.addOption("certpath", "cert", true, "TLS certificate file path", dfltCert) 58 | tconfig.addOption("keypath", "key", true, "TLS key file path", dfltKey) 59 | tconfig.addOption("maxage", "m", true, "Maximum age of rules before deletion. 0 = never delete", dfltMaxage) 60 | 61 | // Now process the command line & config file into a map of options and values 62 | options := tconfig.read() 63 | 64 | if options["help"] == "yes" { 65 | tconfig.printUsage("tnsrids usage:") 66 | return 67 | } 68 | 69 | // Update the global vars 70 | if options["verbose"] == "yes" { 71 | verbose = true 72 | } 73 | 74 | tnsrhost = options["host"] 75 | maxruleage, _ = strconv.ParseUint(options["maxage"], 10, 64) 76 | maxruleage *= 60 // Convert to seconds 77 | port := options["port"] 78 | 79 | // Attempt to initilize TLS 80 | useTLS = false 81 | 82 | if strings.HasPrefix(tnsrhost, "https://") { 83 | err = TLSSetup(options["capath"], options["certpath"], options["keypath"]) 84 | if err != nil { 85 | if verbose { 86 | fmt.Println(err) 87 | } 88 | 89 | log.Fatal(err) 90 | } 91 | } 92 | 93 | // Just list the installed ACL rules and quit 94 | if options["show"] == "yes" { 95 | err := showACLs() 96 | if err != nil { 97 | fmt.Printf("Unable to retrieve rules: %v\n", err) 98 | } 99 | return 100 | } 101 | 102 | // Just delete the old ACL rules and quit 103 | if options["reap"] == "yes" { 104 | err := reapACLs() 105 | if err != nil { 106 | fmt.Printf("ERROR: Failed to reap old rule: %v\n", err) 107 | } 108 | 109 | return 110 | } 111 | 112 | // Set up a timer for regular tasks if maxruleage > 0 113 | tnsrCron := cron.New() 114 | if maxruleage > 0 { 115 | // Such as reaping old rules 116 | tnsrCron.AddFunc(reapPeriod, func() { reapACLs() }) 117 | tnsrCron.Start() 118 | } 119 | 120 | // Prepare a handler to catch terminating signals (^C etc) 121 | c := make(chan os.Signal, 1) 122 | signal.Notify(c, os.Interrupt) 123 | 124 | go func() { 125 | <-c 126 | if verbose { 127 | fmt.Println("Cleaning up and exiting") 128 | } 129 | 130 | // Close the cron process 131 | if maxruleage > 0 { 132 | tnsrCron.Stop() 133 | } 134 | 135 | os.Exit(2) 136 | }() 137 | 138 | // Clean out any old rules 139 | err = reapACLs() 140 | if err != nil { 141 | log.Fatal("Unable to reap old rules prior to starting server") 142 | } 143 | 144 | // And finally start the UDP listener 145 | // This also starts a number of go routines to process Snort alerts and update the TNSR instance 146 | startServer(port) 147 | } 148 | -------------------------------------------------------------------------------- /tnsrids.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Netgate TNSR IDS daemon 3 | Requires=network-online.target 4 | After=network.target network-online.target multi-user.target 5 | 6 | [Service] 7 | Type=simple 8 | ExecStart=/usr/local/sbin/tnsrids 9 | Restart=always 10 | RestartSec=10 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /tnsrids_test.go: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018-2019 Rubicon Communications, LLC (Netgate) 2 | * All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "reflect" 21 | "testing" 22 | ) 23 | 24 | // Creat a set of program options and ensure that they are combined in a manner that lets the command line 25 | // override the config file, or use the defaults if none are provided 26 | func TestMergeItems(t *testing.T) { 27 | 28 | var tconfig Config 29 | 30 | tconfig.addOption("reap", "reap", false, "Reap block rules and exit", "no") 31 | tconfig.addOption("show", "show", false, "List the current block rules and exit", "no") 32 | tconfig.addOption("host", "h", true, "Host name of TNSR instance (including protocol prefix", dfltHost) 33 | tconfig.addOption("port", "p", true, "UDP port on which to listen for alert messages", dfltPort) 34 | 35 | args := map[string]string{"host": "192.168.1.4", "port": "", "show": "", "reap": "yes"} 36 | conf := map[string]string{"host": "192.168.1.4", "port": "4444", "show": "", "reap": "no"} 37 | expected := map[string]string{"host": "192.168.1.4", "port": "4444", "show": "no", "reap": "yes"} 38 | 39 | received := tconfig.mergeItems(args, conf) 40 | 41 | if !reflect.DeepEqual(expected, received) { 42 | t.Errorf("mergeItems() failed. Expected %v but received %v", expected, received) 43 | } 44 | } 45 | 46 | // Ensure that an IPv4 address is properly extracted from an alert string 47 | func TestFindIP(t *testing.T) { 48 | var tests = []struct { 49 | alert string 50 | address string 51 | }{ 52 | {"This alert contains 172.21.2.4", "172.21.2.4"}, 53 | {"This alert contains 192.168.12.14/22", "192.168.12.14"}, 54 | {"192.168.12.14:9090 is contained in this alert", "192.168.12.14"}, 55 | } 56 | 57 | for _, test := range tests { 58 | addr := findIP(test.alert) 59 | if test.address != addr { 60 | t.Errorf("Expected IP address %s, but got %s", test.address, addr) 61 | } 62 | } 63 | } 64 | 65 | // Tests the generation of the next sequence number by creating an aclcache with various sequence numbers, missing a number 66 | // at the start of the list, in the middle, at the end, or with a sequence # > maxSeqNum 67 | func TestGetNextSeqNum(t *testing.T) { 68 | var tests = []struct { 69 | s1 uint64 70 | s2 uint64 71 | s3 uint64 72 | s4 uint64 73 | next uint64 74 | }{ 75 | {1, 2, 3, 4, 5}, 76 | {2, 3, 4, 5, 1}, 77 | {1, 2, 3, 6, 4}, 78 | {1, 2, 3, maxSeqNum + 1, 4}, 79 | } 80 | 81 | for _, test := range tests { 82 | var rule AAclRule 83 | aclcache.AclRule = aclcache.AclRule[:0] // Clear the list of rules 84 | // Add four rules with the seqence numbers specified in the test data 85 | rule.Sequence = test.s1 86 | rule.Action = "deny" 87 | aclcache.AclRule = append(aclcache.AclRule, rule) 88 | rule.Sequence = test.s2 89 | rule.Action = "deny" 90 | aclcache.AclRule = append(aclcache.AclRule, rule) 91 | rule.Sequence = test.s3 92 | rule.Action = "deny" 93 | aclcache.AclRule = append(aclcache.AclRule, rule) 94 | rule.Sequence = test.s4 95 | rule.Action = "deny" 96 | aclcache.AclRule = append(aclcache.AclRule, rule) 97 | 98 | ns := getNextSeqNum() 99 | 100 | if ns != test.next { 101 | t.Errorf("Expected sequence number %d but got %d", test.next, ns) 102 | } 103 | } 104 | } 105 | 106 | // Tests whether the presence of a rule in the cache can be verified 107 | func TestRuleExists(t *testing.T) { 108 | aclcache.AclRule = aclcache.AclRule[:0] // Clear the list of rules 109 | var rule AAclRule 110 | var rule2 AAclRule 111 | 112 | rule.DstIPPrefix = "192.168.1.100" 113 | aclcache.AclRule = append(aclcache.AclRule, rule) 114 | rule2.SrcIPPrefix = "192.168.10.100" 115 | aclcache.AclRule = append(aclcache.AclRule, rule2) 116 | 117 | if ruleExists("172.2.2.2") { 118 | t.Errorf("Host 172.2.2.2 should not exist, but it does") 119 | } 120 | 121 | if !ruleExists("192.168.1.100") { 122 | t.Errorf("Dst 192.168.1.100 should exist, but it does not") 123 | } 124 | 125 | if !ruleExists("192.168.10.100") { 126 | t.Errorf("Src 192.168.10.100 should exist, but it does not") 127 | } 128 | } 129 | --------------------------------------------------------------------------------