This commit is contained in:
commit
1710719573
421
.drone.yml
Normal file
421
.drone.yml
Normal file
|
@ -0,0 +1,421 @@
|
|||
kind: pipeline
|
||||
type: docker
|
||||
name: clippy
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: clippy
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- rustup component add clippy
|
||||
- cargo clippy -- -D warnings
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: tests
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: tests
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- cargo test
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: check-amd64
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: check
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- cargo check --target=$TARGET
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-amd64
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: build
|
||||
image: asonix/rust-builder:latest-linux-amd64
|
||||
pull: always
|
||||
commands:
|
||||
- cargo build --target=$TARGET --release
|
||||
- $TOOL-strip target/$TARGET/release/deny-proxy
|
||||
- cp target/$TARGET/release/deny-proxy .
|
||||
- cp deny-proxy deny-proxy-linux-amd64
|
||||
|
||||
- name: push
|
||||
image: plugins/docker:20
|
||||
settings:
|
||||
username: asonix
|
||||
password:
|
||||
from_secret: dockerhub_token
|
||||
repo: asonix/deny-proxy
|
||||
dockerfile: docker/drone/Dockerfile
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-amd64
|
||||
build_args:
|
||||
- REPO_ARCH=amd64
|
||||
|
||||
- name: publish
|
||||
image: plugins/gitea-release:1
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
base_url: https://git.asonix.dog
|
||||
files:
|
||||
- deny-proxy-linux-amd64
|
||||
|
||||
depends_on:
|
||||
- clippy
|
||||
- tests
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: check-arm64v8
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: check
|
||||
image: asonix/rust-builder:latest-linux-arm64v8
|
||||
pull: always
|
||||
commands:
|
||||
- cargo check --target=$TARGET
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-arm64v8
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: build
|
||||
image: asonix/rust-builder:latest-linux-arm64v8
|
||||
pull: always
|
||||
commands:
|
||||
- cargo build --target=$TARGET --release
|
||||
- $TOOL-strip target/$TARGET/release/deny-proxy
|
||||
- cp target/$TARGET/release/deny-proxy .
|
||||
- cp deny-proxy deny-proxy-linux-arm64v8
|
||||
|
||||
- name: push
|
||||
image: plugins/docker:20
|
||||
settings:
|
||||
username: asonix
|
||||
password:
|
||||
from_secret: dockerhub_token
|
||||
repo: asonix/deny-proxy
|
||||
dockerfile: docker/drone/Dockerfile
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-arm64v8
|
||||
build_args:
|
||||
- REPO_ARCH=arm64v8
|
||||
|
||||
- name: publish
|
||||
image: plugins/gitea-release:1
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
base_url: https://git.asonix.dog
|
||||
files:
|
||||
- deny-proxy-linux-arm64v8
|
||||
|
||||
depends_on:
|
||||
- clippy
|
||||
- tests
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: check-arm32v7
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: check
|
||||
image: asonix/rust-builder:latest-linux-arm32v7
|
||||
pull: always
|
||||
commands:
|
||||
- cargo check --target=$TARGET
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-arm32v7
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: build
|
||||
image: asonix/rust-builder:latest-linux-arm32v7
|
||||
pull: always
|
||||
commands:
|
||||
- cargo build --target=$TARGET --release
|
||||
- $TOOL-strip target/$TARGET/release/deny-proxy
|
||||
- cp target/$TARGET/release/deny-proxy .
|
||||
- cp deny-proxy deny-proxy-linux-arm32v7
|
||||
|
||||
- name: push
|
||||
image: plugins/docker:20
|
||||
settings:
|
||||
username: asonix
|
||||
password:
|
||||
from_secret: dockerhub_token
|
||||
repo: asonix/deny-proxy
|
||||
dockerfile: docker/drone/Dockerfile
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-arm32v7
|
||||
build_args:
|
||||
- REPO_ARCH=arm32v7
|
||||
|
||||
- name: publish
|
||||
image: plugins/gitea-release:1
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
base_url: https://git.asonix.dog
|
||||
files:
|
||||
- deny-proxy-linux-arm32v7
|
||||
|
||||
depends_on:
|
||||
- clippy
|
||||
- tests
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: manifest
|
||||
|
||||
platform:
|
||||
arch: amd64
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: alpine/git:latest
|
||||
user: root
|
||||
commands:
|
||||
- git clone $DRONE_GIT_HTTP_URL .
|
||||
- git checkout $DRONE_COMMIT
|
||||
- chown -R 991:991 .
|
||||
|
||||
- name: manifest
|
||||
image: plugins/manifest:1
|
||||
settings:
|
||||
username: asonix
|
||||
password:
|
||||
from_secret: dockerhub_token
|
||||
dump: true
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: docker/drone/manifest.tmpl
|
||||
|
||||
|
||||
depends_on:
|
||||
- build-amd64
|
||||
- build-arm64v8
|
||||
- build-arm32v7
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
# ---
|
||||
#
|
||||
# kind: pipeline
|
||||
# type: docker
|
||||
# name: publish-crate
|
||||
#
|
||||
# platform:
|
||||
# arch: amd64
|
||||
#
|
||||
# clone:
|
||||
# disable: true
|
||||
#
|
||||
# steps:
|
||||
# - name: clone
|
||||
# image: alpine/git:latest
|
||||
# user: root
|
||||
# commands:
|
||||
# - git clone $DRONE_GIT_HTTP_URL .
|
||||
# - git checkout $DRONE_COMMIT
|
||||
# - chown -R 991:991 .
|
||||
#
|
||||
# - name: publish
|
||||
# image: asonix/rust-builder:latest-linux-amd64
|
||||
# pull: always
|
||||
# environment:
|
||||
# CRATES_IO_TOKEN:
|
||||
# from_secret: crates_io_token
|
||||
# commands:
|
||||
# - cargo publish --token $CRATES_IO_TOKEN
|
||||
#
|
||||
# depends_on:
|
||||
# - build-amd64
|
||||
# - build-arm64v8
|
||||
# - build-arm32v7
|
||||
#
|
||||
# trigger:
|
||||
# event:
|
||||
# - tag
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
1373
Cargo.lock
generated
Normal file
1373
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "deny-proxy"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "4", default-features = false, features = ["macros"] }
|
||||
actix-web-lab = { version = "0.18.9", default-features = false }
|
||||
awc = { version = "3", default-features = false }
|
||||
tracing = "0.1"
|
||||
tracing-actix-web = { version = "0.7.1", features = ["emit_event_on_error"] }
|
||||
tracing-log = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = [
|
||||
"ansi",
|
||||
"env-filter",
|
||||
"fmt",
|
||||
] }
|
10
docker/drone/Dockerfile
Normal file
10
docker/drone/Dockerfile
Normal file
|
@ -0,0 +1,10 @@
|
|||
ARG REPO_ARCH
|
||||
|
||||
FROM asonix/rust-runner:latest-linux-$REPO_ARCH
|
||||
|
||||
COPY deny-proxy /usr/local/bin/deny-proxy
|
||||
|
||||
USER app
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
CMD ["/usr/local/bin/deny-proxy"]
|
25
docker/drone/manifest.tmpl
Normal file
25
docker/drone/manifest.tmpl
Normal file
|
@ -0,0 +1,25 @@
|
|||
image: asonix/deny-proxy:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: asonix/deny-proxy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: asonix/deny-proxy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64v8
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
-
|
||||
image: asonix/deny-proxy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm32v7
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
149
src/main.rs
Normal file
149
src/main.rs
Normal file
|
@ -0,0 +1,149 @@
|
|||
use actix_web::{
|
||||
body::{BodyStream, MessageBody},
|
||||
dev::{ServiceRequest, ServiceResponse},
|
||||
error::{ErrorBadRequest, ErrorInternalServerError},
|
||||
http::{header::USER_AGENT, uri::Parts, Uri},
|
||||
web::{to, Data, Payload, PayloadConfig},
|
||||
App, HttpRequest, HttpResponse, HttpServer,
|
||||
};
|
||||
use actix_web_lab::middleware::{from_fn, Next};
|
||||
use awc::Client;
|
||||
use std::{net::SocketAddr, rc::Rc, time::Duration};
|
||||
use tracing_actix_web::TracingLogger;
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::{
|
||||
filter::Targets, fmt::format::FmtSpan, layer::SubscriberExt, Layer, Registry,
|
||||
};
|
||||
|
||||
struct State {
|
||||
upstream: Uri,
|
||||
client: Client,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MissingUserAgent;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InvalidUserAgent;
|
||||
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> Result<(), Error> {
|
||||
LogTracer::init()?;
|
||||
|
||||
let targets: Targets = std::env::var("RUST_LOG")
|
||||
.unwrap_or_else(|_| "info".into())
|
||||
.parse()?;
|
||||
|
||||
let format_layer = tracing_subscriber::fmt::layer()
|
||||
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
||||
.with_filter(targets.clone());
|
||||
|
||||
let subscriber = Registry::default().with(format_layer);
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
|
||||
let upstream: Uri = std::env::var("PROXY_UPSTREAM")?.parse()?;
|
||||
|
||||
let blocked: Vec<String> = std::env::var("PROXY_BLOCKS")?
|
||||
.split(',')
|
||||
.map(String::from)
|
||||
.collect();
|
||||
|
||||
let addr: SocketAddr = std::env::var("PROXY_ADDR")?.parse()?;
|
||||
|
||||
HttpServer::new(move || {
|
||||
let state = State {
|
||||
upstream: upstream.clone(),
|
||||
client: Client::builder().timeout(Duration::from_secs(30)).finish(),
|
||||
};
|
||||
|
||||
let blocked = Rc::new(blocked.clone());
|
||||
|
||||
App::new()
|
||||
.app_data(Data::new(state))
|
||||
.app_data(PayloadConfig::new(1024 * 1024 * 200))
|
||||
.wrap(from_fn(move |req, next| {
|
||||
block_user_agents(req, next, Rc::clone(&blocked))
|
||||
}))
|
||||
.wrap(TracingLogger::default())
|
||||
.default_service(to(proxy))
|
||||
})
|
||||
.bind(addr)?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn block_user_agents(
|
||||
req: ServiceRequest,
|
||||
next: Next<impl MessageBody>,
|
||||
blocked: Rc<Vec<String>>,
|
||||
) -> Result<ServiceResponse<impl MessageBody>, actix_web::Error> {
|
||||
let Some(user_agent) = req.headers().get(USER_AGENT) else {
|
||||
return Err(ErrorBadRequest(MissingUserAgent).into());
|
||||
};
|
||||
|
||||
let user_agent = user_agent.to_str().map_err(ErrorBadRequest)?.to_lowercase();
|
||||
|
||||
for part in blocked.iter() {
|
||||
if user_agent.contains(part.as_str()) {
|
||||
return Err(ErrorBadRequest(InvalidUserAgent).into());
|
||||
}
|
||||
}
|
||||
|
||||
next.call(req).await
|
||||
}
|
||||
|
||||
async fn proxy(
|
||||
inbound: HttpRequest,
|
||||
body: Option<Payload>,
|
||||
state: Data<State>,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
let mut upstream = Parts::default();
|
||||
upstream.scheme = state.upstream.scheme().cloned();
|
||||
upstream.authority = state.upstream.authority().cloned();
|
||||
upstream.path_and_query = inbound.uri().path_and_query().cloned();
|
||||
|
||||
let mut req = state.client.request_from(upstream, inbound.head());
|
||||
|
||||
if let Some(peer_addr) = inbound.head().peer_addr {
|
||||
req = req.append_header(("x-forwarded-for", peer_addr.to_string()));
|
||||
}
|
||||
|
||||
req = req.no_decompress();
|
||||
|
||||
let response = if let Some(payload) = body {
|
||||
req.send_stream(payload)
|
||||
.await
|
||||
.map_err(ErrorInternalServerError)?
|
||||
} else {
|
||||
req.send().await.map_err(ErrorInternalServerError)?
|
||||
};
|
||||
|
||||
let mut downstream = HttpResponse::build(response.status());
|
||||
|
||||
for (name, value) in response
|
||||
.headers()
|
||||
.iter()
|
||||
.filter(|(h, _)| *h != "connection")
|
||||
{
|
||||
downstream.insert_header((name.clone(), value.clone()));
|
||||
}
|
||||
|
||||
Ok(downstream.body(BodyStream::new(response)))
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MissingUserAgent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "No user-agent header provided")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InvalidUserAgent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Invalid user-agent header provided")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue