From 9b91efe56ee3e3276c3ad71f8069d0d349b7ff71 Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Mon, 4 Sep 2023 10:08:41 +1200 Subject: [PATCH 1/6] Added reverse proxy config variable --- src/config.rs | 3 +++ src/state.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/src/config.rs b/src/config.rs index 77695261..7daa0968 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,6 +4,7 @@ use serde::Deserialize; use std::{env, fs::read_to_string, path::Path}; pub struct RuntimeConfig { + pub reverse_proxy: bool, pub galaxy_at_war: GalaxyAtWarConfig, pub menu_message: String, pub dashboard: DashboardConfig, @@ -58,6 +59,7 @@ pub struct ServicesConfig { #[serde(default)] pub struct Config { pub port: Port, + pub reverse_proxy: bool, pub dashboard: DashboardConfig, pub menu_message: String, pub galaxy_at_war: GalaxyAtWarConfig, @@ -69,6 +71,7 @@ impl Default for Config { fn default() -> Self { Self { port: 80, + reverse_proxy: false, dashboard: Default::default(), menu_message: "Pocket Relay - Logged as: {n}".to_string(), galaxy_at_war: Default::default(), diff --git a/src/state.rs b/src/state.rs index 1a9e1ea9..f9f19ed9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -51,6 +51,7 @@ impl App { // Config data persisted to runtime let runtime_config = RuntimeConfig { + reverse_proxy: config.reverse_proxy, galaxy_at_war: config.galaxy_at_war, menu_message, dashboard: config.dashboard, From 2417923757b8318d39c20c2110976bef2a70f50b Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Mon, 4 Sep 2023 10:28:34 +1200 Subject: [PATCH 2/6] Added handling for X-Real-IP header --- src/middleware/ip_address.rs | 75 ++++++++++++++++++++++++++++++++++++ src/middleware/mod.rs | 2 + src/routes/server.rs | 13 ++----- 3 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 src/middleware/ip_address.rs diff --git a/src/middleware/ip_address.rs b/src/middleware/ip_address.rs new file mode 100644 index 00000000..62e19449 --- /dev/null +++ b/src/middleware/ip_address.rs @@ -0,0 +1,75 @@ +use std::net::SocketAddr; + +use axum::{ + async_trait, + body::boxed, + extract::{rejection::ExtensionRejection, ConnectInfo, FromRequestParts}, + http::request::Parts, + response::{IntoResponse, Response}, + Extension, +}; +use hyper::{HeaderMap, StatusCode}; +use log::warn; +use thiserror::Error; + +use crate::state::App; + +/// Middleware for extracting the server public address +pub struct IpAddress(pub SocketAddr); + +const REAL_IP_HEADER: &str = "X-Real-IP"; + +#[async_trait] +impl FromRequestParts for IpAddress +where + S: Send + Sync, +{ + type Rejection = IpAddressError; + + async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { + let reverse_proxy = App::config().reverse_proxy; + if reverse_proxy { + let ip = match extract_ip_header(&parts.headers) { + Some(ip) => ip, + None => { + warn!("Failed to extract X-Real-IP header from connecting client. If you are NOT using a reverse proxy\n\ + disable the `reverse_proxy` config property, otherwise check that your reverse proxy is configured\n\ + correctly according the guide. (Closing connection with error)"); + return Err(IpAddressError::InvalidOrMissing); + } + }; + return Ok(Self(ip)); + } + let value = Extension::>::from_request_parts(parts, state).await?; + Ok(Self(value.0 .0)) + } +} + +fn extract_ip_header(headers: &HeaderMap) -> Option { + let header = headers.get(REAL_IP_HEADER)?; + let value = header.to_str().ok()?; + value.parse().ok() +} + +/// Error type used by the token checking middleware to handle +/// different errors and create error respones based on them +#[derive(Debug, Error)] +pub enum IpAddressError { + #[error(transparent)] + ConnectInfo(#[from] ExtensionRejection), + #[error("X-Real-IP header is invalid or missing")] + InvalidOrMissing, +} + +/// IntoResponse implementation for TokenError to allow it to be +/// used within the result type as a error response +impl IntoResponse for IpAddressError { + #[inline] + fn into_response(self) -> Response { + let status: StatusCode = match self { + IpAddressError::ConnectInfo(err) => return err.into_response(), + _ => StatusCode::BAD_REQUEST, + }; + (status, boxed(self.to_string())).into_response() + } +} diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 586fac87..94a42ff6 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -4,5 +4,7 @@ pub mod auth; pub mod blaze_upgrade; /// Middleware functions related to CORS implementation pub mod cors; +/// IP address extraction middleware +pub mod ip_address; /// XML response types pub mod xml; diff --git a/src/routes/server.rs b/src/routes/server.rs index 2f9849fa..7ef020f8 100644 --- a/src/routes/server.rs +++ b/src/routes/server.rs @@ -3,14 +3,13 @@ use crate::{ database::entities::players::PlayerRole, - middleware::{auth::AdminAuth, blaze_upgrade::BlazeUpgrade}, + middleware::{auth::AdminAuth, blaze_upgrade::BlazeUpgrade, ip_address::IpAddress}, session::Session, state::{self, App}, utils::logging::LOG_FILE_NAME, }; use axum::{ body::Empty, - extract::ConnectInfo, http::{header, HeaderValue, StatusCode}, response::{IntoResponse, Response}, Json, @@ -19,10 +18,7 @@ use blaze_pk::packet::PacketCodec; use interlink::service::Service; use log::{debug, error}; use serde::{Deserialize, Serialize}; -use std::{ - net::SocketAddr, - sync::atomic::{AtomicU32, Ordering}, -}; +use std::sync::atomic::{AtomicU32, Ordering}; use tokio::{fs::read_to_string, io::split}; use tokio_util::codec::{FramedRead, FramedWrite}; @@ -75,10 +71,7 @@ pub async fn dashboard_details() -> Json { /// Handles upgrading connections from the Pocket Relay Client tool /// from HTTP over to the Blaze protocol for proxing the game traffic /// as blaze sessions using HTTP Upgrade -pub async fn upgrade( - ConnectInfo(socket_addr): ConnectInfo, - upgrade: BlazeUpgrade, -) -> Response { +pub async fn upgrade(IpAddress(socket_addr): IpAddress, upgrade: BlazeUpgrade) -> Response { // TODO: Socket address extraction for forwarded reverse proxy tokio::spawn(async move { From ef791838805d891ec898900fa730f175301a6920 Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Mon, 4 Sep 2023 10:45:16 +1200 Subject: [PATCH 3/6] Added example nginx within docker, Added bind volumes for config into standard docker compose --- docker-compose.yml | 8 +++++++- examples/nginx/config.json | 4 ++++ examples/nginx/docker-compose.yml | 20 ++++++++++++++++++++ examples/nginx/nginx.conf | 22 ++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 examples/nginx/config.json create mode 100644 examples/nginx/docker-compose.yml create mode 100644 examples/nginx/nginx.conf diff --git a/docker-compose.yml b/docker-compose.yml index 3d6cd0f0..f6411e72 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,13 @@ version: "3" services: pocket-relay: container_name: pocket-relay + restart: unless-stopped ports: # Server port - 80:80/tcp - image: jacobtread/pocket-relay:latest \ No newline at end of file + image: jacobtread/pocket-relay:latest + volumes: + # Bind the server config to a local config.json file + - ./config.json:/app/config.json + # Binding the server data to a local data folder + - ./data:/app/data diff --git a/examples/nginx/config.json b/examples/nginx/config.json new file mode 100644 index 00000000..d6112d35 --- /dev/null +++ b/examples/nginx/config.json @@ -0,0 +1,4 @@ +{ + "port": 80, + "reverse_proxy": true +} \ No newline at end of file diff --git a/examples/nginx/docker-compose.yml b/examples/nginx/docker-compose.yml new file mode 100644 index 00000000..ccc25b6f --- /dev/null +++ b/examples/nginx/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3" +services: + server: + restart: unless-stopped + container_name: pocket-relay + image: jacobtread/pocket-relay:latest + volumes: + # Bind the server config to a local config.json file + - ./config.json:/app/config.json + # Binding the server data to a local data folder + - ./data:/app/data + nginx: + restart: unless-stopped + image: nginx + ports: + - "80:80/tcp" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + - server \ No newline at end of file diff --git a/examples/nginx/nginx.conf b/examples/nginx/nginx.conf new file mode 100644 index 00000000..008b0a22 --- /dev/null +++ b/examples/nginx/nginx.conf @@ -0,0 +1,22 @@ +events {} + +http { + server { + listen 80; + listen [::]:80; + + server_name localhost; + + location / { + proxy_pass http://server:80; + + # Provide server with real IP address of clients + proxy_set_header X-Real-IP $remote_addr; + + # Upgrade websocket connections + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + } + } +} From d96342cd8dc148445e4ff30399e6662d770ffe1d Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Mon, 4 Sep 2023 11:10:01 +1200 Subject: [PATCH 4/6] Removed extra listen from nginx config --- examples/nginx/nginx.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/nginx/nginx.conf b/examples/nginx/nginx.conf index 008b0a22..df0062d1 100644 --- a/examples/nginx/nginx.conf +++ b/examples/nginx/nginx.conf @@ -3,7 +3,6 @@ events {} http { server { listen 80; - listen [::]:80; server_name localhost; From 76aae71f7f35c1de5f1ab9441ea80ec36334a41d Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Mon, 4 Sep 2023 11:29:25 +1200 Subject: [PATCH 5/6] Bumped version for release --- Cargo.lock | 2 +- Cargo.toml | 2 +- Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b24632f6..8886007a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1392,7 +1392,7 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "pocket-relay" -version = "0.5.7" +version = "0.5.8" dependencies = [ "argon2", "axum", diff --git a/Cargo.toml b/Cargo.toml index 0bdb4766..8db7044b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pocket-relay" -version = "0.5.7" +version = "0.5.8" description = "Pocket Relay Server" readme = "README.md" keywords = ["EA", "PocketRelay", "MassEffect"] diff --git a/Dockerfile b/Dockerfile index 79110d02..d7d59ce8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN apk add curl WORKDIR /app # Download server executable -RUN curl -LJ -o pocket-relay-linux https://github.com/PocketRelay/Server/releases/download/v0.5.7/pocket-relay-linux +RUN curl -LJ -o pocket-relay-linux https://github.com/PocketRelay/Server/releases/download/v0.5.8/pocket-relay-linux # Make the server executable RUN chmod +x ./pocket-relay-linux From 4dd4b661460f8248cb18803d93a8c7bec6608182 Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Mon, 4 Sep 2023 11:29:37 +1200 Subject: [PATCH 6/6] Removed old default config --- default.json | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 default.json diff --git a/default.json b/default.json deleted file mode 100644 index beb13361..00000000 --- a/default.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "port": 80, - "dashboard": { - "super_email": "example@example.com", - "super_password": "password" - }, - "menu_message": "Pocket Relay - Logged as: {n}", - "galaxy_at_war": { - "decay": 0.0, - "promotions": true - }, - "logging": "info", - "retriever": { - "enabled": true, - "origin_fetch": true, - "origin_fetch_data": true - } -} \ No newline at end of file