all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH backup] server/rest: forward real client IP on proxied request
@ 2020-10-15 15:43 Thomas Lamprecht
  2020-10-16  8:38 ` [pbs-devel] applied: " Dietmar Maurer
  0 siblings, 1 reply; 2+ messages in thread
From: Thomas Lamprecht @ 2020-10-15 15:43 UTC (permalink / raw)
  To: pbs-devel

needs new proxmox dependency to get the RpcEnvironment changes,
adding client_ip getter and setter.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---

NOTE: needs new (not yet bumped) proxmox crate for the client_ip setter/getter

 src/api2/access.rs        |  7 ++++++-
 src/server/environment.rs | 10 ++++++++++
 src/server/rest.rs        | 38 ++++++++++++++++++++++++++++++++------
 3 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/src/api2/access.rs b/src/api2/access.rs
index 81666bda..19b128b1 100644
--- a/src/api2/access.rs
+++ b/src/api2/access.rs
@@ -138,6 +138,7 @@ fn create_ticket(
     path: Option<String>,
     privs: Option<String>,
     port: Option<u16>,
+    rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
     match authenticate_user(&username, &password, path, privs, port) {
         Ok(true) => {
@@ -157,7 +158,11 @@ fn create_ticket(
             "username": username,
         })),
         Err(err) => {
-            let client_ip = "unknown"; // $rpcenv->get_client_ip() || '';
+            let client_ip = match rpcenv.get_client_ip().map(|addr| addr.ip()) {
+                Some(ip) => format!("{}", ip),
+                None => "unknown".into(),
+            };
+
             log::error!("authentication failure; rhost={} user={} msg={}", client_ip, username, err.to_string());
             Err(http_err!(UNAUTHORIZED, "permission check failed."))
         }
diff --git a/src/server/environment.rs b/src/server/environment.rs
index 10ff958c..5fbff307 100644
--- a/src/server/environment.rs
+++ b/src/server/environment.rs
@@ -7,6 +7,7 @@ pub struct RestEnvironment {
     env_type: RpcEnvironmentType,
     result_attributes: Value,
     user: Option<String>,
+    client_ip: Option<std::net::SocketAddr>,
 }
 
 impl RestEnvironment {
@@ -14,6 +15,7 @@ impl RestEnvironment {
         Self {
             result_attributes: json!({}),
             user: None,
+            client_ip: None,
             env_type,
         }
     }
@@ -40,4 +42,12 @@ impl RpcEnvironment for RestEnvironment {
     fn get_user(&self) -> Option<String> {
         self.user.clone()
     }
+
+    fn set_client_ip(&mut self, client_ip: Option<std::net::SocketAddr>) {
+        self.client_ip = client_ip;
+    }
+
+    fn get_client_ip(&self) -> Option<std::net::SocketAddr> {
+        self.client_ip.clone()
+    }
 }
diff --git a/src/server/rest.rs b/src/server/rest.rs
index d145573f..98e56039 100644
--- a/src/server/rest.rs
+++ b/src/server/rest.rs
@@ -9,13 +9,15 @@ use std::task::{Context, Poll};
 use anyhow::{bail, format_err, Error};
 use futures::future::{self, FutureExt, TryFutureExt};
 use futures::stream::TryStreamExt;
-use hyper::header;
+use hyper::header::{self, HeaderMap};
 use hyper::http::request::Parts;
 use hyper::{Body, Request, Response, StatusCode};
+use lazy_static::lazy_static;
 use serde_json::{json, Value};
 use tokio::fs::File;
 use tokio::time::Instant;
 use url::form_urlencoded;
+use regex::Regex;
 
 use proxmox::http_err;
 use proxmox::api::{
@@ -127,6 +129,17 @@ fn log_response(
     }
 }
 
+fn get_proxied_peer(headers: &HeaderMap) -> Option<std::net::SocketAddr> {
+    lazy_static! {
+        static ref RE: Regex = Regex::new(r#"for="([^"]+)""#).unwrap();
+    }
+    let forwarded = headers.get(header::FORWARDED)?.to_str().ok()?;
+    let capture = RE.captures(&forwarded)?;
+    let rhost = capture.get(1)?.as_str();
+
+    rhost.parse().ok()
+}
+
 impl tower_service::Service<Request<Body>> for ApiService {
     type Response = Response<Body>;
     type Error = Error;
@@ -141,9 +154,12 @@ impl tower_service::Service<Request<Body>> for ApiService {
         let method = req.method().clone();
 
         let config = Arc::clone(&self.api_config);
-        let peer = self.peer;
+        let peer = match get_proxied_peer(req.headers()) {
+            Some(proxied_peer) => proxied_peer,
+            None => self.peer,
+        };
         async move {
-            let response = match handle_request(config, req).await {
+            let response = match handle_request(config, req, &peer).await {
                 Ok(response) => response,
                 Err(err) => {
                     let (err, code) = match err.downcast_ref::<HttpError>() {
@@ -246,6 +262,7 @@ async fn proxy_protected_request(
     info: &'static ApiMethod,
     mut parts: Parts,
     req_body: Body,
+    peer: &std::net::SocketAddr,
 ) -> Result<Response<Body>, Error> {
 
     let mut uri_parts = parts.uri.clone().into_parts();
@@ -256,7 +273,10 @@ async fn proxy_protected_request(
 
     parts.uri = new_uri;
 
-    let request = Request::from_parts(parts, req_body);
+    let mut request = Request::from_parts(parts, req_body);
+    request
+        .headers_mut()
+        .insert(header::FORWARDED, format!("for=\"{}\";", peer).parse().unwrap());
 
     let reload_timezone = info.reload_timezone;
 
@@ -505,7 +525,11 @@ fn check_auth(
     Ok(userid)
 }
 
-async fn handle_request(api: Arc<ApiConfig>, req: Request<Body>) -> Result<Response<Body>, Error> {
+async fn handle_request(
+    api: Arc<ApiConfig>,
+    req: Request<Body>,
+    peer: &std::net::SocketAddr,
+) -> Result<Response<Body>, Error> {
 
     let (parts, body) = req.into_parts();
 
@@ -517,6 +541,8 @@ async fn handle_request(api: Arc<ApiConfig>, req: Request<Body>) -> Result<Respo
     let env_type = api.env_type();
     let mut rpcenv = RestEnvironment::new(env_type);
 
+    rpcenv.set_client_ip(Some(*peer));
+
     let user_info = CachedUserInfo::new()?;
 
     let delay_unauth_time = std::time::Instant::now() + std::time::Duration::from_millis(3000);
@@ -571,7 +597,7 @@ async fn handle_request(api: Arc<ApiConfig>, req: Request<Body>) -> Result<Respo
                     }
 
                     let result = if api_method.protected && env_type == RpcEnvironmentType::PUBLIC {
-                        proxy_protected_request(api_method, parts, body).await
+                        proxy_protected_request(api_method, parts, body, peer).await
                     } else {
                         handle_api_request(rpcenv, api_method, formatter, parts, body, uri_param).await
                     };
-- 
2.27.0





^ permalink raw reply	[flat|nested] 2+ messages in thread

* [pbs-devel] applied: [PATCH backup] server/rest: forward real client IP on proxied request
  2020-10-15 15:43 [pbs-devel] [PATCH backup] server/rest: forward real client IP on proxied request Thomas Lamprecht
@ 2020-10-16  8:38 ` Dietmar Maurer
  0 siblings, 0 replies; 2+ messages in thread
From: Dietmar Maurer @ 2020-10-16  8:38 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Thomas Lamprecht

applied




^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-10-16  8:39 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-15 15:43 [pbs-devel] [PATCH backup] server/rest: forward real client IP on proxied request Thomas Lamprecht
2020-10-16  8:38 ` [pbs-devel] applied: " Dietmar Maurer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal