diff --git a/src/api2/node/apt.rs b/src/api2/node/apt.rs index 1e57ea8d..c149a14b 100644 --- a/src/api2/node/apt.rs +++ b/src/api2/node/apt.rs @@ -5,11 +5,11 @@ use std::collections::HashMap; use proxmox::list_subdirs_api_method; use proxmox::api::{api, RpcEnvironment, RpcEnvironmentType, Permission}; use proxmox::api::router::{Router, SubdirMap}; +use proxmox::tools::fs::{replace_file, CreateOptions}; use crate::config::node; use crate::server::WorkerTask; -use crate::tools::{apt, SimpleHttp, subscription}; - +use crate::tools::{apt, SimpleHttp, http::ProxyConfig, subscription}; use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}; use crate::api2::types::{Authid, APTUpdateInfo, NODE_SCHEMA, UPID_SCHEMA}; @@ -47,10 +47,38 @@ fn apt_update_available(_param: Value) -> Result { Ok(json!(cache.package_status)) } +pub fn update_apt_proxy_config(proxy_config: Option<&ProxyConfig>) -> Result<(), Error> { + + const PROXY_CFG_FN: &str = "/etc/apt/apt.conf.d/76pveproxy"; // use same file as PVE + + if let Some(proxy_config) = proxy_config { + let proxy = proxy_config.to_proxy_string()?; + let data = format!("Acquire::http::Proxy \"{}\";\n", proxy); + replace_file(PROXY_CFG_FN, data.as_bytes(), CreateOptions::new())?; + } else { + std::fs::remove_file(PROXY_CFG_FN).map_err(|err| { + format_err!("failed to remove proxy config '{}' - {}", PROXY_CFG_FN, err) + })?; + } + + Ok(()) +} + +fn read_and_update_proxy_config() -> Result, Error> { + let proxy_config = if let Ok((node_config, _digest)) = node::config() { + node_config.http_proxy() + } else { + None + }; + update_apt_proxy_config(proxy_config.as_ref())?; + + Ok(proxy_config) +} + fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> { if !quiet { worker.log("starting apt-get update") } - // TODO: set proxy /etc/apt/apt.conf.d/76pbsproxy like PVE + read_and_update_proxy_config()?; let mut command = std::process::Command::new("apt-get"); command.arg("update"); @@ -153,6 +181,7 @@ pub fn apt_update_database( } #[api( + protected: true, input: { properties: { node: { @@ -195,12 +224,7 @@ fn apt_get_changelog( bail!("Package '{}' not found", name); } - let proxy_config = if let Ok((node_config, _digest)) = node::config() { - node_config.http_proxy() - } else { - None - }; - + let proxy_config = read_and_update_proxy_config()?; let mut client = SimpleHttp::new(proxy_config); let changelog_url = &pkg_info[0].change_log_url; diff --git a/src/api2/node/config.rs b/src/api2/node/config.rs index 92c0100e..d7d96c34 100644 --- a/src/api2/node/config.rs +++ b/src/api2/node/config.rs @@ -4,6 +4,7 @@ use proxmox::api::schema::Updatable; use proxmox::api::{api, Permission, Router, RpcEnvironment}; use crate::api2::types::NODE_SCHEMA; +use crate::api2::node::apt::update_apt_proxy_config; use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}; use crate::config::node::{NodeConfig, NodeConfigUpdater}; @@ -78,5 +79,9 @@ pub fn update_node_config( config.update_from(updater, &delete)?; - crate::config::node::save_config(&config) + crate::config::node::save_config(&config)?; + + update_apt_proxy_config(config.http_proxy().as_ref())?; + + Ok(()) } diff --git a/src/config/node.rs b/src/config/node.rs index 1ddee688..e818e47e 100644 --- a/src/config/node.rs +++ b/src/config/node.rs @@ -147,6 +147,7 @@ impl NodeConfig { AcmeDomainIter::new(self) } + /// Returns the parsed ProxyConfig pub fn http_proxy(&self) -> Option { if let Some(http_proxy) = &self.http_proxy { match ProxyConfig::parse_proxy_url(&http_proxy) { @@ -158,7 +159,8 @@ impl NodeConfig { } } - pub fn set_proxy(&mut self, http_proxy: Option) { + /// Sets the HTTP proxy configuration + pub fn set_http_proxy(&mut self, http_proxy: Option) { self.http_proxy = http_proxy; } diff --git a/src/tools/http.rs b/src/tools/http.rs index 998e7647..0821992a 100644 --- a/src/tools/http.rs +++ b/src/tools/http.rs @@ -43,7 +43,7 @@ pub(crate) fn build_authority(host: &str, port: u16) -> Result pub struct ProxyConfig { pub host: String, pub port: u16, - pub authorization: Option, // Proxy-Authorization header value + pub authorization: Option, // user:pass pub force_connect: bool, } @@ -94,7 +94,7 @@ impl ProxyConfig { let authority_vec: Vec<&str> = proxy_authority.as_str().rsplitn(2, '@').collect(); let authorization = if authority_vec.len() == 2 { - Some(format!("Basic {}", base64::encode(authority_vec[1]))) + Some(authority_vec[1].to_string()) } else { None }; @@ -107,6 +107,15 @@ impl ProxyConfig { }) }).map_err(|err| format_err!("parse_proxy_url failed: {}", err)) } + + /// Assemble canonical proxy string (including scheme and port) + pub fn to_proxy_string(&self) -> Result { + let authority = build_authority(&self.host, self.port)?; + Ok(match self.authorization { + None => format!("http://{}", authority), + Some(ref authorization) => format!("http://{}@{}", authorization, authority) + }) + } } #[derive(Clone)] @@ -241,7 +250,10 @@ impl hyper::service::Service for HttpsConnector { let mut connect_request = format!("CONNECT {0}:{1} HTTP/1.1\r\n", host, port); if let Some(authorization) = authorization { - connect_request.push_str(&format!("Proxy-Authorization: {}\r\n", authorization)); + connect_request.push_str(&format!( + "Proxy-Authorization: Basic {}\r\n", + base64::encode(authorization), + )); } connect_request.push_str(&format!("Host: {0}:{1}\r\n\r\n", host, port));