PolarProxy TLS Firewall

PolarProxy can be used as a TLS firewall by providing a ruleset that defines match conditions and corresponding actions.

The rule based logic is activated by starting PolarProxy with --ruleset FILE, where FILE can be either a path or a URL to a ruleset file.

Ruleset Format

Ruleset files contain a list of rules and a default action in JSON format. PolarProxy attempts to match the rules from top to bottom of the ruleset. The action for the first matched rule will be performed for each incoming connection. A default action must be defined, which is performed if no rule matches.

The following example defines a basic ruleset for inspection of TLS traffic. Incoming connections are blocked (closed) if the client attempts to communicate using a protocol other than TLS, or if the client doesn't provide a domain name using the SNI extension. The default policy's inspect action will trigger TLS inspection through decryption and re-encryption if none of the block rules are matched.

{ "name": "TLS inspection", "version": "1.0", "rules": [ { "active": true, "match": { "type": "nontls" }, "action": { "type": "block" } }, { "active": true, "match": { "type": "domain", "expression": null }, "action": { "type": "block" } } ], "default": { "action": { "type": "inspect" }, "description": "Default policy if no rule matched" } }

Additional ruleset examples can be found in the ruleset*.json files included in our PolarProxy downloads and in our PolarProxy GitHub repo.

Match Types

Type Required attributes Optional attributes
alpn expression
decrypt_fail expression, period
domain expression
domain_in_file expression format, gzip, reload
domain_regex expression
ja3 expression
ja4 expression

alpn matches on the Application-Layer Protocol Negotiation Extension provided by the client, which indicates which protocol is to be used inside the TLS encryption. Common values for the expression attribute are "http/1.1" or "h2", but additional ALPN values are provided in IANA's Protocol ID list.

decrypt_fail is used to define a dynamic fallback action in case a particular client-server connection fails repeatedly, for example due to certificate pinning or if PolarProxy's root CA isn't trusted by the client. The fallback action must be one of the Action Types defined below, typically "bypass" (for fail-open) or "block" (for fail-close). It is also possible to use decrypt_fail to redirect a client somewhere else. The expression attribute is an integer that defines how many failed TLS connections there must be between a particular client and server before the rule will match. The period is an integer that specifies a look back period (in seconds) during which the TLS connection failures must have occurred.

domain matches on the domain name provided by the client in the SNI extension of TLS. It is possible to match sessions that don't use SNI by setting the expression attribute value to null, as in the example ruleset on this page. A matched domain must be a fully qualified domain name (FQDN) and no wildcards are allowed. Use domain_regex if more flexible matching is needed.

domain_in_file specifies a path or URL to a file in the expression attribute value.

Supported values for the format attribute are:

The reload attribute can be used to reload the domain list file periodically. The value defines the reload period in seconds, i.e. 3600 will reload the list every hour.

The gzip attribute has a boolean value indicating if the file specified in expression is gzip compressed. Supported values are false and true.

This match example checks if a domain is in ThreatFox's IOC data:

"match": { "type": "domain_in_file", "expression": "https://threatfox.abuse.ch/downloads/threatfox.rpz", "format": "rpz", "gzip": false, "reload": 3600 }

domain_regex performs a regular expression matching on the domain name provided by the client in the SNI extension of TLS. Here's an example expression that matches various Google domains:

"match": { "type": "domain_regex", "expression": "\\.google\\.com$|\\.gvt1\\.com$|\\.1e100\\.net$" }

Note: Backslash must be represented as "\\" because it's an escape character in JSON.

ja3 matches on the client TLS handshake using a JA3 hash. For example, "51c64c77e60f3980eea90869b68c58a8" will match Emotet and Dridex traffic.

ja4 also matches on the client TLS handshake, but it uses the more advanced JA4 format. As an example, "t13i190800_9dc949149365_97f8aa674fd9" matches Sliver traffic.

nontls type is used to match traffic that is using protocols other than TLS.

Action Types

Type Required attributes Optional attributes
block log
bypass target, throttle, log
encrypt sni, target throttle, log
inspect alpn, sni, target, throttle, log
terminate alpn, target, throttle, log

The block action closes the incoming connection.

The bypass action lets the incoming connection pass through PolarProxy to the desired target. This feature is also known as pass-through or splicing because PolarProxy splices the external TCP connection with the internal one without interfering with the transferred data. Bypassed connections are end-to-end encrypted and therefore don't require clients to trust PolarProxy's root CA, which allows applications to communicate even though certificate pinning is used.

The encrypt action encrypts the outgoing session with TLS. This can be used to encrypt traffic from legacy applications that don't support TLS. The target attribute must be specified unless the used proxy type provides explicit information about where the client wanted to connect, for example when running PolarProxy as a SOCKS or HAProxy server.

The inspect action decrypts the incoming TLS connection, provides access to the decrypted traffic, and then re-encrypts the traffic in a connection to the target host. This technique is also known as bumping or TLS bridging.

The terminate action terminates the incoming TLS connection by acting as a TLS server and forwards the decrypted traffic to the desired target. This is also known as TLS offloading.

Action Attributes

The alpn attribute value should be a string, such as "http/1.1", "h2" or "imap", which PolarProxy will send to the connecting client using the ALPN TLS extension to let it know which protocol should be used inside of TLS. We recommend using protocol strings defined in IANA's Protocol ID list, but PolarProxy allows non-registered ALPN values to be used as well.

The sni attribute is used to specify a custom domain name in the SNI extension of PolarProxy's outgoing TLS connection. This SNI also affects which IP PolarProxy will connect to, since the IP address resolved by PolarProxy for the provided domain name will be used as the new target for the outgoing connection.

The target attribute defines a custom IP address or domain name and TCP port that PolarProxy should connect the outgoing session to. This value overrides whatever is defined in the sni attribute with regards to which host to connect to, but not with regards to which domain name to use in the SNI extension. The attribute value should be formatted as "host:port", for example "" or "www.example.com:443".

The throttle attribute limits the bandwidth of a matched session. The value is bytes per second, but a 'k', 'm' or 'g' suffix can be used to specify kilo-, mega-, or gigabytes per second. The following example limits the throughput to 10 kB/s (80 kbit/s): "throttle": "10k"

The log attribute can be used to define a custom log entry to be written to stderr or systemd journal when the rule is matched.

Combined Matches

It's possible to use the "match_all" property instead of the normal "match" in order to specify multiple matches that must be fulfilled in order to trigger a particular action. The match_all property contains a list of matches, for example like this:

"match_all": [ { "type": "ja3", "expression": "8fffe46776bf62f45716b436b6dbc131" }, { "type": "ja4", "expression": "t10d060600_4dc025c38c38_950472255fe9" } ]

______ʕ•ᴥ•ʔ____ |_|___|___|___|_| |___|___|___|___| |_|___|___|___|_|