From 4fdf13f95fb535d33235ffde46920fab755aec1f Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Wed, 4 Nov 2020 16:12:13 +0100 Subject: [PATCH] api: factor out auth logger and use for all API authentication failures we have information here not available in the access log, especially if the /api2/extjs formatter is used, which encapsulates errors in a 200 response. So keep the auth log for now, but extend it use from create ticket calls to all authentication failures for API calls, this ensures one can also fail2ban tokens. Do that logging in a central place, which makes it simple but means that we do not have the user ID information available to include in the log. Signed-off-by: Thomas Lamprecht --- debian/postinst | 3 +++ src/api2/access.rs | 12 ++---------- src/buildcfg.rs | 7 ++++++- src/server/rest.rs | 13 +++++++++++++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/debian/postinst b/debian/postinst index 300fdf46..af5ee097 100644 --- a/debian/postinst +++ b/debian/postinst @@ -25,6 +25,9 @@ case "$1" in sed -i '/^\s\+verify-schedule /d' /etc/proxmox-backup/datastore.cfg || true fi fi + if dpkg --compare-versions "$2" 'le' '0.9.5-1'; then + chown --quiet backup:backup /var/log/proxmox-backup/api/auth.log || true + fi fi # FIXME: Remove in future version once we're sure no broken entries remain in anyone's files if grep -q -e ':termproxy::[^@]\+: ' /var/log/proxmox-backup/tasks/active; then diff --git a/src/api2/access.rs b/src/api2/access.rs index 9b9e85c0..3b59b3d3 100644 --- a/src/api2/access.rs +++ b/src/api2/access.rs @@ -12,7 +12,6 @@ use proxmox::{http_err, list_subdirs_api_method}; use crate::tools::ticket::{self, Empty, Ticket}; use crate::auth_helpers::*; use crate::api2::types::*; -use crate::tools::{FileLogOptions, FileLogger}; use crate::config::acl as acl_config; use crate::config::acl::{PRIVILEGES, PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY}; @@ -144,20 +143,13 @@ fn create_ticket( port: Option, rpcenv: &mut dyn RpcEnvironment, ) -> Result { - let logger_options = FileLogOptions { - append: true, - prefix_time: true, - ..Default::default() - }; - let mut auth_log = FileLogger::new("/var/log/proxmox-backup/api/auth.log", logger_options)?; - match authenticate_user(&username, &password, path, privs, port) { Ok(true) => { let ticket = Ticket::new("PBS", &username)?.sign(private_auth_key(), None)?; let token = assemble_csrf_prevention_token(csrf_secret(), &username); - auth_log.log(format!("successful auth for user '{}'", username)); + crate::server::rest::auth_logger()?.log(format!("successful auth for user '{}'", username)); Ok(json!({ "username": username, @@ -180,7 +172,7 @@ fn create_ticket( username, err.to_string() ); - auth_log.log(&msg); + crate::server::rest::auth_logger()?.log(&msg); log::error!("{}", msg); Err(http_err!(UNAUTHORIZED, "permission check failed.")) diff --git a/src/buildcfg.rs b/src/buildcfg.rs index 402f2c9d..c7e65032 100644 --- a/src/buildcfg.rs +++ b/src/buildcfg.rs @@ -16,9 +16,14 @@ pub const PROXMOX_BACKUP_RUN_DIR: &str = PROXMOX_BACKUP_RUN_DIR_M!(); /// namespaced directory for persistent logging pub const PROXMOX_BACKUP_LOG_DIR: &str = PROXMOX_BACKUP_LOG_DIR_M!(); -/// logfile for all API reuests handled by the proxy and privileged API daemons +/// logfile for all API reuests handled by the proxy and privileged API daemons. Note that not all +/// failed logins can be logged here with full information, use the auth log for that. pub const API_ACCESS_LOG_FN: &str = concat!(PROXMOX_BACKUP_LOG_DIR_M!(), "/api/access.log"); +/// logfile for any failed authentication, via ticket or via token, and new successfull ticket +/// creations. This file can be useful for fail2ban. +pub const API_AUTH_LOG_FN: &str = concat!(PROXMOX_BACKUP_LOG_DIR_M!(), "/api/auth.log"); + /// the PID filename for the unprivileged proxy daemon pub const PROXMOX_BACKUP_PROXY_PID_FN: &str = concat!(PROXMOX_BACKUP_RUN_DIR_M!(), "/proxy.pid"); diff --git a/src/server/rest.rs b/src/server/rest.rs index 0749b891..ea87c9c8 100644 --- a/src/server/rest.rs +++ b/src/server/rest.rs @@ -164,6 +164,15 @@ fn log_response( )); } } +pub fn auth_logger() -> Result { + let logger_options = tools::FileLogOptions { + append: true, + prefix_time: true, + owned_by_backup: true, + ..Default::default() + }; + FileLogger::new(crate::buildcfg::API_AUTH_LOG_FN, logger_options) +} fn get_proxied_peer(headers: &HeaderMap) -> Option { lazy_static! { @@ -687,6 +696,10 @@ async fn handle_request( match auth_result { Ok(authid) => rpcenv.set_auth_id(Some(authid.to_string())), Err(err) => { + let peer = peer.ip(); + auth_logger()? + .log(format!("authentication failure; rhost={} msg={}", peer, err)); + // always delay unauthorized calls by 3 seconds (from start of request) let err = http_err!(UNAUTHORIZED, "authentication failed - {}", err); tokio::time::delay_until(Instant::from_std(delay_unauth_time)).await;