mirror of
https://github.com/mikedilger/chorus.git
synced 2026-05-03 06:51:42 +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"
|
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 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.
|
# 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
|
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
|
### use_tls
|
||||||
|
|
||||||
If true, chorus will handle TLS, running over HTTPS. If false, chorus run over HTTP.
|
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
|
### 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.
|
If `use_tls` is false, this value is irrelevant.
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ data_directory = "./sample"
|
|||||||
ip_address = "127.0.0.1"
|
ip_address = "127.0.0.1"
|
||||||
port = 8080
|
port = 8080
|
||||||
hostname = "localhost"
|
hostname = "localhost"
|
||||||
|
chorus_is_behind_a_proxy = false
|
||||||
use_tls = false
|
use_tls = false
|
||||||
certchain_pem_path = "tls/fullchain.pem"
|
certchain_pem_path = "tls/fullchain.pem"
|
||||||
key_pem_path = "tls/privkey.pem"
|
key_pem_path = "tls/privkey.pem"
|
||||||
|
|||||||
@ -93,8 +93,10 @@ async fn main() -> Result<(), Error> {
|
|||||||
(tcp_stream, hashed_peer)
|
(tcp_stream, hashed_peer)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Possibly IP block
|
// Possibly IP block early
|
||||||
if GLOBALS.config.read().enable_ip_blocking {
|
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())?;
|
let ip_data = chorus::get_ip_data(GLOBALS.store.get().unwrap(), hashed_peer.ip())?;
|
||||||
if ip_data.is_banned() {
|
if ip_data.is_banned() {
|
||||||
log::debug!(target: "Client",
|
log::debug!(target: "Client",
|
||||||
|
|||||||
@ -12,6 +12,7 @@ pub struct FriendlyConfig {
|
|||||||
pub ip_address: String,
|
pub ip_address: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub hostname: String,
|
pub hostname: String,
|
||||||
|
pub chorus_is_behind_a_proxy: bool,
|
||||||
pub use_tls: bool,
|
pub use_tls: bool,
|
||||||
pub certchain_pem_path: String,
|
pub certchain_pem_path: String,
|
||||||
pub key_pem_path: String,
|
pub key_pem_path: String,
|
||||||
@ -45,6 +46,7 @@ impl Default for FriendlyConfig {
|
|||||||
ip_address: "127.0.0.1".to_string(),
|
ip_address: "127.0.0.1".to_string(),
|
||||||
port: 443,
|
port: 443,
|
||||||
hostname: "localhost".to_string(),
|
hostname: "localhost".to_string(),
|
||||||
|
chorus_is_behind_a_proxy: false,
|
||||||
use_tls: true,
|
use_tls: true,
|
||||||
certchain_pem_path: "/opt/chorus/etc/tls/fullchain.pem".to_string(),
|
certchain_pem_path: "/opt/chorus/etc/tls/fullchain.pem".to_string(),
|
||||||
key_pem_path: "/opt/chorus/etc/tls/privkey.pem".to_string(),
|
key_pem_path: "/opt/chorus/etc/tls/privkey.pem".to_string(),
|
||||||
@ -80,6 +82,7 @@ impl FriendlyConfig {
|
|||||||
ip_address,
|
ip_address,
|
||||||
port,
|
port,
|
||||||
hostname,
|
hostname,
|
||||||
|
chorus_is_behind_a_proxy,
|
||||||
use_tls,
|
use_tls,
|
||||||
certchain_pem_path,
|
certchain_pem_path,
|
||||||
key_pem_path,
|
key_pem_path,
|
||||||
@ -135,6 +138,7 @@ impl FriendlyConfig {
|
|||||||
ip_address,
|
ip_address,
|
||||||
port,
|
port,
|
||||||
hostname,
|
hostname,
|
||||||
|
chorus_is_behind_a_proxy,
|
||||||
use_tls,
|
use_tls,
|
||||||
certchain_pem_path,
|
certchain_pem_path,
|
||||||
key_pem_path,
|
key_pem_path,
|
||||||
@ -171,6 +175,7 @@ pub struct Config {
|
|||||||
pub ip_address: String,
|
pub ip_address: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub hostname: Host,
|
pub hostname: Host,
|
||||||
|
pub chorus_is_behind_a_proxy: bool,
|
||||||
pub use_tls: bool,
|
pub use_tls: bool,
|
||||||
pub certchain_pem_path: String,
|
pub certchain_pem_path: String,
|
||||||
pub key_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
|
// Bad request
|
||||||
BadRequest(&'static str),
|
BadRequest(&'static str),
|
||||||
|
|
||||||
|
// Bad X-Real-Ip header
|
||||||
|
BadRealIpHeader(String),
|
||||||
|
|
||||||
|
// Bad X-Real-Ip header characters
|
||||||
|
BadRealIpHeaderCharacters,
|
||||||
|
|
||||||
// Event is banned
|
// Event is banned
|
||||||
BannedEvent,
|
BannedEvent,
|
||||||
|
|
||||||
@ -40,6 +46,9 @@ pub enum ChorusError {
|
|||||||
// Base64 Decode Error
|
// Base64 Decode Error
|
||||||
Base64Decode(base64::DecodeError),
|
Base64Decode(base64::DecodeError),
|
||||||
|
|
||||||
|
// Blocked IP
|
||||||
|
BlockedIp,
|
||||||
|
|
||||||
// Channel Recv
|
// Channel Recv
|
||||||
ChannelRecv(tokio::sync::broadcast::error::RecvError),
|
ChannelRecv(tokio::sync::broadcast::error::RecvError),
|
||||||
|
|
||||||
@ -103,6 +112,9 @@ pub enum ChorusError {
|
|||||||
// Pocket Types Error
|
// Pocket Types Error
|
||||||
PocketType(pocket_types::Error),
|
PocketType(pocket_types::Error),
|
||||||
|
|
||||||
|
// X-Real-Ip header is missing
|
||||||
|
RealIpHeaderMissing,
|
||||||
|
|
||||||
// Restricted
|
// Restricted
|
||||||
Restricted,
|
Restricted,
|
||||||
|
|
||||||
@ -146,9 +158,14 @@ impl std::fmt::Display for ChorusError {
|
|||||||
ChorusError::AuthFailure(s) => write!(f, "AUTH failure: {s}"),
|
ChorusError::AuthFailure(s) => write!(f, "AUTH failure: {s}"),
|
||||||
ChorusError::AuthRequired => write!(f, "AUTH required"),
|
ChorusError::AuthRequired => write!(f, "AUTH required"),
|
||||||
ChorusError::BadRequest(s) => write!(f, "Bad Request: {s}"),
|
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::BannedEvent => write!(f, "Event is banned"),
|
||||||
ChorusError::BannedUser => write!(f, "User is banned"),
|
ChorusError::BannedUser => write!(f, "User is banned"),
|
||||||
ChorusError::Base64Decode(e) => write!(f, "{e}"),
|
ChorusError::Base64Decode(e) => write!(f, "{e}"),
|
||||||
|
ChorusError::BlockedIp => write!(f, "IP is temporarily blocked"),
|
||||||
ChorusError::ChannelRecv(e) => write!(f, "{e}"),
|
ChorusError::ChannelRecv(e) => write!(f, "{e}"),
|
||||||
ChorusError::ChannelSend(e) => write!(f, "{e}"),
|
ChorusError::ChannelSend(e) => write!(f, "{e}"),
|
||||||
ChorusError::Config(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::PocketDbHeed(e) => write!(f, "{e}"),
|
||||||
ChorusError::PocketType(e) => write!(f, "{e}"),
|
ChorusError::PocketType(e) => write!(f, "{e}"),
|
||||||
ChorusError::ProtectedEvent => write!(f, "Protected event"),
|
ChorusError::ProtectedEvent => write!(f, "Protected event"),
|
||||||
|
ChorusError::RealIpHeaderMissing => write!(f, "X-Real-Ip header is missing"),
|
||||||
ChorusError::Restricted => write!(f, "Restricted"),
|
ChorusError::Restricted => write!(f, "Restricted"),
|
||||||
ChorusError::Rustls(e) => write!(f, "{e}"),
|
ChorusError::Rustls(e) => write!(f, "{e}"),
|
||||||
ChorusError::TimedOut => write!(f, "Timed out"),
|
ChorusError::TimedOut => write!(f, "Timed out"),
|
||||||
@ -226,9 +244,12 @@ impl ChorusError {
|
|||||||
ChorusError::AuthFailure(_) => 0.25,
|
ChorusError::AuthFailure(_) => 0.25,
|
||||||
ChorusError::AuthRequired => 0.0,
|
ChorusError::AuthRequired => 0.0,
|
||||||
ChorusError::BadRequest(_) => 0.1,
|
ChorusError::BadRequest(_) => 0.1,
|
||||||
|
ChorusError::BadRealIpHeader(_) => 0.0,
|
||||||
|
ChorusError::BadRealIpHeaderCharacters => 0.0,
|
||||||
ChorusError::BannedEvent => 0.1,
|
ChorusError::BannedEvent => 0.1,
|
||||||
ChorusError::BannedUser => 0.2,
|
ChorusError::BannedUser => 0.2,
|
||||||
ChorusError::Base64Decode(_) => 0.0,
|
ChorusError::Base64Decode(_) => 0.0,
|
||||||
|
ChorusError::BlockedIp => 0.0,
|
||||||
ChorusError::ChannelRecv(_) => 0.0,
|
ChorusError::ChannelRecv(_) => 0.0,
|
||||||
ChorusError::ChannelSend(_) => 0.0,
|
ChorusError::ChannelSend(_) => 0.0,
|
||||||
ChorusError::Config(_) => 0.0,
|
ChorusError::Config(_) => 0.0,
|
||||||
@ -250,6 +271,7 @@ impl ChorusError {
|
|||||||
ChorusError::PocketDbHeed(_) => 0.0,
|
ChorusError::PocketDbHeed(_) => 0.0,
|
||||||
ChorusError::PocketType(_) => 0.0,
|
ChorusError::PocketType(_) => 0.0,
|
||||||
ChorusError::ProtectedEvent => 0.35,
|
ChorusError::ProtectedEvent => 0.35,
|
||||||
|
ChorusError::RealIpHeaderMissing => 0.0,
|
||||||
ChorusError::Restricted => 0.1,
|
ChorusError::Restricted => 0.1,
|
||||||
ChorusError::Rustls(_) => 0.0,
|
ChorusError::Rustls(_) => 0.0,
|
||||||
ChorusError::TimedOut => 0.1,
|
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
|
// This is called for each HTTP request made by the client
|
||||||
// NOTE: it is not called for each websocket message once upgraded.
|
// NOTE: it is not called for each websocket message once upgraded.
|
||||||
fn call(&self, req: Request<Incoming>) -> Self::Future {
|
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
|
let failvalue =
|
||||||
// that ip address instead (otherwise their log file will just say "127.0.0.1"
|
|c: ChorusError| -> Self::Future { Box::pin(futures::future::ready(Err(c.into()))) };
|
||||||
// for every peer)
|
|
||||||
if peer.ip().is_loopback() {
|
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 Some(rip) = req.headers().get("x-real-ip") {
|
||||||
if let Ok(ripstr) = rip.to_str() {
|
if let Ok(ripstr) = rip.to_str() {
|
||||||
if let Ok(ipaddr) = ripstr.parse::<IpAddr>() {
|
if let Ok(ipaddr) = ripstr.parse::<IpAddr>() {
|
||||||
let hashed_ip = HashedIp::new(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