mirror of
https://github.com/mikedilger/chorus.git
synced 2026-01-03 06:15:33 +00:00
Fix proxy setups (ip blocking, real IP); needs new chorus_is_behind_a_proxy config
This commit is contained in:
parent
37b1b3a0f3
commit
a76be24d68
@ -34,6 +34,15 @@ port = 443
|
||||
hostname = "localhost"
|
||||
|
||||
|
||||
# If chorus is behind a proxy like nginx, set this to true. In this case chorus will look for and
|
||||
# trust the `X-Real-Ip` HTTP request header to get the real IP of the client. This header MUST exist
|
||||
# or the connection will not be served.
|
||||
#
|
||||
# Default is false.
|
||||
#
|
||||
chorus_is_behind_a_proxy = false
|
||||
|
||||
|
||||
# If true, chorus will handle TLS, running over HTTPS. If false, chorus run over HTTP.
|
||||
#
|
||||
# If you are proxying via nginx, normally you will set this to false and allow nginx to handle TLS.
|
||||
|
||||
@ -36,6 +36,14 @@ your relay host name.
|
||||
|
||||
Default is localhost
|
||||
|
||||
### chorus_is_behind_a_proxy
|
||||
|
||||
If chorus is behind a proxy like nginx, set this to true. In this case chorus will look for and
|
||||
trust the `X-Real-Ip` HTTP request header to get the real IP of the client. This header MUST exist
|
||||
or the connection will not be served.
|
||||
|
||||
Default is false.
|
||||
|
||||
### use_tls
|
||||
|
||||
If true, chorus will handle TLS, running over HTTPS. If false, chorus run over HTTP.
|
||||
@ -58,7 +66,7 @@ systemd service copies letsencrypt TLS certificates into this position on start.
|
||||
|
||||
### key_pem_path
|
||||
|
||||
This is the path to yoru TLS private key file.
|
||||
This is the path to your TLS private key file.
|
||||
|
||||
If `use_tls` is false, this value is irrelevant.
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ data_directory = "./sample"
|
||||
ip_address = "127.0.0.1"
|
||||
port = 8080
|
||||
hostname = "localhost"
|
||||
chorus_is_behind_a_proxy = false
|
||||
use_tls = false
|
||||
certchain_pem_path = "tls/fullchain.pem"
|
||||
key_pem_path = "tls/privkey.pem"
|
||||
|
||||
@ -93,8 +93,10 @@ async fn main() -> Result<(), Error> {
|
||||
(tcp_stream, hashed_peer)
|
||||
};
|
||||
|
||||
// Possibly IP block
|
||||
if GLOBALS.config.read().enable_ip_blocking {
|
||||
// Possibly IP block early
|
||||
if ! GLOBALS.config.read().chorus_is_behind_a_proxy
|
||||
&& GLOBALS.config.read().enable_ip_blocking
|
||||
{
|
||||
let ip_data = chorus::get_ip_data(GLOBALS.store.get().unwrap(), hashed_peer.ip())?;
|
||||
if ip_data.is_banned() {
|
||||
log::debug!(target: "Client",
|
||||
|
||||
@ -12,6 +12,7 @@ pub struct FriendlyConfig {
|
||||
pub ip_address: String,
|
||||
pub port: u16,
|
||||
pub hostname: String,
|
||||
pub chorus_is_behind_a_proxy: bool,
|
||||
pub use_tls: bool,
|
||||
pub certchain_pem_path: String,
|
||||
pub key_pem_path: String,
|
||||
@ -45,6 +46,7 @@ impl Default for FriendlyConfig {
|
||||
ip_address: "127.0.0.1".to_string(),
|
||||
port: 443,
|
||||
hostname: "localhost".to_string(),
|
||||
chorus_is_behind_a_proxy: false,
|
||||
use_tls: true,
|
||||
certchain_pem_path: "/opt/chorus/etc/tls/fullchain.pem".to_string(),
|
||||
key_pem_path: "/opt/chorus/etc/tls/privkey.pem".to_string(),
|
||||
@ -80,6 +82,7 @@ impl FriendlyConfig {
|
||||
ip_address,
|
||||
port,
|
||||
hostname,
|
||||
chorus_is_behind_a_proxy,
|
||||
use_tls,
|
||||
certchain_pem_path,
|
||||
key_pem_path,
|
||||
@ -135,6 +138,7 @@ impl FriendlyConfig {
|
||||
ip_address,
|
||||
port,
|
||||
hostname,
|
||||
chorus_is_behind_a_proxy,
|
||||
use_tls,
|
||||
certchain_pem_path,
|
||||
key_pem_path,
|
||||
@ -171,6 +175,7 @@ pub struct Config {
|
||||
pub ip_address: String,
|
||||
pub port: u16,
|
||||
pub hostname: Host,
|
||||
pub chorus_is_behind_a_proxy: bool,
|
||||
pub use_tls: bool,
|
||||
pub certchain_pem_path: String,
|
||||
pub key_pem_path: String,
|
||||
|
||||
22
src/error.rs
22
src/error.rs
@ -31,6 +31,12 @@ pub enum ChorusError {
|
||||
// Bad request
|
||||
BadRequest(&'static str),
|
||||
|
||||
// Bad X-Real-Ip header
|
||||
BadRealIpHeader(String),
|
||||
|
||||
// Bad X-Real-Ip header characters
|
||||
BadRealIpHeaderCharacters,
|
||||
|
||||
// Event is banned
|
||||
BannedEvent,
|
||||
|
||||
@ -40,6 +46,9 @@ pub enum ChorusError {
|
||||
// Base64 Decode Error
|
||||
Base64Decode(base64::DecodeError),
|
||||
|
||||
// Blocked IP
|
||||
BlockedIp,
|
||||
|
||||
// Channel Recv
|
||||
ChannelRecv(tokio::sync::broadcast::error::RecvError),
|
||||
|
||||
@ -103,6 +112,9 @@ pub enum ChorusError {
|
||||
// Pocket Types Error
|
||||
PocketType(pocket_types::Error),
|
||||
|
||||
// X-Real-Ip header is missing
|
||||
RealIpHeaderMissing,
|
||||
|
||||
// Restricted
|
||||
Restricted,
|
||||
|
||||
@ -146,9 +158,14 @@ impl std::fmt::Display for ChorusError {
|
||||
ChorusError::AuthFailure(s) => write!(f, "AUTH failure: {s}"),
|
||||
ChorusError::AuthRequired => write!(f, "AUTH required"),
|
||||
ChorusError::BadRequest(s) => write!(f, "Bad Request: {s}"),
|
||||
ChorusError::BadRealIpHeader(s) => write!(f, "Bad X-Real-Ip header: {s}"),
|
||||
ChorusError::BadRealIpHeaderCharacters => {
|
||||
write!(f, "Bad X-Real-Ip header (non utf-8 characters)")
|
||||
}
|
||||
ChorusError::BannedEvent => write!(f, "Event is banned"),
|
||||
ChorusError::BannedUser => write!(f, "User is banned"),
|
||||
ChorusError::Base64Decode(e) => write!(f, "{e}"),
|
||||
ChorusError::BlockedIp => write!(f, "IP is temporarily blocked"),
|
||||
ChorusError::ChannelRecv(e) => write!(f, "{e}"),
|
||||
ChorusError::ChannelSend(e) => write!(f, "{e}"),
|
||||
ChorusError::Config(e) => write!(f, "{e}"),
|
||||
@ -170,6 +187,7 @@ impl std::fmt::Display for ChorusError {
|
||||
ChorusError::PocketDbHeed(e) => write!(f, "{e}"),
|
||||
ChorusError::PocketType(e) => write!(f, "{e}"),
|
||||
ChorusError::ProtectedEvent => write!(f, "Protected event"),
|
||||
ChorusError::RealIpHeaderMissing => write!(f, "X-Real-Ip header is missing"),
|
||||
ChorusError::Restricted => write!(f, "Restricted"),
|
||||
ChorusError::Rustls(e) => write!(f, "{e}"),
|
||||
ChorusError::TimedOut => write!(f, "Timed out"),
|
||||
@ -226,9 +244,12 @@ impl ChorusError {
|
||||
ChorusError::AuthFailure(_) => 0.25,
|
||||
ChorusError::AuthRequired => 0.0,
|
||||
ChorusError::BadRequest(_) => 0.1,
|
||||
ChorusError::BadRealIpHeader(_) => 0.0,
|
||||
ChorusError::BadRealIpHeaderCharacters => 0.0,
|
||||
ChorusError::BannedEvent => 0.1,
|
||||
ChorusError::BannedUser => 0.2,
|
||||
ChorusError::Base64Decode(_) => 0.0,
|
||||
ChorusError::BlockedIp => 0.0,
|
||||
ChorusError::ChannelRecv(_) => 0.0,
|
||||
ChorusError::ChannelSend(_) => 0.0,
|
||||
ChorusError::Config(_) => 0.0,
|
||||
@ -250,6 +271,7 @@ impl ChorusError {
|
||||
ChorusError::PocketDbHeed(_) => 0.0,
|
||||
ChorusError::PocketType(_) => 0.0,
|
||||
ChorusError::ProtectedEvent => 0.35,
|
||||
ChorusError::RealIpHeaderMissing => 0.0,
|
||||
ChorusError::Restricted => 0.1,
|
||||
ChorusError::Rustls(_) => 0.0,
|
||||
ChorusError::TimedOut => 0.1,
|
||||
|
||||
40
src/lib.rs
40
src/lib.rs
@ -85,23 +85,49 @@ impl Service<Request<Incoming>> for ChorusService {
|
||||
// This is called for each HTTP request made by the client
|
||||
// NOTE: it is not called for each websocket message once upgraded.
|
||||
fn call(&self, req: Request<Incoming>) -> Self::Future {
|
||||
let mut peer = self.peer;
|
||||
let mut hashed_peer = self.peer;
|
||||
|
||||
// If chorus is behind a proxy that sets an "X-Real-Ip" header, we use
|
||||
// that ip address instead (otherwise their log file will just say "127.0.0.1"
|
||||
// for every peer)
|
||||
if peer.ip().is_loopback() {
|
||||
let failvalue =
|
||||
|c: ChorusError| -> Self::Future { Box::pin(futures::future::ready(Err(c.into()))) };
|
||||
|
||||
if GLOBALS.config.read().chorus_is_behind_a_proxy {
|
||||
// If chorus is behind a proxy that sets an "X-Real-Ip" header, we use
|
||||
// that ip address instead (otherwise their log file will just give the proxy IP
|
||||
// for every peer)
|
||||
//
|
||||
// This header must be found and be valid for us to proceed
|
||||
if let Some(rip) = req.headers().get("x-real-ip") {
|
||||
if let Ok(ripstr) = rip.to_str() {
|
||||
if let Ok(ipaddr) = ripstr.parse::<IpAddr>() {
|
||||
let hashed_ip = HashedIp::new(ipaddr);
|
||||
peer = HashedPeer::from_parts(hashed_ip, peer.port());
|
||||
hashed_peer = HashedPeer::from_parts(hashed_ip, hashed_peer.port());
|
||||
} else {
|
||||
return failvalue(ChorusError::BadRealIpHeader(ripstr.to_owned()));
|
||||
}
|
||||
} else {
|
||||
return failvalue(ChorusError::BadRealIpHeaderCharacters);
|
||||
}
|
||||
} else {
|
||||
return failvalue(ChorusError::RealIpHeaderMissing);
|
||||
}
|
||||
|
||||
// Possibly IP block late (if behind a proxy)
|
||||
if GLOBALS.config.read().enable_ip_blocking {
|
||||
if let Ok(ip_data) =
|
||||
crate::get_ip_data(GLOBALS.store.get().unwrap(), hashed_peer.ip())
|
||||
{
|
||||
if ip_data.is_banned() {
|
||||
log::debug!(target: "Client",
|
||||
"{}: Blocking reconnection until {}",
|
||||
hashed_peer.ip(),
|
||||
ip_data.ban_until);
|
||||
return failvalue(ChorusError::BlockedIp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box::pin(async move { handle_http_request(peer, req).await })
|
||||
Box::pin(async move { handle_http_request(hashed_peer, req).await })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user