Skip to content

Commit adcffbd

Browse files
authored
cli: use connection token for CLI connections (microsoft#167426)
This uses a hash of the tunnel ID to create the connection token, which should be sufficient to resolve the issues. We also now publish the protocol version in the tunnel tags, since the connection token must be supplied in the resolver, which is before we start connecting to the tunnel. See microsoft/vscode-internalbacklog#3287
1 parent 316fb89 commit adcffbd

File tree

6 files changed

+75
-6
lines changed

6 files changed

+75
-6
lines changed

cli/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ url = "2.3"
4747
async-trait = "0.1"
4848
log = "0.4"
4949
const_format = "0.2"
50+
sha2 = "0.10"
51+
base64 = "0.13"
5052

5153
[build-dependencies]
5254
serde = { version = "1.0" }

cli/src/commands/tunnels.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
use async_trait::async_trait;
7+
use sha2::{Digest, Sha256};
78
use std::fmt;
89
use std::str::FromStr;
910
use sysinfo::{Pid, SystemExt};
@@ -18,6 +19,7 @@ use super::{
1819
CommandContext,
1920
};
2021

22+
use crate::tunnels::dev_tunnels::ActiveTunnel;
2123
use crate::{
2224
auth::Auth,
2325
log::{self, Logger},
@@ -234,11 +236,18 @@ pub async fn serve(ctx: CommandContext, gateway_args: TunnelServeArgs) -> Result
234236
serve_with_csa(paths, log, gateway_args, csa, None).await
235237
}
236238

239+
fn get_connection_token(tunnel: &ActiveTunnel) -> String {
240+
let mut hash = Sha256::new();
241+
hash.update(tunnel.id.as_bytes());
242+
let result = hash.finalize();
243+
base64::encode_config(result, base64::URL_SAFE_NO_PAD)
244+
}
245+
237246
async fn serve_with_csa(
238247
paths: LauncherPaths,
239248
log: Logger,
240249
gateway_args: TunnelServeArgs,
241-
csa: CodeServerArgs,
250+
mut csa: CodeServerArgs,
242251
shutdown_rx: Option<mpsc::UnboundedReceiver<ShutdownSignal>>,
243252
) -> Result<i32, AnyError> {
244253
// Intentionally read before starting the server. If the server updated and
@@ -256,6 +265,8 @@ async fn serve_with_csa(
256265
.await
257266
}?;
258267

268+
csa.connection_token = Some(get_connection_token(&tunnel));
269+
259270
let shutdown_tx = if let Some(tx) = shutdown_rx {
260271
tx
261272
} else {

cli/src/constants.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ pub const CONTROL_PORT: u16 = 31545;
1717
/// 1 - Initial protocol version
1818
/// 2 - Addition of `serve.compressed` property to control whether servermsg's
1919
/// are compressed bidirectionally.
20-
pub const PROTOCOL_VERSION: u32 = 2;
20+
/// 3 - The server's connection token is set to a SHA256 hash of the tunnel ID
21+
pub const PROTOCOL_VERSION: u32 = 3;
22+
23+
/// Prefix for the tunnel tag that includes the version.
24+
pub const PROTOCOL_VERSION_TAG_PREFIX: &str = "protocolv";
25+
/// Tag for the current protocol version, which is included in dev tunnels.
26+
pub const PROTOCOL_VERSION_TAG: &str = concatcp!("protocolv", PROTOCOL_VERSION);
2127

2228
pub const VSCODE_CLI_VERSION: Option<&'static str> = option_env!("VSCODE_CLI_VERSION");
2329
pub const VSCODE_CLI_AI_KEY: Option<&'static str> = option_env!("VSCODE_CLI_AI_KEY");

cli/src/tunnels/code_server.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@ impl<'a, Http: SimpleHttp + Send + Sync + Clone + 'static> ServerBuilder<'a, Htt
534534

535535
let mut cmd = self.get_base_command();
536536
cmd.arg("--start-server")
537-
.arg("--without-connection-token")
538537
.arg("--enable-remote-auto-shutdown")
539538
.arg(format!("--socket-path={}", socket.display()));
540539

cli/src/tunnels/dev_tunnels.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55
use crate::auth;
6-
use crate::constants::{CONTROL_PORT, TUNNEL_SERVICE_USER_AGENT};
6+
use crate::constants::{
7+
CONTROL_PORT, PROTOCOL_VERSION_TAG, PROTOCOL_VERSION_TAG_PREFIX, TUNNEL_SERVICE_USER_AGENT,
8+
};
79
use crate::state::{LauncherPaths, PersistedState};
810
use crate::util::errors::{
911
wrap, AnyError, DevTunnelError, InvalidTunnelName, TunnelCreationFailed, WrappedError,
@@ -138,6 +140,8 @@ pub struct DevTunnels {
138140
pub struct ActiveTunnel {
139141
/// Name of the tunnel
140142
pub name: String,
143+
/// Underlying dev tunnels ID
144+
pub id: String,
141145
manager: ActiveTunnelManager,
142146
}
143147

@@ -378,7 +382,7 @@ impl DevTunnels {
378382
preferred_name: Option<String>,
379383
use_random_name: bool,
380384
) -> Result<ActiveTunnel, AnyError> {
381-
let (tunnel, persisted) = match self.launcher_tunnel.load() {
385+
let (mut tunnel, persisted) = match self.launcher_tunnel.load() {
382386
Some(mut persisted) => {
383387
if let Some(name) = preferred_name {
384388
if persisted.name.ne(&name) {
@@ -404,6 +408,12 @@ impl DevTunnels {
404408
}
405409
};
406410

411+
if !tunnel.tags.iter().any(|t| t == PROTOCOL_VERSION_TAG) {
412+
tunnel = self
413+
.update_protocol_version_tag(tunnel, &HOST_TUNNEL_REQUEST_OPTIONS)
414+
.await?;
415+
}
416+
407417
let locator = TunnelLocator::try_from(&tunnel).unwrap();
408418
let host_token = get_host_token_from_tunnel(&tunnel);
409419

@@ -462,7 +472,11 @@ impl DevTunnels {
462472
let mut tried_recycle = false;
463473

464474
let new_tunnel = Tunnel {
465-
tags: vec![name.to_string(), VSCODE_CLI_TUNNEL_TAG.to_string()],
475+
tags: vec![
476+
name.to_string(),
477+
PROTOCOL_VERSION_TAG.to_string(),
478+
VSCODE_CLI_TUNNEL_TAG.to_string(),
479+
],
466480
..Default::default()
467481
};
468482

@@ -507,6 +521,40 @@ impl DevTunnels {
507521
}
508522
}
509523

524+
/// Ensures the tunnel contains a tag for the current PROTCOL_VERSION, and no
525+
/// other version tags.
526+
async fn update_protocol_version_tag(
527+
&self,
528+
tunnel: Tunnel,
529+
options: &TunnelRequestOptions,
530+
) -> Result<Tunnel, AnyError> {
531+
debug!(
532+
self.log,
533+
"Updating tunnel protocol version tag to {}", PROTOCOL_VERSION_TAG
534+
);
535+
let mut new_tags: Vec<String> = tunnel
536+
.tags
537+
.into_iter()
538+
.filter(|t| !t.starts_with(PROTOCOL_VERSION_TAG_PREFIX))
539+
.collect();
540+
new_tags.push(PROTOCOL_VERSION_TAG.to_string());
541+
542+
let tunnel_update = Tunnel {
543+
tags: new_tags,
544+
tunnel_id: tunnel.tunnel_id.clone(),
545+
cluster_id: tunnel.cluster_id.clone(),
546+
..Default::default()
547+
};
548+
549+
let result = spanf!(
550+
self.log,
551+
self.log.span("dev-tunnel.protocol-tag-update"),
552+
self.client.update_tunnel(&tunnel_update, options)
553+
);
554+
555+
result.map_err(|e| wrap(e, "tunnel tag update failed").into())
556+
}
557+
510558
/// Tries to delete an unused tunnel, and then creates a tunnel with the
511559
/// given `new_name`.
512560
async fn try_recycle_tunnel(&mut self) -> Result<bool, AnyError> {
@@ -690,6 +738,7 @@ impl DevTunnels {
690738

691739
Ok(ActiveTunnel {
692740
name: tunnel_details.name.clone(),
741+
id: tunnel_details.id.clone(),
693742
manager,
694743
})
695744
}

0 commit comments

Comments
 (0)