Skip to content

Commit bc280d9

Browse files
committed
Make reverse proxy check credentials when accessing internal URLs
1 parent 28b9d9a commit bc280d9

File tree

4 files changed

+34
-25
lines changed

4 files changed

+34
-25
lines changed

src/container_orchestrator.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ macro_rules! try_quiet {
3030
};
3131
}
3232

33-
#[derive(Debug)]
3433
pub(crate) struct ContainerOrchestrator {
3534
podman: Podman,
3635
reverse_proxy: Arc<ReverseProxy>,

src/main.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ async fn main() -> anyhow::Result<()> {
5555

5656
debug!(?cfg, "loaded configuration");
5757

58+
let rockslide_pw = cfg.rockslide.master_key.as_secret_string();
59+
let auth_provider = Arc::new(cfg.rockslide.master_key);
60+
5861
let local_ip: IpAddr = if podman_is_remote() {
5962
info!("podman is remote, trying to guess IP address");
6063
let local_hostname = gethostname();
@@ -78,12 +81,9 @@ async fn main() -> anyhow::Result<()> {
7881
let local_addr = SocketAddr::from(([127, 0, 0, 1], cfg.reverse_proxy.http_bind.port()));
7982
info!(%local_addr, "guessing local registry address");
8083

81-
let reverse_proxy = ReverseProxy::new();
84+
let reverse_proxy = ReverseProxy::new(auth_provider.clone());
8285

83-
let credentials = (
84-
"rockslide-podman".to_owned(),
85-
cfg.rockslide.master_key.as_secret_string(),
86-
);
86+
let credentials = ("rockslide-podman".to_owned(), rockslide_pw);
8787
let orchestrator = Arc::new(ContainerOrchestrator::new(
8888
&cfg.containers.podman_path,
8989
reverse_proxy.clone(),
@@ -97,11 +97,7 @@ async fn main() -> anyhow::Result<()> {
9797
orchestrator.synchronize_all().await?;
9898
orchestrator.updated_published_set().await;
9999

100-
let registry = ContainerRegistry::new(
101-
&cfg.registry.storage_path,
102-
orchestrator,
103-
cfg.rockslide.master_key,
104-
)?;
100+
let registry = ContainerRegistry::new(&cfg.registry.storage_path, orchestrator, auth_provider)?;
105101

106102
let app = Router::new()
107103
.merge(registry.make_router())

src/registry.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,24 +92,20 @@ impl IntoResponse for AppError {
9292

9393
pub(crate) struct ContainerRegistry {
9494
realm: String,
95-
auth_provider: Box<dyn AuthProvider>,
95+
auth_provider: Arc<dyn AuthProvider>,
9696
storage: Box<dyn RegistryStorage>,
9797
hooks: Box<dyn RegistryHooks>,
9898
}
9999

100100
impl ContainerRegistry {
101-
pub(crate) fn new<
102-
P: AsRef<std::path::Path>,
103-
T: RegistryHooks + 'static,
104-
A: AuthProvider + 'static,
105-
>(
101+
pub(crate) fn new<P: AsRef<std::path::Path>, T: RegistryHooks + 'static>(
106102
storage_path: P,
107103
orchestrator: T,
108-
auth_provider: A,
104+
auth_provider: Arc<dyn AuthProvider>,
109105
) -> Result<Arc<Self>, FilesystemStorageError> {
110106
Ok(Arc::new(ContainerRegistry {
111107
realm: "ContainerRegistry".to_string(),
112-
auth_provider: Box::new(auth_provider),
108+
auth_provider: auth_provider,
113109
storage: Box::new(FilesystemStorage::new(storage_path)?),
114110
hooks: Box::new(orchestrator),
115111
}))

src/reverse_proxy.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,21 @@ use axum::{
1515
Method, StatusCode, Uri,
1616
},
1717
response::{IntoResponse, Response},
18-
Router,
18+
RequestExt, Router,
1919
};
2020
use itertools::Itertools;
2121
use tokio::sync::RwLock;
2222
use tracing::{trace, warn};
2323

2424
use crate::{
2525
container_orchestrator::{ContainerOrchestrator, PublishedContainer},
26-
registry::{storage::ImageLocation, ManifestReference, Reference},
26+
registry::{
27+
storage::ImageLocation, AuthProvider, ManifestReference, Reference, UnverifiedCredentials,
28+
},
2729
};
2830

29-
#[derive(Debug)]
3031
pub(crate) struct ReverseProxy {
32+
auth_provider: Arc<dyn AuthProvider>,
3133
client: reqwest::Client,
3234
routing_table: RwLock<RoutingTable>,
3335
orchestrator: OnceLock<Arc<ContainerOrchestrator>>,
@@ -173,6 +175,7 @@ enum AppError {
173175
InternalUrlInvalid,
174176
AssertionFailed(&'static str),
175177
NonUtf8Header,
178+
AuthFailure(StatusCode),
176179
Internal(anyhow::Error),
177180
}
178181

@@ -184,6 +187,7 @@ impl Display for AppError {
184187
AppError::InternalUrlInvalid => f.write_str("internal url invalid"),
185188
AppError::AssertionFailed(msg) => f.write_str(msg),
186189
AppError::NonUtf8Header => f.write_str("a header contained non-utf8 data"),
190+
AppError::AuthFailure(_) => f.write_str("authentication missing or not present"),
187191
AppError::Internal(err) => Display::fmt(err, f),
188192
}
189193
}
@@ -207,6 +211,7 @@ impl IntoResponse for AppError {
207211
AppError::InternalUrlInvalid => StatusCode::NOT_FOUND.into_response(),
208212
AppError::AssertionFailed(msg) => (StatusCode::NOT_FOUND, msg).into_response(),
209213
AppError::NonUtf8Header => StatusCode::BAD_REQUEST.into_response(),
214+
AppError::AuthFailure(status) => status.into_response(),
210215
AppError::Internal(err) => {
211216
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response()
212217
}
@@ -215,8 +220,9 @@ impl IntoResponse for AppError {
215220
}
216221

217222
impl ReverseProxy {
218-
pub(crate) fn new() -> Arc<Self> {
223+
pub(crate) fn new(auth_provider: Arc<dyn AuthProvider>) -> Arc<Self> {
219224
Arc::new(ReverseProxy {
225+
auth_provider,
220226
client: reqwest::Client::new(),
221227
routing_table: RwLock::new(Default::default()),
222228
orchestrator: OnceLock::new(),
@@ -240,6 +246,7 @@ impl ReverseProxy {
240246
pub(crate) fn set_orchestrator(&self, orchestrator: Arc<ContainerOrchestrator>) -> &Self {
241247
self.orchestrator
242248
.set(orchestrator)
249+
.map_err(|_| ())
243250
.expect("set already set orchestrator");
244251
self
245252
}
@@ -271,7 +278,18 @@ async fn route_request(
271278
let dest = match dest_uri {
272279
Destination::ReverseProxied(dest) => dest,
273280
Destination::Internal(uri) => {
274-
todo!("check access (master password)");
281+
let method = request.method().clone();
282+
// Note: The auth functionality has been lifted from `registry`. It may need to be
283+
// refactored out because of that.
284+
let creds = request
285+
.extract::<UnverifiedCredentials, _>()
286+
.await
287+
.map_err(AppError::AuthFailure)?;
288+
289+
// Any internal URL is subject to requiring auth through the master key.
290+
if !rp.auth_provider.check_credentials(&creds).await {
291+
todo!("return 403");
292+
}
275293

276294
let remainder = uri
277295
.path()
@@ -297,7 +315,7 @@ async fn route_request(
297315
.get()
298316
.ok_or_else(|| AppError::AssertionFailed("no orchestrator configured"))?;
299317

300-
return match *request.method() {
318+
return match method {
301319
Method::GET => {
302320
let config = orchestrator
303321
.load_config(&manifest_reference)

0 commit comments

Comments
 (0)