fix(security): remove insecure default secret from GOOSE_EXTERNAL_BACKEND (#7783)

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
jh-block
2026-03-10 16:27:37 +01:00
committed by GitHub
parent a191f0d958
commit b6d6984125
4 changed files with 30 additions and 5 deletions
+1
View File
@@ -144,6 +144,7 @@ debug-ui *alpha:
@echo "🚀 Starting goose frontend in external backend mode{{ if alpha == "alpha" { " with alpha features enabled" } else { "" } }}"
cd ui/desktop && \
export GOOSE_EXTERNAL_BACKEND=true && \
export GOOSE_SERVER__SECRET_KEY="${GOOSE_SERVER__SECRET_KEY:-test}" && \
{{ if alpha == "alpha" { "export ALPHA=true &&" } else { "" } }} \
npm ci && \
npm run {{ if alpha == "alpha" { "start-alpha-gui" } else { "start-gui" } }}
+9 -2
View File
@@ -37,11 +37,18 @@ pub async fn run() -> Result<()> {
let settings = configuration::Settings::new()?;
let secret_key =
std::env::var("GOOSE_SERVER__SECRET_KEY").unwrap_or_else(|_| "test".to_string());
let secret_key = std::env::var("GOOSE_SERVER__SECRET_KEY")
.unwrap_or_else(|_| hex::encode(rand::random::<[u8; 32]>()));
let app_state = state::AppState::new(settings.tls).await?;
// Share the server secret with the tunnel manager so it uses the same
// key for forwarded requests, without mutating the process environment.
app_state
.tunnel_manager
.set_server_secret(secret_key.clone())
.await;
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
+13 -2
View File
@@ -89,6 +89,7 @@ pub struct TunnelManager {
watchdog_handle: Arc<RwLock<Option<tokio::task::JoinHandle<()>>>>,
lock_file: Arc<std::sync::Mutex<Option<File>>>,
scheme: String,
server_secret: Arc<RwLock<Option<String>>>,
}
impl Default for TunnelManager {
@@ -107,6 +108,7 @@ impl TunnelManager {
watchdog_handle: Arc::new(RwLock::new(None)),
lock_file: Arc::new(std::sync::Mutex::new(None)),
scheme: if tls { "https" } else { "http" }.to_string(),
server_secret: Arc::new(RwLock::new(None)),
}
}
@@ -192,6 +194,10 @@ impl TunnelManager {
}
}
pub async fn set_server_secret(&self, secret: String) {
*self.server_secret.write().await = Some(secret);
}
pub fn set_auto_start(auto_start: bool) -> anyhow::Result<()> {
Config::global()
.set_param("tunnel_auto_start", auto_start)
@@ -213,8 +219,12 @@ impl TunnelManager {
async fn start_tunnel_internal(&self) -> anyhow::Result<(TunnelInfo, mpsc::Receiver<()>)> {
let server_port = get_server_port()?;
let tunnel_secret = Self::get_secret().unwrap_or_else(generate_secret);
let server_secret =
std::env::var("GOOSE_SERVER__SECRET_KEY").unwrap_or_else(|_| "test".to_string());
let server_secret = self
.server_secret
.read()
.await
.clone()
.expect("server_secret must be set before starting tunnel");
let agent_id = Self::get_agent_id().unwrap_or_else(generate_agent_id);
Self::set_secret(&tunnel_secret)?;
@@ -317,6 +327,7 @@ impl TunnelManager {
watchdog_handle: self.watchdog_handle.clone(),
lock_file: self.lock_file.clone(),
scheme: self.scheme.clone(),
server_secret: self.server_secret.clone(),
}
}
+7 -1
View File
@@ -511,7 +511,13 @@ const getServerSecret = (settings: Settings): string => {
return settings.externalGoosed.secret;
}
if (process.env.GOOSE_EXTERNAL_BACKEND) {
return 'test';
if (!process.env.GOOSE_SERVER__SECRET_KEY) {
throw new Error(
'GOOSE_SERVER__SECRET_KEY must be set when using GOOSE_EXTERNAL_BACKEND. ' +
'Set it to the same value on both the server and the desktop client.'
);
}
return process.env.GOOSE_SERVER__SECRET_KEY;
}
return GENERATED_SECRET;
};