a
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
asonix 2023-01-09 16:27:10 -06:00
commit 1710719573
7 changed files with 1998 additions and 0 deletions

421
.drone.yml Normal file
View 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
View file

@ -0,0 +1 @@
/target

1373
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

19
Cargo.toml Normal file
View 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
View 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"]

View 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
View 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")
}
}