Skip to content

Commit

Permalink
Merge pull request #29 from jwhb/synproxy
Browse files Browse the repository at this point in the history
Implement SynProxy
  • Loading branch information
jwhb authored Oct 21, 2024
2 parents ae6d307 + 0108fbf commit 94a467b
Show file tree
Hide file tree
Showing 6 changed files with 405 additions and 10 deletions.
299 changes: 299 additions & 0 deletions resources/test/json/synproxy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
{
"nftables": [
{
"metainfo": {
"version": "1.0.6",
"release_name": "Lester Gooch #5",
"json_schema_version": 1
}
},
{
"table": {
"family": "ip",
"name": "synproxy_anonymous",
"handle": 1
}
},
{
"chain": {
"family": "ip",
"table": "synproxy_anonymous",
"name": "PREROUTING",
"handle": 1,
"type": "filter",
"hook": "prerouting",
"prio": -300,
"policy": "accept"
}
},
{
"chain": {
"family": "ip",
"table": "synproxy_anonymous",
"name": "INPUT",
"handle": 2,
"type": "filter",
"hook": "input",
"prio": 0,
"policy": "accept"
}
},
{
"rule": {
"family": "ip",
"table": "synproxy_anonymous",
"chain": "PREROUTING",
"handle": 3,
"expr": [
{
"match": {
"op": "==",
"left": {
"payload": {
"protocol": "tcp",
"field": "dport"
}
},
"right": 8080
}
},
{
"match": {
"op": "in",
"left": {
"payload": {
"protocol": "tcp",
"field": "flags"
}
},
"right": "syn"
}
},
{
"notrack": null
}
]
}
},
{
"rule": {
"family": "ip",
"table": "synproxy_anonymous",
"chain": "INPUT",
"handle": 4,
"expr": [
{
"match": {
"op": "==",
"left": {
"payload": {
"protocol": "tcp",
"field": "dport"
}
},
"right": 8080
}
},
{
"match": {
"op": "in",
"left": {
"ct": {
"key": "state"
}
},
"right": [
"invalid",
"untracked"
]
}
},
{
"synproxy": {
"mss": 1460,
"wscale": 7,
"flags": [
"timestamp",
"sack-perm"
]
}
}
]
}
},
{
"rule": {
"family": "ip",
"table": "synproxy_anonymous",
"chain": "INPUT",
"handle": 5,
"expr": [
{
"match": {
"op": "in",
"left": {
"ct": {
"key": "state"
}
},
"right": "invalid"
}
},
{
"drop": null
}
]
}
},
{
"table": {
"family": "ip",
"name": "synproxy_named",
"handle": 2
}
},
{
"synproxy": {
"family": "ip",
"name": "synproxy_named_1",
"table": "synproxy_named",
"handle": 3,
"mss": 1460,
"wscale": 7,
"flags": [
"timestamp",
"sack-perm"
]
}
},
{
"synproxy": {
"family": "ip",
"name": "synproxy_named_2",
"table": "synproxy_named",
"handle": 4,
"mss": 1460,
"wscale": 5
}
},
{
"chain": {
"family": "ip",
"table": "synproxy_named",
"name": "PREROUTING",
"handle": 1,
"type": "filter",
"hook": "prerouting",
"prio": -300,
"policy": "accept"
}
},
{
"chain": {
"family": "ip",
"table": "synproxy_named",
"name": "FORWARD",
"handle": 2,
"type": "filter",
"hook": "forward",
"prio": 0,
"policy": "accept"
}
},
{
"rule": {
"family": "ip",
"table": "synproxy_named",
"chain": "PREROUTING",
"handle": 5,
"expr": [
{
"match": {
"op": "==",
"left": {
"payload": {
"protocol": "tcp",
"field": "dport"
}
},
"right": 8080
}
},
{
"match": {
"op": "in",
"left": {
"payload": {
"protocol": "tcp",
"field": "flags"
}
},
"right": "syn"
}
},
{
"notrack": null
}
]
}
},
{
"rule": {
"family": "ip",
"table": "synproxy_named",
"chain": "FORWARD",
"handle": 7,
"expr": [
{
"match": {
"op": "in",
"left": {
"ct": {
"key": "state"
}
},
"right": [
"invalid",
"untracked"
]
}
},
{
"synproxy": {
"map": {
"key": {
"payload": {
"protocol": "ip",
"field": "saddr"
}
},
"data": {
"set": [
[
{
"prefix": {
"addr": "192.168.1.0",
"len": 24
}
},
"synproxy_named_1"
],
[
{
"prefix": {
"addr": "192.168.2.0",
"len": 24
}
},
"synproxy_named_2"
]
]
}
}
}
}
]
}
}
]
}
9 changes: 1 addition & 8 deletions resources/test/nft-to-json.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,7 @@ OUTPUT_DIR=./json

convert_file () {
INFILE=$1
NETNS=nftables
ip netns delete $NETNS 2>/dev/null || true
ip netns add $NETNS
(
ip netns exec $NETNS nft -f "${INFILE}"
ip netns exec $NETNS nft -j list ruleset
) || true
ip netns delete $NETNS
unshare -rn sh -exc "nft -f \"${INFILE}\" && nft -j list ruleset"
}

for nftfile in "$INPUT_DIR"/*.nft; do
Expand Down
41 changes: 41 additions & 0 deletions resources/test/nft/synproxy.nft
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
table ip synproxy_anonymous {

chain PREROUTING {
type filter hook prerouting priority raw; policy accept;
tcp dport 8080 tcp flags syn notrack
}

chain INPUT {
type filter hook input priority filter; policy accept;
tcp dport 8080 ct state invalid,untracked synproxy mss 1460 wscale 7 timestamp sack-perm
ct state invalid drop
}
}

table ip synproxy_named {

synproxy synproxy_named_1 {
mss 1460
wscale 7
timestamp sack-perm
}

synproxy synproxy_named_2 {
mss 1460
wscale 5
}

chain PREROUTING {
type filter hook prerouting priority raw; policy accept;
tcp dport 8080 tcp flags syn notrack
}

chain FORWARD {
type filter hook forward priority filter; policy accept;

ct state invalid,untracked synproxy name ip saddr map {
192.168.1.0/24 : "synproxy_named_1",
192.168.2.0/24 : "synproxy_named_2",
}
}
}
29 changes: 29 additions & 0 deletions src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub enum NfListObject {
CTTimeout(CTTimeout),
#[serde(rename = "ct expectation")]
CTExpectation(CTExpectation),
SynProxy(SynProxy),
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -449,3 +450,31 @@ pub struct CTExpectation {
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<u32>,
}

/// [SynProxy] intercepts new TCP connections and handles the initial 3-way handshake using
/// syncookies instead of conntrack to establish the connection.
///
/// Named SynProxy requires **nftables 0.9.3 or newer**.
///
/// [SynProxy]: https://wiki.nftables.org/wiki-nftables/index.php/Synproxy
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct SynProxy {
/// The table’s family.
pub family: NfFamily,
/// The table’s name.
pub table: String,
/// The synproxy's name.
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// The synproxy's handle. For input, it is used by the [delete command][NfCmd::Delete] only.
pub handle: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
/// The maximum segment size (must match your backend server).
pub mss: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
/// The window scale (must match your backend server).
pub wscale: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
/// The synproxy's [flags][crate::types::SynProxyFlag].
pub flags: Option<HashSet<SynProxyFlag>>,
}
Loading

0 comments on commit 94a467b

Please sign in to comment.