Working and okay-looking port forward/accept screen
This commit is contained in:
parent
04ece4cca8
commit
ab42817c2b
186
Cargo.lock
generated
186
Cargo.lock
generated
|
@ -155,30 +155,30 @@ dependencies = [
|
|||
"libc",
|
||||
"once_cell",
|
||||
"parking 2.0.0",
|
||||
"polling",
|
||||
"polling 0.1.9",
|
||||
"socket2",
|
||||
"vec-arena",
|
||||
"vec-arena 0.5.2",
|
||||
"wepoll-sys-stjepang",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "0.2.7"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9424bb88867b003ca32a1d99cf64595c5a310c7322891795f38aa061860d0af"
|
||||
checksum = "016a7f0eda7091ef24ad8562d6503ad8da47af8c432d4d3fa440eea9e89055fe"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite 1.0.0",
|
||||
"futures-lite 1.3.0",
|
||||
"libc",
|
||||
"log",
|
||||
"once_cell",
|
||||
"parking 2.0.0",
|
||||
"polling",
|
||||
"polling 1.0.1",
|
||||
"socket2",
|
||||
"vec-arena",
|
||||
"vec-arena 1.0.0",
|
||||
"waker-fn",
|
||||
"wepoll-sys-stjepang",
|
||||
"winapi",
|
||||
|
@ -186,24 +186,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-mutex"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "065de1ccf10280d0d75c2f3a71a970ee1007c85c51aa3e7deee1df100f1dfadb"
|
||||
checksum = "66941c2577c4fa351e4ce5fdde8f86c69b88d623f3b955be1bc7362a23434632"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "0.1.3"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d510baf319291a41e6c3363df4408a3b196ad18fcb1305ae4e500f2eabe260"
|
||||
checksum = "0bb915df28b8309139bd9c9c700d84c20e5c21385d05378caa84912332d0f6a1"
|
||||
dependencies = [
|
||||
"async-io 0.2.7",
|
||||
"blocking 0.6.1",
|
||||
"async-io 1.0.1",
|
||||
"blocking 1.0.0",
|
||||
"cfg-if",
|
||||
"event-listener",
|
||||
"futures-lite 1.0.0",
|
||||
"futures-lite 1.3.0",
|
||||
"once_cell",
|
||||
"signal-hook",
|
||||
"winapi",
|
||||
|
@ -391,14 +391,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "0.6.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f30e08a950487f80d2de5cbb72772c8bbaed6002dc8d979722dabd034ede18d"
|
||||
checksum = "2640778f8053e72c11f621b0a5175a0560a269282aa98ed85107773ab8e2a556"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"atomic-waker",
|
||||
"fastrand",
|
||||
"futures-lite 1.0.0",
|
||||
"futures-lite 1.3.0",
|
||||
"once_cell",
|
||||
"waker-fn",
|
||||
]
|
||||
|
@ -442,9 +442,6 @@ name = "cc"
|
|||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -523,7 +520,7 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"rand",
|
||||
"sha2",
|
||||
"time 0.2.17",
|
||||
"time 0.2.18",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
|
@ -708,9 +705,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "1.0.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd8ccee4974dccf68838bb2b9c90439238090061c82454af83866ac059eb9f"
|
||||
checksum = "4fc6854fcb40c6446abf6043e82604e42567dcf3d652a5ff4e997fc36876414c"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
|
@ -813,12 +810,6 @@ version = "0.22.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.1"
|
||||
|
@ -875,7 +866,7 @@ dependencies = [
|
|||
"rand",
|
||||
"serde 1.0.115",
|
||||
"serde_json",
|
||||
"serde_qs",
|
||||
"serde_qs 0.6.1",
|
||||
"serde_urlencoded",
|
||||
"url",
|
||||
]
|
||||
|
@ -924,15 +915,6 @@ version = "0.4.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.44"
|
||||
|
@ -1231,9 +1213,22 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1e9fa0ab21ed700cf0c4ebec57ae5496bec942a0aef9545562979a9f75b97aa"
|
||||
checksum = "8fffa183f6bd5f1a8a3e1f60ce2f8d5621e350eed84a62d6daaa5b9d1aaf6fbd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
"wepoll-sys-stjepang",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0835fa5f9af34c170eb38638ae6bc88e1b11ecdd0b968c9d9de8e343450385eb"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -1365,15 +1360,17 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"async-process",
|
||||
"blocking 0.6.1",
|
||||
"blocking 1.0.0",
|
||||
"config",
|
||||
"futures-lite 1.0.0",
|
||||
"futures-lite 1.3.0",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"ructe",
|
||||
"serde 1.0.115",
|
||||
"serde_json",
|
||||
"serde_qs 0.7.0",
|
||||
"serde_with",
|
||||
"sled",
|
||||
"tide",
|
||||
]
|
||||
|
@ -1529,6 +1526,18 @@ dependencies = [
|
|||
"serde 1.0.115",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_qs"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9408a61dabe404c76cec504ec510f7d92f41dc0a9362a0db8ab73d141cfbf93f"
|
||||
dependencies = [
|
||||
"data-encoding",
|
||||
"percent-encoding",
|
||||
"serde 1.0.115",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_test"
|
||||
version = "0.8.23"
|
||||
|
@ -1550,6 +1559,27 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d3d595d64120bbbc70b7f6d5ae63298b62a3d9f373ec2f56acf5365ca8a444"
|
||||
dependencies = [
|
||||
"serde 1.0.115",
|
||||
"serde_with_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4070d2c9b9d258465ad1d82aabb985b84cd9a3afa94da25ece5a9938ba5f1606"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.6.0"
|
||||
|
@ -1609,7 +1639,6 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1702,21 +1731,41 @@ checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
|||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.2.3"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1"
|
||||
checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9"
|
||||
checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
|
@ -1760,9 +1809,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.2.17"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca7ec98a72285d12e0febb26f0847b12d54be24577618719df654c66cadab55d"
|
||||
checksum = "12785163ae8a1cbb52a5db39af4a5baabd3fe49f07f76f952f89d7e89e5ce531"
|
||||
dependencies = [
|
||||
"const_fn",
|
||||
"libc",
|
||||
|
@ -1869,6 +1918,12 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cb18268690309760d59ee1a9b21132c126ba384f374c59a94db4bc03adeb561"
|
||||
|
||||
[[package]]
|
||||
name = "vec-arena"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
|
@ -2010,34 +2065,3 @@ checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
|
|||
dependencies = [
|
||||
"linked-hash-map 0.5.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.5.3+zstd.1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "2.0.5+zstd.1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "1.4.17+zstd.1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"glob",
|
||||
"itertools",
|
||||
"libc",
|
||||
]
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -9,16 +9,18 @@ build = "src/build.rs"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
async-process = "0.1.3"
|
||||
blocking = "0.6.1"
|
||||
async-process = "1.0.0"
|
||||
blocking = "1.0.0"
|
||||
config = { version = "0.10.1", features = ["toml"] }
|
||||
futures-lite = "1.0.0"
|
||||
futures-lite = "1.1.0"
|
||||
mime = "0.3"
|
||||
once_cell = "1.4.1"
|
||||
regex = "1.3.9"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
sled = { version = "0.34.3", features = ["compression"] }
|
||||
serde_qs = "0.7"
|
||||
serde_with = "1.4.0"
|
||||
sled = "0.34.3"
|
||||
tide = "0.13.0"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
108
scss/index.scss
108
scss/index.scss
|
@ -0,0 +1,108 @@
|
|||
body {
|
||||
margin: 0;
|
||||
background-color: #f5f5f5;
|
||||
font-family: sans-serif;
|
||||
padding: 16px 0 48px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: sans-serif;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
section {
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
padding: 16px;
|
||||
|
||||
> h4 {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
thead {
|
||||
background-color: #fdf;
|
||||
border-radius: 2px 2px 0 0;
|
||||
|
||||
tr {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th.port, td.port {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
td.delete {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
form {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
|
||||
.form-body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.submit {
|
||||
padding: 16px;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
padding: 12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: 650px) {
|
||||
body {
|
||||
padding: 8px 0 48px;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 8px 0;
|
||||
|
||||
> h1 {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
> h4 {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table, form {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ pub(crate) enum Proto {
|
|||
}
|
||||
|
||||
impl Proto {
|
||||
fn as_str(&self) -> &'static str {
|
||||
fn as_iptables_str(&self) -> &'static str {
|
||||
match self {
|
||||
Proto::Tcp => "tcp",
|
||||
Proto::Udp => "udp",
|
||||
|
@ -64,7 +64,7 @@ async fn input(
|
|||
external_mask: u8,
|
||||
func: impl Fn(&mut Command) -> &mut Command,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
iptables(move |cmd| {
|
||||
iptables_filter(move |cmd| {
|
||||
func(cmd).args(&[
|
||||
"INPUT",
|
||||
"-d",
|
||||
|
@ -115,7 +115,7 @@ pub(crate) async fn delete_forward_accept(
|
|||
internal_interface,
|
||||
proto,
|
||||
external_port,
|
||||
move |cmd| cmd.arg("-I"),
|
||||
move |cmd| cmd.arg("-D"),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ async fn forward(
|
|||
external_port: u16,
|
||||
func: impl Fn(&mut Command) -> &mut Command,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
iptables(move |cmd| {
|
||||
iptables_filter(move |cmd| {
|
||||
func(cmd).args(&[
|
||||
"FORWARD",
|
||||
"-i",
|
||||
|
@ -135,7 +135,7 @@ async fn forward(
|
|||
"-o",
|
||||
internal_interface,
|
||||
"-p",
|
||||
proto.as_str(),
|
||||
proto.as_iptables_str(),
|
||||
"--dport",
|
||||
&external_port.to_string(),
|
||||
"-m",
|
||||
|
@ -150,28 +150,31 @@ async fn forward(
|
|||
}
|
||||
|
||||
pub(crate) async fn forward_postrouting(
|
||||
proto: Proto,
|
||||
external_ip: Ipv4Addr,
|
||||
external_port: u16,
|
||||
destination_ip: Ipv4Addr,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
forward_postrouting_snat(external_ip, external_port, destination_ip, |cmd| {
|
||||
forward_postrouting_snat(proto, external_ip, external_port, destination_ip, |cmd| {
|
||||
cmd.arg("-I")
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_forward_postrouting(
|
||||
proto: Proto,
|
||||
external_ip: Ipv4Addr,
|
||||
external_port: u16,
|
||||
destination_ip: Ipv4Addr,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
forward_postrouting_snat(external_ip, external_port, destination_ip, |cmd| {
|
||||
forward_postrouting_snat(proto, external_ip, external_port, destination_ip, |cmd| {
|
||||
cmd.arg("-D")
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn forward_postrouting_snat(
|
||||
proto: Proto,
|
||||
external_ip: Ipv4Addr,
|
||||
external_port: u16,
|
||||
destination_ip: Ipv4Addr,
|
||||
|
@ -183,9 +186,9 @@ async fn forward_postrouting_snat(
|
|||
"-d",
|
||||
&destination_ip.to_string(),
|
||||
"-p",
|
||||
"tcp",
|
||||
proto.as_iptables_str(),
|
||||
"-m",
|
||||
"tcp",
|
||||
proto.as_iptables_str(),
|
||||
"--dport",
|
||||
&external_port.to_string(),
|
||||
"-m",
|
||||
|
@ -254,7 +257,7 @@ async fn forward_prerouting_dnat(
|
|||
func(cmd).args(&[
|
||||
"PREROUTING",
|
||||
"-p",
|
||||
proto.as_str(),
|
||||
proto.as_iptables_str(),
|
||||
"-d",
|
||||
&format!("{}/{}", external_ip, external_mask),
|
||||
"--dport",
|
||||
|
@ -266,23 +269,21 @@ async fn forward_prerouting_dnat(
|
|||
"-j",
|
||||
"DNAT",
|
||||
"--to",
|
||||
format!("{}:{}", destination_ip, destination_port).as_str(),
|
||||
&format!("{}:{}", destination_ip, destination_port),
|
||||
])
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn iptables_nat<F>(func: F) -> Result<(), anyhow::Error>
|
||||
where
|
||||
F: Fn(&mut Command) -> &mut Command,
|
||||
{
|
||||
async fn iptables_nat(func: impl Fn(&mut Command) -> &mut Command) -> Result<(), anyhow::Error> {
|
||||
iptables(move |cmd| func(cmd.args(&["-t", "nat"]))).await
|
||||
}
|
||||
|
||||
async fn iptables<F>(func: F) -> Result<(), anyhow::Error>
|
||||
where
|
||||
F: Fn(&mut Command) -> &mut Command,
|
||||
{
|
||||
async fn iptables_filter(func: impl Fn(&mut Command) -> &mut Command) -> Result<(), anyhow::Error> {
|
||||
iptables(move |cmd| func(cmd.args(&["-t", "filter"]))).await
|
||||
}
|
||||
|
||||
async fn iptables(func: impl Fn(&mut Command) -> &mut Command) -> Result<(), anyhow::Error> {
|
||||
let mut command = Command::new("iptables");
|
||||
|
||||
func(&mut command);
|
||||
|
|
33
src/main.rs
33
src/main.rs
|
@ -2,6 +2,7 @@ use blocking::unblock;
|
|||
use futures_lite::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use sled::Db;
|
||||
use tide::log::{self, LogMiddleware};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
||||
|
||||
|
@ -9,7 +10,7 @@ mod iptables;
|
|||
mod rules;
|
||||
mod startup;
|
||||
|
||||
use self::{rules::Rule, startup::Interfaces};
|
||||
use self::{rules::Rule, startup::Interfaces, templates::statics::StaticFile};
|
||||
|
||||
static INTERFACES: Lazy<Interfaces> = Lazy::new(|| {
|
||||
let interfaces = Interfaces::init_blocking().unwrap();
|
||||
|
@ -19,6 +20,8 @@ static INTERFACES: Lazy<Interfaces> = Lazy::new(|| {
|
|||
|
||||
static DB: Lazy<Db> = Lazy::new(|| sled::open("router-db-0-34-3").unwrap());
|
||||
|
||||
static QS: Lazy<serde_qs::Config> = Lazy::new(|| serde_qs::Config::new(3, false));
|
||||
|
||||
async fn rules_page(_: tide::Request<()>) -> tide::Result {
|
||||
let mut html = Vec::new();
|
||||
|
||||
|
@ -37,9 +40,12 @@ async fn rules_page(_: tide::Request<()>) -> tide::Result {
|
|||
}
|
||||
|
||||
async fn save_rule(mut req: tide::Request<()>) -> tide::Result {
|
||||
let rule: Rule = req.body_form().await?;
|
||||
let body_string = req.body_string().await?;
|
||||
|
||||
let rule: Rule = QS.deserialize_str(&body_string)?;
|
||||
|
||||
rules::save(&DB, &rule).await?;
|
||||
|
||||
rules::save(&DB, &rule)?;
|
||||
rules::apply(&INTERFACES, rule).await?;
|
||||
|
||||
Ok(to_rules_page())
|
||||
|
@ -47,7 +53,7 @@ async fn save_rule(mut req: tide::Request<()>) -> tide::Result {
|
|||
|
||||
async fn delete_rule(req: tide::Request<()>) -> tide::Result {
|
||||
let id = req.param("id")?;
|
||||
let rule = rules::delete(&DB, id)?;
|
||||
let rule = rules::delete(&DB, id).await?;
|
||||
rules::unset(&INTERFACES, rule).await?;
|
||||
|
||||
Ok(to_rules_page())
|
||||
|
@ -59,15 +65,32 @@ fn to_rules_page() -> tide::Response {
|
|||
.build()
|
||||
}
|
||||
|
||||
async fn statics(req: tide::Request<()>) -> tide::Result {
|
||||
let file: String = req.param("file")?;
|
||||
|
||||
if let Some(data) = StaticFile::get(&file) {
|
||||
Ok(tide::Response::builder(200)
|
||||
.header("Content-Type", data.mime.to_string())
|
||||
.body(data.content)
|
||||
.build())
|
||||
} else {
|
||||
Ok(tide::Response::builder(404).build())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
future::block_on(async {
|
||||
println!("Hello, world!");
|
||||
|
||||
log::with_level(log::LevelFilter::Debug);
|
||||
|
||||
rules::apply_all(&DB, &INTERFACES).await?;
|
||||
|
||||
let mut app = tide::new();
|
||||
app.with(LogMiddleware::new());
|
||||
app.at("/static/:file").get(statics);
|
||||
app.at("/rules").get(rules_page).post(save_rule);
|
||||
app.at("/rules/:id").delete(delete_rule);
|
||||
app.at("/rules/:id").get(delete_rule);
|
||||
|
||||
let listeners: Vec<String> = INTERFACES
|
||||
.internal
|
||||
|
|
80
src/rules.rs
80
src/rules.rs
|
@ -2,10 +2,17 @@ use crate::{
|
|||
iptables::{self, Proto},
|
||||
startup::Interfaces,
|
||||
};
|
||||
use sled::Db;
|
||||
use once_cell::sync::OnceCell;
|
||||
use sled::{Db, Tree};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
static RULES_TREE: OnceCell<Tree> = OnceCell::new();
|
||||
|
||||
fn rules_tree(db: &Db) -> &'static Tree {
|
||||
RULES_TREE.get_or_init(|| db.open_tree("rules").unwrap())
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Rule {
|
||||
pub(crate) proto: Proto,
|
||||
pub(crate) port: u16,
|
||||
|
@ -21,11 +28,15 @@ impl Rule {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub(crate) enum RuleKind {
|
||||
Accept,
|
||||
Forward { dest_ip: Ipv4Addr, dest_port: u16 },
|
||||
Forward {
|
||||
dest_ip: Ipv4Addr,
|
||||
#[serde(with = "serde_with::rust::display_fromstr")]
|
||||
dest_port: u16,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) async fn apply_all(db: &Db, interfaces: &Interfaces) -> Result<(), anyhow::Error> {
|
||||
|
@ -37,23 +48,31 @@ pub(crate) async fn apply_all(db: &Db, interfaces: &Interfaces) -> Result<(), an
|
|||
}
|
||||
|
||||
pub(crate) fn read(db: &Db) -> Result<Vec<(String, Rule)>, anyhow::Error> {
|
||||
db.iter()
|
||||
rules_tree(db)
|
||||
.iter()
|
||||
.map(|res| {
|
||||
let (id, rule) = res?;
|
||||
|
||||
let id = String::from_utf8_lossy(&id).to_string();
|
||||
tide::log::debug!("id: {}", id);
|
||||
tide::log::debug!("rule str: {}", String::from_utf8_lossy(&rule));
|
||||
let rule: Rule = serde_json::from_slice(&rule)?;
|
||||
tide::log::debug!("rule: {:?}", rule);
|
||||
|
||||
Ok((id, rule)) as Result<(String, Rule), anyhow::Error>
|
||||
})
|
||||
.collect::<Result<Vec<_>, anyhow::Error>>()
|
||||
}
|
||||
|
||||
pub(crate) fn delete(db: &Db, rule_id: String) -> Result<Rule, anyhow::Error> {
|
||||
let rule = db
|
||||
pub(crate) async fn delete(db: &Db, rule_id: String) -> Result<Rule, anyhow::Error> {
|
||||
let tree = rules_tree(db);
|
||||
|
||||
let rule = tree
|
||||
.remove(rule_id.as_bytes())?
|
||||
.ok_or(anyhow::anyhow!("No rule with id {}", rule_id))?;
|
||||
|
||||
tree.flush_async().await?;
|
||||
|
||||
let rule: Rule = serde_json::from_slice(&rule)?;
|
||||
|
||||
Ok(rule)
|
||||
|
@ -89,19 +108,28 @@ pub(crate) async fn unset(interfaces: &Interfaces, rule: Rule) -> Result<(), any
|
|||
dest_port,
|
||||
)
|
||||
.await?;
|
||||
iptables::delete_forward_postrouting(interfaces.external.ip, rule.port, dest_ip)
|
||||
.await?;
|
||||
iptables::delete_forward_postrouting(
|
||||
rule.proto,
|
||||
interfaces.external.ip,
|
||||
rule.port,
|
||||
dest_ip,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn save(db: &Db, rule: &Rule) -> Result<(), anyhow::Error> {
|
||||
pub(crate) async fn save(db: &Db, rule: &Rule) -> Result<(), anyhow::Error> {
|
||||
let tree = rules_tree(db);
|
||||
|
||||
let s = serde_json::to_string(rule)?;
|
||||
let id = db.generate_id()?;
|
||||
|
||||
db.insert(rule_id(id).as_bytes(), s.as_bytes())?;
|
||||
tree.insert(rule_id(id).as_bytes(), s.as_bytes())?;
|
||||
|
||||
tree.flush_async().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -136,7 +164,8 @@ pub(crate) async fn apply(interfaces: &Interfaces, rule: Rule) -> Result<(), any
|
|||
dest_port,
|
||||
)
|
||||
.await?;
|
||||
iptables::forward_postrouting(interfaces.external.ip, rule.port, dest_ip).await?;
|
||||
iptables::forward_postrouting(rule.proto, interfaces.external.ip, rule.port, dest_ip)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,3 +175,30 @@ pub(crate) async fn apply(interfaces: &Interfaces, rule: Rule) -> Result<(), any
|
|||
fn rule_id(id: u64) -> String {
|
||||
format!("rule-{}", id)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
iptables::Proto,
|
||||
rules::{Rule, RuleKind},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn can_serialize() {
|
||||
let rule = Rule {
|
||||
proto: Proto::Tcp,
|
||||
port: 53,
|
||||
kind: RuleKind::Forward {
|
||||
dest_ip: "192.168.6.84".parse().unwrap(),
|
||||
dest_port: 53,
|
||||
},
|
||||
};
|
||||
|
||||
let s = serde_qs::to_string(&rule).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
s,
|
||||
"proto=Tcp&port=53&kind[type]=Forward&kind[dest_ip]=192.168.6.84&kind[dest_port]=53"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
@use crate::rules::Rule;
|
||||
@use crate::{
|
||||
rules::Rule,
|
||||
templates::statics::index_css,
|
||||
};
|
||||
|
||||
@(rules: &[(String, Rule)])
|
||||
|
||||
|
@ -7,55 +10,93 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Rules</title>
|
||||
<link rel="stylesheet" href="/static/@index_css.name" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<thead>
|
||||
<th>Kind</th>
|
||||
<th>Protocol</th>
|
||||
<th>Port</th>
|
||||
<th>Destination</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
</tbody>
|
||||
@for (id, rule) in rules {
|
||||
@if let Some((dest_ip, dest_port)) = rule.as_forward() {
|
||||
<td>Forward</td>
|
||||
<td>@rule.proto</td>
|
||||
<td>@rule.port</td>
|
||||
<td>@dest_ip</td>
|
||||
<td>@dest_port</td>
|
||||
} else {
|
||||
<td>Accept</td>
|
||||
<td>@rule.proto</td>
|
||||
<td>@rule.port</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
}
|
||||
<td><a href="/rules/@id">Delete</a></td>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<form method="POST" action="/rules">
|
||||
<select name="proto">
|
||||
<option value="Tcp">TCP</option>
|
||||
<option value="Udp">UDP</option>
|
||||
</select>
|
||||
<label for="ext_port">
|
||||
<h4>External Port</h4>
|
||||
<input name="ext_port" type="integer" />
|
||||
</label>
|
||||
<input name="kind[type]" value="Forward" type="hidden" />
|
||||
<label for="kind[dest_port]">
|
||||
<h4>Internal Port</h4>
|
||||
<input name="kind[dest_port]" type="integer" />
|
||||
</label>
|
||||
<label for="kind[dest_ip]">
|
||||
<h4>IP Address</h4>
|
||||
<input name="kind[dest_ip]" type="text" />
|
||||
</label>
|
||||
<button type="submit">Forward!</button>
|
||||
</form>
|
||||
<section>
|
||||
<h1>Rules</h1>
|
||||
</section>
|
||||
<section>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<th>Kind</th>
|
||||
<th>Protocol</th>
|
||||
<th class="port">Port</th>
|
||||
<th>Destination IP</th>
|
||||
<th class="port">Destination Port</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
</tbody>
|
||||
@for (id, rule) in rules {
|
||||
<tr>
|
||||
@if let Some((dest_ip, dest_port)) = rule.as_forward() {
|
||||
<td>Forward</td>
|
||||
<td>@rule.proto</td>
|
||||
<td class="port">@rule.port</td>
|
||||
<td>@dest_ip</td>
|
||||
<td class="port">@dest_port</td>
|
||||
} else {
|
||||
<td>Accept</td>
|
||||
<td>@rule.proto</td>
|
||||
<td class="port">@rule.port</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
}
|
||||
<td class="delete"><a href="/rules/@id">Delete</a></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h4>Forward Port</h4>
|
||||
<form method="POST" action="/rules">
|
||||
<div class="form-body">
|
||||
<select name="proto">
|
||||
<option value="Tcp">TCP</option>
|
||||
<option value="Udp">UDP</option>
|
||||
</select>
|
||||
<label for="port">
|
||||
<h4>External Port</h4>
|
||||
<input name="port" type="number" />
|
||||
</label>
|
||||
<input name="kind[type]" value="Forward" type="hidden" />
|
||||
<label for="kind[dest_port]">
|
||||
<h4>Internal Port</h4>
|
||||
<input name="kind[dest_port]" type="number" />
|
||||
</label>
|
||||
<label for="kind[dest_ip]">
|
||||
<h4>IP Address</h4>
|
||||
<input name="kind[dest_ip]" type="text" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="submit">
|
||||
<button type="submit">Forward!</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<section>
|
||||
<h4>Accept Port</h4>
|
||||
<form method="POST" action="/rules">
|
||||
<div class="form-body">
|
||||
<select name="proto">
|
||||
<option value="Tcp">TCP</option>
|
||||
<option value="Udp">UDP</option>
|
||||
</select>
|
||||
<label for="port">
|
||||
<h4>External Port</h4>
|
||||
<input name="port" type="number" />
|
||||
</label>
|
||||
<input name="kind[type]" value="Accept" type="hidden" />
|
||||
</div>
|
||||
<div class="submit">
|
||||
<button type="submit">Accept!</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in a new issue