mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-12 12:40:31 +00:00
feat: upstream auth for all protocols (Docker, Maven, npm, PyPI)
Wire up basic auth credentials for upstream registry proxying: - Docker: pass configured auth to Bearer token requests - Maven: support url|auth format in NORA_MAVEN_PROXIES env var - npm: add NORA_NPM_PROXY_AUTH env var - PyPI: add NORA_PYPI_PROXY_AUTH env var - Mask credentials in logs (never log plaintext passwords) Config examples: NORA_DOCKER_UPSTREAMS="https://registry.corp.com|user:pass" NORA_MAVEN_PROXIES="https://nexus.corp.com/maven2|user:pass" NORA_NPM_PROXY_AUTH="user:pass" NORA_PYPI_PROXY_AUTH="user:pass"
This commit is contained in:
@@ -15,6 +15,7 @@ use axum::{
|
||||
routing::{delete, get, head, patch, put},
|
||||
Json, Router,
|
||||
};
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
use parking_lot::RwLock;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
@@ -181,6 +182,7 @@ async fn download_blob(
|
||||
&digest,
|
||||
&state.docker_auth,
|
||||
state.config.docker.proxy_timeout,
|
||||
upstream.auth.as_deref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -392,6 +394,7 @@ async fn get_manifest(
|
||||
&reference,
|
||||
&state.docker_auth,
|
||||
state.config.docker.proxy_timeout,
|
||||
upstream.auth.as_deref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -733,6 +736,7 @@ async fn fetch_blob_from_upstream(
|
||||
digest: &str,
|
||||
docker_auth: &DockerAuth,
|
||||
timeout: u64,
|
||||
basic_auth: Option<&str>,
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
let url = format!(
|
||||
"{}/v2/{}/blobs/{}",
|
||||
@@ -741,13 +745,13 @@ async fn fetch_blob_from_upstream(
|
||||
digest
|
||||
);
|
||||
|
||||
// First try without auth
|
||||
let response = client
|
||||
.get(&url)
|
||||
.timeout(Duration::from_secs(timeout))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|_| ())?;
|
||||
// First try — with basic auth if configured
|
||||
let mut request = client.get(&url).timeout(Duration::from_secs(timeout));
|
||||
if let Some(credentials) = basic_auth {
|
||||
let encoded = STANDARD.encode(credentials);
|
||||
request = request.header("Authorization", format!("Basic {}", encoded));
|
||||
}
|
||||
let response = request.send().await.map_err(|_| ())?;
|
||||
|
||||
let response = if response.status() == reqwest::StatusCode::UNAUTHORIZED {
|
||||
// Get Www-Authenticate header and fetch token
|
||||
@@ -758,7 +762,7 @@ async fn fetch_blob_from_upstream(
|
||||
.map(String::from);
|
||||
|
||||
if let Some(token) = docker_auth
|
||||
.get_token(upstream_url, name, www_auth.as_deref())
|
||||
.get_token(upstream_url, name, www_auth.as_deref(), basic_auth)
|
||||
.await
|
||||
{
|
||||
client
|
||||
@@ -790,6 +794,7 @@ async fn fetch_manifest_from_upstream(
|
||||
reference: &str,
|
||||
docker_auth: &DockerAuth,
|
||||
timeout: u64,
|
||||
basic_auth: Option<&str>,
|
||||
) -> Result<(Vec<u8>, String), ()> {
|
||||
let url = format!(
|
||||
"{}/v2/{}/manifests/{}",
|
||||
@@ -806,16 +811,18 @@ async fn fetch_manifest_from_upstream(
|
||||
application/vnd.oci.image.manifest.v1+json, \
|
||||
application/vnd.oci.image.index.v1+json";
|
||||
|
||||
// First try without auth
|
||||
let response = client
|
||||
// First try — with basic auth if configured
|
||||
let mut request = client
|
||||
.get(&url)
|
||||
.timeout(Duration::from_secs(timeout))
|
||||
.header("Accept", accept_header)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!(error = %e, url = %url, "Failed to send request to upstream");
|
||||
})?;
|
||||
.header("Accept", accept_header);
|
||||
if let Some(credentials) = basic_auth {
|
||||
let encoded = STANDARD.encode(credentials);
|
||||
request = request.header("Authorization", format!("Basic {}", encoded));
|
||||
}
|
||||
let response = request.send().await.map_err(|e| {
|
||||
tracing::error!(error = %e, url = %url, "Failed to send request to upstream");
|
||||
})?;
|
||||
|
||||
tracing::debug!(status = %response.status(), "Initial upstream response");
|
||||
|
||||
@@ -830,7 +837,7 @@ async fn fetch_manifest_from_upstream(
|
||||
tracing::debug!(www_auth = ?www_auth, "Got 401, fetching token");
|
||||
|
||||
if let Some(token) = docker_auth
|
||||
.get_token(upstream_url, name, www_auth.as_deref())
|
||||
.get_token(upstream_url, name, www_auth.as_deref(), basic_auth)
|
||||
.await
|
||||
{
|
||||
tracing::debug!("Token acquired, retrying with auth");
|
||||
|
||||
Reference in New Issue
Block a user