Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dstack/gateway/docs/cluster-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ Important:

### 2.7 Verify Cluster Sync

The admin API requires a bearer token (see `core.admin.admin_token` in `gateway.toml`,
The admin API requires a bearer token (see `core.admin.auth_token` in `gateway.toml`,
or the `ADMIN_API_TOKEN` env injected by `deploy-to-vmm.sh`). Export it once:

```bash
Expand Down
2 changes: 1 addition & 1 deletion dstack/gateway/dstack-app/builder/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ sync_connections_interval = "${SYNC_CONNECTIONS_INTERVAL:-30s}"
enabled = true
address = "${ADMIN_LISTEN_ADDR:-0.0.0.0}"
port = ${ADMIN_LISTEN_PORT:-8001}
admin_token = "${ADMIN_API_TOKEN}"
auth_token = "${ADMIN_API_TOKEN}"

[core.wg]
public_key = "$PUBLIC_KEY"
Expand Down
4 changes: 2 additions & 2 deletions dstack/gateway/gateway.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ address = "127.0.0.1:8011"
# be supplied via the `DSTACK_GATEWAY_ADMIN_TOKEN` or `ADMIN_API_TOKEN` env
# vars. Clients send it as `Authorization: Bearer <token>`, `X-Admin-Token`,
# or (GET only, for dashboard links) `?token=...`. Required unless
# `insecure_no_auth = true`.
admin_token = ""
# `insecure_no_auth = true`. (The legacy key `admin_token` is still accepted.)
auth_token = ""
# Optional Apache htpasswd file for HTTP Basic authentication.
htpasswd_file = ""
# Development/testing escape hatch only. Never enable this on an admin
Expand Down
8 changes: 4 additions & 4 deletions dstack/gateway/src/admin_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ impl AdminAuthFairing {
http_config(),
)));
}
let token = if !config.admin_token.is_empty() {
config.admin_token.trim().to_owned()
let token = if !config.auth_token.is_empty() {
config.auth_token.trim().to_owned()
} else {
std::env::var(ENV_ADMIN_TOKEN)
.or_else(|_| std::env::var(ENV_ADMIN_TOKEN_COMPAT))
Expand All @@ -34,8 +34,8 @@ impl AdminAuthFairing {
};
if token.is_empty() && config.htpasswd_file.as_os_str().is_empty() {
bail!(
"admin API is enabled but neither admin_token nor htpasswd_file is configured; \
set core.admin.admin_token, {ENV_ADMIN_TOKEN}, {ENV_ADMIN_TOKEN_COMPAT}, \
"admin API is enabled but neither auth_token nor htpasswd_file is configured; \
set core.admin.auth_token, {ENV_ADMIN_TOKEN}, {ENV_ADMIN_TOKEN_COMPAT}, \
core.admin.htpasswd_file, or insecure_no_auth = true (testing only)"
);
}
Expand Down
23 changes: 21 additions & 2 deletions dstack/gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,10 @@ pub struct AdminConfig {
/// Shared secret required to call any admin endpoint (RPC + dashboard).
/// Can also be supplied via `DSTACK_GATEWAY_ADMIN_TOKEN` / `ADMIN_API_TOKEN`
/// env vars. Required unless `insecure_no_auth = true`.
#[serde(default)]
pub admin_token: String,
///
/// Accepts the legacy `admin_token` key for backward compatibility.
#[serde(default, alias = "admin_token")]
pub auth_token: String,
/// Optional Apache htpasswd file. Enables standard HTTP Basic auth while
/// preserving token authentication for existing clients.
#[serde(default)]
Expand Down Expand Up @@ -354,8 +356,25 @@ pub fn setup_wireguard(config: &WgConfig) -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
use rocket::figment::providers::{Format, Toml};
use std::str::FromStr;

#[test]
fn admin_auth_token_reads_new_and_legacy_keys() {
// new key
let cfg: AdminConfig =
Figment::from(Toml::string("enabled = true\nauth_token = \"new\"\n"))
.extract()
.unwrap();
assert_eq!(cfg.auth_token, "new");
// legacy `admin_token` key still deserializes via the serde alias
let cfg: AdminConfig =
Figment::from(Toml::string("enabled = true\nadmin_token = \"legacy\"\n"))
.extract()
.unwrap();
assert_eq!(cfg.auth_token, "legacy");
}

#[test]
fn test_validate() {
// Valid configuration
Expand Down
2 changes: 1 addition & 1 deletion dstack/gateway/test-run/e2e/configs/gateway-1.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ rpc_domain = "gateway-1"
enabled = true
port = 9016
address = "0.0.0.0"
admin_token = "e2e-admin-token"
auth_token = "e2e-admin-token"

[core.debug]
insecure_enable_debug_rpc = true
Expand Down
2 changes: 1 addition & 1 deletion dstack/gateway/test-run/e2e/configs/gateway-2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ rpc_domain = "gateway-2"
enabled = true
port = 9016
address = "0.0.0.0"
admin_token = "e2e-admin-token"
auth_token = "e2e-admin-token"

[core.debug]
insecure_enable_debug_rpc = true
Expand Down
2 changes: 1 addition & 1 deletion dstack/gateway/test-run/e2e/configs/gateway-3.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ rpc_domain = "gateway-3"
enabled = true
port = 9016
address = "0.0.0.0"
admin_token = "e2e-admin-token"
auth_token = "e2e-admin-token"

[core.debug]
insecure_enable_debug_rpc = true
Expand Down
2 changes: 1 addition & 1 deletion dstack/gateway/test-run/e2e/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ GATEWAY_PROXIES="gateway-1:9014 gateway-2:9014 gateway-3:9014"
GATEWAY_DEBUG_URLS="http://gateway-1:9015 http://gateway-2:9015 http://gateway-3:9015"
GATEWAY_ADMIN="http://gateway-1:9016"

# Must match `admin_token` in configs/gateway-*.toml
# Must match `auth_token` in configs/gateway-*.toml
ADMIN_TOKEN="e2e-admin-token"
ADMIN_AUTH_HEADER="Authorization: Bearer ${ADMIN_TOKEN}"

Expand Down
Loading