-
-
Notifications
You must be signed in to change notification settings - Fork 648
Rules
Rules are stored as JSON files in the directory passed via -rules-path (by default /etc/opensnitchd/rules). In its simplest form, a rule looks like this:
{
"created": "2018-04-07T14:13:27.903996051+02:00",
"updated": "2018-04-07T14:13:27.904060088+02:00",
"name": "deny-simple-www-google-analytics-l-google-com",
"enabled": true,
"precedence": false,
"action": "deny",
"duration": "always",
"operator": {
"type": "simple",
"operand": "dest.host",
"data": "www-google-analytics.l.google.com",
"sensitive": false
}
}| Field | Description |
|---|---|
| created | UTC date and time of creation. |
| updated | UTC date and time of the last update. |
| name | The name of the rule. |
| description | Free-text annotation field, optional, daemon ignores it. |
| enabled | Enable or disable the rule. |
| precedence |
true or false. Sets if a rule take precedence over the rest (>= v1.2.0). If a connection matches this rule, no other rules will be evaluated. |
| action | Can be deny, reject or allow. reject kills the socket, terminating the connection immediately. deny drops/ignores the packet. |
| duration | The duration of the rule in Duration format. always is always used when the rule is written to disk. The rest of the options are temporary, until they reach the deadline: 12h, 5h, 1h, 30s, or once to only run the rule one time. |
| operator.type |
simple, regexp, network, lists, list, range. |
simple is a simple == comparison. |
|
regexp matches the regexp from the data field against the connection. Uses Go's RE2 — no lookahead/lookbehind/backrefs. Note that the GUI uses Python's regex engine to validate input, so a pattern accepted by the editor may still fail to compile in the daemon. |
|
network checks if the IP of a connection is contained within the specified network range (127.0.0.1/8, 192.168.1.0/24, etc) |
|
lists will look for matches on lists of something (domains, IPs, etc). Typically used to create blocklists
|
|
range (v1.9.0) will check if an Operand (dest.port or source.port) is within the given range. |
|
list, a combination of all of the previous types. |
|
| operator.data | The data of the rule against which an outbound connection will be compared: an IP, a destination port, a command line, etc. |
| operator.sensitive | Case sensitivity, default false (case insensitive). |
| operator.operand | Property of the connection against which the rule will be compared: |
true - will always match |
|
process.path - the absolute path of the executable |
|
process.id PID of the process |
|
process.command (full command line, including path and arguments). Note that cmdlines can contain or not the process name, and the path can be absolute or relative (./cmd -x a). |
|
process.parent.path (v1.7.0) checks against any ancestor in the process tree (parent, grandparent, etc., up to PID 1). To require multiple ancestors, combine with list — but note this matches when each path appears anywhere in the chain, regardless of order or depth. |
|
process.env.ENV_VAR_NAME (use the value of an environment variable of the process given its name). |
|
process.hash.md5 (v1.7.0) - verify the checksum of an executable |
|
process.hash.sha1 |
|
user.id - UID |
|
user.name user name (v1.7.0). Check against a regular system username (no namespaces, containers or virtual user names). |
|
protocol - TCP, UDP, UDPLITE, SCTP, DCCP, ICMP (append "6" for IPv6 protocols: TCP6) |
|
source.port |
|
source.ip |
|
source.network |
|
dest.ip |
|
dest.host |
|
dest.network (v1.3.0) - you can use a network range, or the constants predefined in the file https://github.com/evilsocket/opensnitch/blob/master/daemon/data/network_aliases.json
|
|
dest.port |
|
iface.in (v1.6.0) - name of a network interface |
|
iface.out (v1.6.0) - name of a network interface |
|
lists.domains (v1.4.0) lists of domains in hosts format read more
|
|
lists.domains_regexp (v1.5.0) list of domains with regular expressions (.*\.example\.com) read more |
|
lists.ips (v1.5.0) list of IPs read more
|
|
lists.nets (v1.5.0) list of network ranges read more
|
|
lists.hash.md5 (v1.7.0) list of md5s |
For lists.* operands, the data field is the path to a directory
(not a single file). The daemon loads every file under it that has a
recognized extension (.txt, .list, .dat, etc.). Files without
an extension are silently skipped — see block-lists
for the full file format and examples. lists.domains matches each line
exactly (so example.com does not match www.example.com); use
lists.domains_regexp for wildcard subdomain matching.
All the fields you select when defining a rule will be used to match connections. For example:
-
Rule: allow -> port 443 -> Dst IP 1.1.1.1 -> Protocol TCP -> Host www.site.test
- This rule will match connections to port 443 AND IP 1.1.1.1 AND protocol TCP AND host www.site.test
- connections to IP 2.2.2.2 won't match, connections to port 80 won't match, etc...
-
Rule: allow -> port 53 -> [x] domains list -> [x] network ranges list
- This rule will match connections to port 53 AND domains in the list AND IPs in the network ranges list
-
Rule: allow -> port ^(53|80|443)$ -> UID 1000 -> Path /app/bin/test -> [x] domains list
- This rule will match connections to ports (53 OR 80 OR 443) AND UID 1000 AND Path /app/bin/test AND domains in the specified.
-
If you select multiple lists on the same rule, bear in mind that all the lists must match in order to apply an action. Read this discussion to learn more.
The GUI auto-generates rule names based on the fields you specify. If these include long paths, command lines, or domains, the resulting name can get quite long (up to 128 characters). However, you can edit the rule name as you please.
When a connection is attempted, OpenSnitch evaluates each of the enabled rules. The rules are sorted in the alphabetical order of rule names (since v.1.2.0). OpenSnitch goes through the list and as soon as it encounters a Deny/Reject rule or an Important ([x] Priority rule) (since v1.2.0) that matches the connection, that rule will be immediately selected as the effective rule. If no such rule is found, then the last non-Important Allow rule that matched will be selected. If no rule matched, it shows a pop-up dialogue, or applys the default action if that's not possible.
- In the following example, the Deny rule takes precedence over the Allow rules:
000-allow-chrome-to-specific-domains
001-allow-not-so-important-rule
001-deny-chrome
- In the following example, the first Allow rule takes precedence because it is set to Priority and it comes first in the alphabetical order:
000-allow-chrome-to-specific-domains [x] Priority
001-allow-not-so-important-rule
001-deny-chrome
This way you can not only prioritize critical connections (like VPNs), but also gain performance.
As already mentioned, the order of rules is critical. If you use Firefox and prioritize Allow rules to allow Firefox's connections, web navigation will be faster.
But the type of rule also impacts the rule's performance. regexp and list types are slower than simple because regexp and list types check multiple parameters while simple rules check just one. And regexp is the slowest, because is the more complex type.
An example with a regular expression:
{
"created": "2018-04-07T14:13:27.903996051+02:00",
"updated": "2018-04-07T14:13:27.904060088+02:00",
"name": "deny-any-google-analytics",
"enabled": true,
"precedence": false,
"action": "deny",
"duration": "always",
"operator": {
"type": "regexp",
"sensitive": false,
"operand": "dest.host",
"data": "(?i)google-?analytics"
}
}An example whitelisting a process path:
{
"created": "2018-04-07T15:00:48.156737519+02:00",
"updated": "2018-04-07T15:00:48.156772601+02:00",
"name": "allow-simple-opt-google-chrome-chrome",
"enabled": true,
"precedence": false,
"action": "allow",
"duration": "always",
"operator": {
"type": "simple",
"sensitive": false,
"operand": "process.path",
"data": "/opt/google/chrome/chrome"
}
}Example of a complex rule using the operator list, saved from the GUI (Note: version v1.2.0):
{
"created": "2020-02-07T14:16:20.550255152+01:00",
"updated": "2020-02-07T14:16:20.729849966+01:00",
"name": "deny-list-type-simple-operand-destip-data-1101-type-simple-operand-destport-data-23-type-simple-operand-userid-data-1000-type-simple-operand-processpath-data-usrbintelnetnetkit",
"enabled": true,
"precedence": false,
"action": "deny",
"duration": "always",
"operator": {
"type": "list",
"operand": "list",
"list": [
{
"type": "simple",
"operand": "dest.ip",
"sensitive": false,
"data": "1.1.0.1",
"list": null
},
{
"type": "simple",
"operand": "dest.port",
"sensitive": false,
"data": "23",
"list": null
},
{
"type": "simple",
"operand": "user.id",
"sensitive": false,
"data": "1000",
"list": null
},
{
"type": "simple",
"operand": "process.path",
"sensitive": false,
"data": "/usr/bin/telnet.netkit",
"list": null
}
]
}
}
Some applications have components that communicate in localhost. For example KDE uses kdeinit5 and kwin, Xfce and others use xbrlapi , and GnuPG dirmngr.
If you change daemon's default action to deny these applications will stop working. For example you may notice a delay login to the Desktop Environment (See issues #982 and #965 for more information).
The solution is to allow either localhost connections, or these binaries in particular.
Here's a rule to allow localhost connections:
{
"created": "2023-07-05T10:46:47.904024069+01:00",
"updated": "2023-07-05T10:46:47.921828104+01:00",
"name": "000-allow-localhost",
"enabled": true,
"precedence": true,
"action": "allow",
"duration": "always",
"operator": {
"type": "network",
"operand": "dest.network",
"sensitive": false,
"data": "127.0.0.0/8",
"list": []
}
}If you want to restrict it further, under the Addresses tab you can review what binaries established localhost connections, and then add the absolute path to the rule + destination port.
-
Allow DNS queries only to your configured DNS nameservers:
⚠️ DNS protocol can be used to exfiltrate information from local networks.- Allow
systemd-resolved,dnsmasq,dnscrypt-proxy, etc, to connect only to your DNS nameservers + port 53 + UID. - Besides allowing connections to remote DNS servers (9.9.9.9 for example), you may need to allow connections to localhost IPs (127.0.0.1, etc)
- If you already allowed these stub resolvers, the easiest way would we to delete the existing rule, let it ask you again to allow/deny it, click on the
[+]button and then select from the pop-upfrom this command lineAND to IP x.x.x.x AND_ to port xxx
- Allow
-
Limit what an application can do as much as possible:
-
Filter by executable + command line: You don't want to allow
curlorwgetsystem wide. Instead, allow only a particular command line, for example:command launched:
$ wget https://mirror.karneval.cz/pub/linux/fedora/linux/releases/34/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-34-1.2.isoInstead of allowing
from this executable: wget, use allowfrom this executable+from this command lineYou can narrow it further, by allowing
from this command line+from this User ID+to this IP+to this portOr just let it prompt you to allow/deny it if you don't these commands usually, and allow it for
30s.
-
-
Don't allow
python3,perlorrubybinaries system-wide:-
As explained above, filter by executable + command line + (... more parameters ...) If you allow
python3for example, you'll allow ANYpython3script, so be careful.
-
-
Disable unprivileged namespaces to prevent rules bypass
If
/proc/sys/kernel/unprivileged_userns_cloneis set to 1, change it to 0.Or enable checksums verifying from the Preferences -> Nodes -> Rules.
These recommendations also apply to the Linux Desktop, but are specially important on servers.
Why? If someone gets access to the system, usually there are a few directories where everyone can write files: /tmp, /var/tmp or /dev/shm.
Thus these directories can be used to drop malicious files or download remote binaries to escalate privileges, mine cryptocoins, etc.
Usually the attackers use wget, curl or bash to establish outbound connections (as in these malware examples). So, if you don't need these binaries, just uninstall them.
There are two approaches to secure a server with OpenSnitch:
- Restrict everything by default (
DefaultActionset to deny/reject in thedefault-config.jsonfile) and allow only system binaries and needed apps. Incoming connections will keep working, but NEW outbound connections will be denied. - Allow everything by default, and deny connections from specific locations, or by binary / destination.
-
If you need curl or wget and the
DefaultActionis notallow, restrict their outbound connections as much as possible (this practice applies to any other binary of the server):{ "created": "2020-02-07T14:16:20.550255152+01:00", "updated": "2020-02-07T14:16:20.729849966+01:00", "name": "allow-curl-net-proxy", "description": "allow curl only to 10.168.10.164 on port 8081", "enabled": true, "precedence": false, "action": "allow", "duration": "always", "operator": { "type": "list", "operand": "list", "list": [ { "type": "simple", "operand": "process.path", "sensitive": false, "data": "/usr/bin/curl", "list": null }, { "type": "simple", "operand": "dest.ip", "sensitive": false, "data": "10.168.10.164", "list": null }, { "type": "simple", "operand": "dest.port", "sensitive": false, "data": "8081", "list": null } ] } }Or for example you can allow everything only to the local lan, and let the rest of outbound connections be denied by the DefaultAction:
{ "created": "2023-05-20T20:39:33.765468194+02:00", "updated": "2023-05-20T20:39:33.7655761+02:00", "name": "000-allow-lan", "description": "", "enabled": true, "precedence": true, "nolog": false, "action": "allow", "duration": "always", "operator": { "type": "network", "operand": "dest.network", "sensitive": false, "data": "LAN", "list": [] } } -
When the
DefaultActionisallow, don't allow connections opened by binaries located under certain directories:/dev/shm,/tmp,/var/tmpor/memfd:There're ton of malware examples that drop malicious files to temporary directories (more common on servers than on the desktop):
Collection of Linux malware payloads
https://github.com/timb-machine/linux-malware
(*) Deny [x] From this executable: ^(/memfd|/tmp/|/var/tmp/|/dev/shm/|/var/run|/var/lock).*/etc/opensnitchd/rules/000-deny-tmp.json:
{ "created": "2025-04-26T09:58:03.704090244+02:00", "updated": "2025-04-26T09:58:03.704216578+02:00", "name": "000-deny-tmp", "enabled": true, "precedence": true, "action": "reject", "duration": "always", "operator": { "type": "regexp", "operand": "process.path", "sensitive": false, "data": "^(/var/tmp|/dev|/memfd|/tmp).*", "list": [] } } -
You can also block outbound connections to crypto mining pools and malware domains/ips with blocklists rules.
One of the common reason to compromise servers is to mine cryptocurrencies. Denying connections to the mining pools disrupts the operation.
-
Think also if your web server, database server, etc, needs to establish connections to remote IPs.
In some cases, the download of malicious files is executed from common applications; see the pg_mem campaign
Note that the default policy should be to deny everything unless explicitly allowed. But by creating a rule to deny specifically these directories, you can have a place where to monitor these executions.
Please help us make this wiki better.
How to submit changes: https://github.com/evilsocket/opensnitch/blob/wiki/README.md
- Installation
- Getting started
- Configuration
- Compilation
- GUI translations
- FAQs and common errors
- Examples OpenSnitch in action