mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Create initial CONNECT proxy
This commit is contained in:
parent
cef2194427
commit
a60c68074a
5 changed files with 3875 additions and 0 deletions
59
.github/workflows/create-artifact.yml
vendored
59
.github/workflows/create-artifact.yml
vendored
|
|
@ -80,3 +80,62 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: safe-chain-${{ matrix.os }}-${{ matrix.arch }}
|
name: safe-chain-${{ matrix.os }}-${{ matrix.arch }}
|
||||||
path: dist/*
|
path: dist/*
|
||||||
|
|
||||||
|
create-proxy-binaries:
|
||||||
|
name: Create proxy binary for ${{ matrix.os }}-${{ matrix.arch }}
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: macos
|
||||||
|
arch: x64
|
||||||
|
runner: macos-15-intel
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
extension: ""
|
||||||
|
- os: macos
|
||||||
|
arch: arm64
|
||||||
|
runner: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
extension: ""
|
||||||
|
- os: linux
|
||||||
|
arch: x64
|
||||||
|
runner: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
extension: ""
|
||||||
|
- os: linux
|
||||||
|
arch: arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
target: aarch64-unknown-linux-gnu
|
||||||
|
extension: ""
|
||||||
|
- os: win
|
||||||
|
arch: x64
|
||||||
|
runner: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
extension: ".exe"
|
||||||
|
- os: win
|
||||||
|
arch: arm64
|
||||||
|
runner: windows-11-arm
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
|
extension: ".exe"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Build proxy
|
||||||
|
working-directory: proxy
|
||||||
|
run: cargo build --release --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Upload proxy artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: proxy-${{ matrix.os }}-${{ matrix.arch }}
|
||||||
|
path: proxy/target/${{ matrix.target }}/release/proxy${{ matrix.extension }}
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -151,3 +151,7 @@ dist/
|
||||||
|
|
||||||
# Jetbrains IDEs
|
# Jetbrains IDEs
|
||||||
.idea/**
|
.idea/**
|
||||||
|
|
||||||
|
|
||||||
|
# Rust
|
||||||
|
target
|
||||||
3681
proxy/Cargo.lock
generated
Normal file
3681
proxy/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
8
proxy/Cargo.toml
Normal file
8
proxy/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "proxy"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rama = { git = "https://github.com/plabayo/rama", features = ["http-full", "dns", "boring"] }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
123
proxy/src/main.rs
Normal file
123
proxy/src/main.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
use std::{convert::Infallible, time::Duration};
|
||||||
|
|
||||||
|
use rama::{
|
||||||
|
Layer, Service,
|
||||||
|
extensions::ExtensionsMut,
|
||||||
|
http::{
|
||||||
|
Request, Response, StatusCode,
|
||||||
|
client::EasyHttpWebClient,
|
||||||
|
layer::{
|
||||||
|
remove_header::{RemoveRequestHeaderLayer, RemoveResponseHeaderLayer},
|
||||||
|
trace::TraceLayer,
|
||||||
|
upgrade::UpgradeLayer,
|
||||||
|
},
|
||||||
|
matcher::MethodMatcher,
|
||||||
|
server::HttpServer,
|
||||||
|
service::web::response::IntoResponse,
|
||||||
|
},
|
||||||
|
layer::ConsumeErrLayer,
|
||||||
|
net::{http::RequestContext, proxy::ProxyTarget, stream::layer::http::BodyLimitLayer},
|
||||||
|
rt::Executor,
|
||||||
|
service::service_fn,
|
||||||
|
tcp::{client::service::Forwarder, server::TcpListener},
|
||||||
|
telemetry::tracing::{
|
||||||
|
self,
|
||||||
|
metadata::LevelFilter,
|
||||||
|
subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
setup_tracing();
|
||||||
|
run_server().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_tracing() {
|
||||||
|
tracing::subscriber::registry()
|
||||||
|
.with(fmt::layer())
|
||||||
|
.with(
|
||||||
|
EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::INFO.into())
|
||||||
|
.from_env_lossy(),
|
||||||
|
)
|
||||||
|
.init();
|
||||||
|
tracing::info!("Tracing is set up");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_server() {
|
||||||
|
let graceful = rama::graceful::Shutdown::default();
|
||||||
|
|
||||||
|
graceful.spawn_task_fn(server_task);
|
||||||
|
|
||||||
|
graceful
|
||||||
|
.shutdown_with_limit(Duration::from_secs(30))
|
||||||
|
.await
|
||||||
|
.expect("graceful shutdown");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn server_task(guard: rama::graceful::ShutdownGuard) {
|
||||||
|
let tcp_service = TcpListener::build()
|
||||||
|
.bind("127.0.0.1:3128")
|
||||||
|
.await
|
||||||
|
.expect("bind tcp proxy to 127.0.0.1:3128");
|
||||||
|
let exec = Executor::graceful(guard.clone());
|
||||||
|
|
||||||
|
let http_service = HttpServer::auto(exec).service(
|
||||||
|
(
|
||||||
|
TraceLayer::new_for_http(),
|
||||||
|
ConsumeErrLayer::default(),
|
||||||
|
UpgradeLayer::new(
|
||||||
|
MethodMatcher::CONNECT,
|
||||||
|
service_fn(http_connect_accept),
|
||||||
|
ConsumeErrLayer::default().into_layer(Forwarder::ctx()),
|
||||||
|
),
|
||||||
|
RemoveResponseHeaderLayer::hop_by_hop(),
|
||||||
|
RemoveRequestHeaderLayer::hop_by_hop(),
|
||||||
|
)
|
||||||
|
.into_layer(service_fn(http_plain_proxy)),
|
||||||
|
);
|
||||||
|
|
||||||
|
tcp_service
|
||||||
|
.serve_graceful(
|
||||||
|
guard,
|
||||||
|
(
|
||||||
|
// protect the http proxy from too large bodies, both from request and response end
|
||||||
|
BodyLimitLayer::symmetric(500 * 1024 * 1024),
|
||||||
|
)
|
||||||
|
.into_layer(http_service),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn http_connect_accept(mut req: Request) -> Result<(Response, Request), Response> {
|
||||||
|
match RequestContext::try_from(&req).map(|ctx| ctx.host_with_port()) {
|
||||||
|
Ok(authority) => {
|
||||||
|
tracing::info!(
|
||||||
|
server.address = %authority.host,
|
||||||
|
server.port = authority.port,
|
||||||
|
"accept CONNECT",
|
||||||
|
);
|
||||||
|
req.extensions_mut().insert(ProxyTarget(authority));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!("error extracting authority: {err:?}");
|
||||||
|
return Err(StatusCode::BAD_REQUEST.into_response());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok((StatusCode::OK.into_response(), req));
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn http_plain_proxy(req: Request) -> Result<Response, Infallible> {
|
||||||
|
let client = EasyHttpWebClient::default();
|
||||||
|
|
||||||
|
return match client.serve(req).await {
|
||||||
|
Ok(resp) => Ok(resp),
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!("Error forwarding request: {err:?}");
|
||||||
|
let resp = StatusCode::BAD_GATEWAY.into_response();
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue