Skip to content

Commit

Permalink
Merge pull request #50 from PocketRelay/49-task-reverse-proxy-ip-forw…
Browse files Browse the repository at this point in the history
…arding-support

Fixed reverse proxy IP forwading
  • Loading branch information
jacobtread authored Sep 3, 2023
2 parents 078d393 + 4dd4b66 commit bdd4510
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 32 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 0 additions & 18 deletions default.json

This file was deleted.

8 changes: 7 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
4 changes: 4 additions & 0 deletions examples/nginx/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"port": 80,
"reverse_proxy": true
}
20 changes: 20 additions & 0 deletions examples/nginx/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions examples/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
events {}

http {
server {
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;
}
}
}
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -69,6 +71,7 @@ impl Default for Config {
fn default() -> Self {
Self {
port: 80,
reverse_proxy: false,
dashboard: Default::default(),
menu_message: "<font color='#B2B2B2'>Pocket Relay</font> - <font color='#FFFF66'>Logged as: {n}</font>".to_string(),
galaxy_at_war: Default::default(),
Expand Down
75 changes: 75 additions & 0 deletions src/middleware/ip_address.rs
Original file line number Diff line number Diff line change
@@ -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<S> FromRequestParts<S> for IpAddress
where
S: Send + Sync,
{
type Rejection = IpAddressError;

async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
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::<ConnectInfo<SocketAddr>>::from_request_parts(parts, state).await?;
Ok(Self(value.0 .0))
}
}

fn extract_ip_header(headers: &HeaderMap) -> Option<SocketAddr> {
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()
}
}
2 changes: 2 additions & 0 deletions src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
13 changes: 3 additions & 10 deletions src/routes/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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};

Expand Down Expand Up @@ -75,10 +71,7 @@ pub async fn dashboard_details() -> Json<DashboardDetails> {
/// 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<SocketAddr>,
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 {
Expand Down
1 change: 1 addition & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit bdd4510

Please sign in to comment.