feat: update ipset m
This commit is contained in:
81
Cargo.lock
generated
81
Cargo.lock
generated
@@ -76,17 +76,6 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-trait"
|
|
||||||
version = "0.1.88"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -95,13 +84,13 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.7.9"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
|
||||||
"axum-core",
|
"axum-core",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"form_urlencoded",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
@@ -129,13 +118,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.4.5"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
|
checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-core",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
@@ -181,6 +169,46 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -296,6 +324,12 @@ version = "0.31.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@@ -393,6 +427,7 @@ name = "ipset-management"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"rust_util",
|
"rust_util",
|
||||||
@@ -477,9 +512,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
version = "0.7.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
@@ -778,6 +813,12 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.104"
|
version = "2.0.104"
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.7"
|
axum = "0.8"
|
||||||
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rust_util = "0.6.48"
|
rust_util = "0.6"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tokio = { version = "1.37", features = ["full"] }
|
tokio = { version = "1.37", features = ["full"] }
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
# ipset-management
|
# ipset-management
|
||||||
|
|
||||||
Linux ipset management
|
Linux ipset management
|
||||||
|
|
||||||
|
|
||||||
|
```shell
|
||||||
|
xh GET 0:2688/ipset x-ssrf-token:token
|
||||||
|
```
|
||||||
|
|||||||
18
src/ipset.rs
18
src/ipset.rs
@@ -26,13 +26,7 @@ impl IpSet {
|
|||||||
|
|
||||||
impl IpSet {
|
impl IpSet {
|
||||||
pub fn list(&self) -> XResult<Vec<String>> {
|
pub fn list(&self) -> XResult<Vec<String>> {
|
||||||
let (stdout, _stderr) = execute_ipset(&["list", &self.name])?;
|
list_ipset_ips(&self.name)
|
||||||
let ipset_list_ips = opt_result!(
|
|
||||||
String::from_utf8(stdout.clone()),
|
|
||||||
"Parse output: {} failed: {}",
|
|
||||||
String::from_utf8_lossy(&stdout)
|
|
||||||
);
|
|
||||||
Ok(parse_ipset_list_ips(&ipset_list_ips))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&self, ip: &str) -> XResult<()> {
|
pub fn add(&self, ip: &str) -> XResult<()> {
|
||||||
@@ -54,7 +48,15 @@ fn list_names() -> XResult<Vec<String>> {
|
|||||||
Ok(parse_ipset_list(&ipset_list_names))
|
Ok(parse_ipset_list(&ipset_list_names))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_ipset(ipset_name: &str) {}
|
fn list_ipset_ips(ipset_name: &str) -> XResult<Vec<String>> {
|
||||||
|
let (stdout, _stderr) = execute_ipset(&["list", ipset_name])?;
|
||||||
|
let ipset_list_ips = opt_result!(
|
||||||
|
String::from_utf8(stdout.clone()),
|
||||||
|
"Parse output: {} failed: {}",
|
||||||
|
String::from_utf8_lossy(&stdout)
|
||||||
|
);
|
||||||
|
Ok(parse_ipset_list_ips(&ipset_list_ips))
|
||||||
|
}
|
||||||
|
|
||||||
fn add_ipset(ipset_name: &str, ip: &str) -> XResult<()> {
|
fn add_ipset(ipset_name: &str, ip: &str) -> XResult<()> {
|
||||||
if let Err(err) = execute_ipset(&["add", ipset_name, ip]) {
|
if let Err(err) = execute_ipset(&["add", ipset_name, ip]) {
|
||||||
|
|||||||
140
src/main.rs
140
src/main.rs
@@ -1,5 +1,141 @@
|
|||||||
|
use crate::ipset::IpSet;
|
||||||
|
use axum::extract::Path;
|
||||||
|
use axum::http::{HeaderMap, HeaderValue, StatusCode};
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
use axum::routing::{get, post};
|
||||||
|
use axum::{Form, Json, Router};
|
||||||
|
use clap::Parser;
|
||||||
|
use rust_util::XResult;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::json;
|
||||||
|
use std::{env, process};
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
const SSRF_TOKEN: &str = "SSRF_TOKEN";
|
||||||
|
|
||||||
mod ipset;
|
mod ipset;
|
||||||
|
|
||||||
fn main() {
|
#[derive(Debug, Parser)]
|
||||||
println!("Hello, world!");
|
#[command(name = "ipset-management")]
|
||||||
|
#[command(about = "A ipset management tool", long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// SSRF token
|
||||||
|
#[arg(long, required = true)]
|
||||||
|
ssrf: String,
|
||||||
|
|
||||||
|
/// Port
|
||||||
|
#[arg(long)]
|
||||||
|
port: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
if let Err(err) = inner_main().await {
|
||||||
|
eprintln!("Error: {}", err);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn inner_main() -> XResult<()> {
|
||||||
|
let args = Args::parse();
|
||||||
|
env::set_var(SSRF_TOKEN, args.ssrf);
|
||||||
|
|
||||||
|
let app = Router::new() // --
|
||||||
|
.route("/", get(root)) // --
|
||||||
|
.route("/ipset", get(get_ipset)) // --
|
||||||
|
.route("/ipset/{ipset}/ips", get(get_ipset_ips)) // --
|
||||||
|
.route("/ipset/{ipset}/ips/{ip}", post(delete_ipset_ips)) // --
|
||||||
|
.route("/ipset/{ipset}/ips", post(post_ipset_ips));
|
||||||
|
|
||||||
|
let listen_addr = format!("127.0.0.1:{}", args.port.unwrap_or(2688));
|
||||||
|
let listener = TcpListener::bind(&listen_addr).await.unwrap();
|
||||||
|
axum::serve(listener, app).await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn root() -> String {
|
||||||
|
format!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct IpsetParams {
|
||||||
|
ipset: String,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct IpsetIpParams {
|
||||||
|
ipset: String,
|
||||||
|
ip: String,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
// #[allow(dead_code)]
|
||||||
|
struct PostIpsetIpsRequest {
|
||||||
|
ip: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_value_or_bad_request {
|
||||||
|
($ex: expr) => {
|
||||||
|
match $ex {
|
||||||
|
Ok(o) => o,
|
||||||
|
Err(e) => {
|
||||||
|
return (StatusCode::BAD_REQUEST, Json(json!({"message": format!("error: {}", e)})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! check_header_ssrf_token {
|
||||||
|
($header_map: expr) => {
|
||||||
|
match $header_map.get("x-ssrf-token") {
|
||||||
|
None => {
|
||||||
|
return (
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Json(json!({"message": format!("SSRF token required")})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Some(ssrf_token) => {
|
||||||
|
if ssrf_token.to_str().unwrap_or("")
|
||||||
|
!= env::var(SSRF_TOKEN).unwrap_or("default-ssrf-token".to_string())
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Json(json!({"message": format!("invalid SSRF token: {:?}", ssrf_token)})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /ipset
|
||||||
|
async fn get_ipset(header_map: HeaderMap<HeaderValue>) -> impl IntoResponse {
|
||||||
|
check_header_ssrf_token!(header_map);
|
||||||
|
let names = get_value_or_bad_request!(IpSet::list_names());
|
||||||
|
(StatusCode::OK, Json(json!({"names": names})))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /ipset/{}/ips
|
||||||
|
async fn get_ipset_ips(
|
||||||
|
header_map: HeaderMap<HeaderValue>,
|
||||||
|
Path(params): Path<IpsetParams>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
check_header_ssrf_token!(header_map);
|
||||||
|
let ipset = get_value_or_bad_request!(IpSet::new(¶ms.ipset));
|
||||||
|
let ips = get_value_or_bad_request!(ipset.list());
|
||||||
|
(StatusCode::OK, Json(json!({"ips": ips})))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /ipset/{}/ips/{}
|
||||||
|
async fn delete_ipset_ips(Path(params): Path<IpsetIpParams>) -> impl IntoResponse {
|
||||||
|
let ipset = get_value_or_bad_request!(IpSet::new(¶ms.ipset));
|
||||||
|
get_value_or_bad_request!(ipset.del(¶ms.ip));
|
||||||
|
(StatusCode::OK, Json(json!({})))
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /ipset/{}/ips with parameter: ip=?
|
||||||
|
async fn post_ipset_ips(
|
||||||
|
Path(params): Path<IpsetParams>,
|
||||||
|
Form(request): Form<PostIpsetIpsRequest>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let ipset = get_value_or_bad_request!(IpSet::new(¶ms.ipset));
|
||||||
|
get_value_or_bad_request!(ipset.add(&request.ip));
|
||||||
|
(StatusCode::OK, Json(json!({})))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user