diff --git a/cs/src/Connections/TunnelRelayTunnelHost.cs b/cs/src/Connections/TunnelRelayTunnelHost.cs index 288a7293..0de92535 100644 --- a/cs/src/Connections/TunnelRelayTunnelHost.cs +++ b/cs/src/Connections/TunnelRelayTunnelHost.cs @@ -130,12 +130,10 @@ protected override async Task CreateTunnelConnectorAsync(Cance Requires.Argument(this.accessToken != null, nameof(Tunnel), $"There is no access token for {TunnelAccessScope} scope on the tunnel."); var hostPublicKey = HostPrivateKey.GetPublicKeyBytes(HostPrivateKey.KeyAlgorithmName).ToBase64(); - var tunnelHasSshPort = Tunnel.Ports != null && - Tunnel.Ports.Any((p) => p.Protocol == TunnelProtocol.Ssh); var endpointSignature = $"{Tunnel.TunnelId}.{Tunnel.ClusterId}:" + $"{Tunnel.Name}.{Tunnel.Domain}:" + - $"{tunnelHasSshPort}:{this.hostId}:{hostPublicKey}"; + $"{this.hostId}:{hostPublicKey}"; if (!string.Equals(endpointSignature, EndpointSignature, StringComparison.OrdinalIgnoreCase) || RelayUri == null) @@ -147,19 +145,10 @@ protected override async Task CreateTunnelConnectorAsync(Cance HostPublicKeys = new[] { hostPublicKey }, }; - List>? additionalQueryParams = null; - if (tunnelHasSshPort) - { - additionalQueryParams = new () {new KeyValuePair("includeSshGatewayPublicKey", "true")}; - } - endpoint = (TunnelRelayTunnelEndpoint)await ManagementClient!.UpdateTunnelEndpointAsync( Tunnel, endpoint, - options: new TunnelRequestOptions() - { - AdditionalQueryParameters = additionalQueryParams, - }, + options: null, cancellation); EndpointSignature = endpointSignature; diff --git a/cs/src/Contracts/TunnelPort.cs b/cs/src/Contracts/TunnelPort.cs index 34ee515f..b7b5f9aa 100644 --- a/cs/src/Contracts/TunnelPort.cs +++ b/cs/src/Contracts/TunnelPort.cs @@ -92,7 +92,7 @@ public TunnelPort() /// /// Selection of a default port for a connection also depends on matching the connection to the /// port , so it is possible to configure separate defaults for distinct - /// protocols like and . + /// protocols like and . /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IsDefault { get; set; } @@ -134,7 +134,7 @@ public TunnelPort() /// Gets or sets the username for the ssh service user is trying to forward. /// /// - /// Should be provided if the is Ssh. + /// This property is retained for compatibility with legacy SSH tunnel ports. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [StringLength(SshUserMaxLength)] diff --git a/cs/src/Contracts/TunnelProtocol.cs b/cs/src/Contracts/TunnelProtocol.cs index 696297c9..d13e071b 100644 --- a/cs/src/Contracts/TunnelProtocol.cs +++ b/cs/src/Contracts/TunnelProtocol.cs @@ -27,11 +27,6 @@ public static class TunnelProtocol /// public const string Udp = "udp"; - /// - /// SSH protocol. - /// - public const string Ssh = "ssh"; - /// /// Remote desktop protocol. /// diff --git a/cs/test/TunnelsSDK.Test/Mocks/MockTunnelManagementClient.cs b/cs/test/TunnelsSDK.Test/Mocks/MockTunnelManagementClient.cs index 2532518c..abd954fb 100644 --- a/cs/test/TunnelsSDK.Test/Mocks/MockTunnelManagementClient.cs +++ b/cs/test/TunnelsSDK.Test/Mocks/MockTunnelManagementClient.cs @@ -121,6 +121,7 @@ public Task UpdateTunnelEndpointAsync( CancellationToken cancellation = default) { TunnelEndpointsUpdated++; + LastTunnelEndpointUpdateOptions = options; tunnel.Endpoints ??= Array.Empty(); for (int i = 0; i < tunnel.Endpoints.Length; i++) @@ -153,6 +154,8 @@ public Task UpdateTunnelEndpointAsync( public int TunnelEndpointsUpdated { get; private set; } + public TunnelRequestOptions LastTunnelEndpointUpdateOptions { get; private set; } + public Task DeleteTunnelEndpointsAsync( Tunnel tunnel, string Id, diff --git a/cs/test/TunnelsSDK.Test/TunnelHostAndClientTests.cs b/cs/test/TunnelsSDK.Test/TunnelHostAndClientTests.cs index 576ba93f..df21e6cc 100644 --- a/cs/test/TunnelsSDK.Test/TunnelHostAndClientTests.cs +++ b/cs/test/TunnelsSDK.Test/TunnelHostAndClientTests.cs @@ -714,6 +714,35 @@ public async Task ConnectRelayHost() await clientSshSession.ConnectAsync(clientRelayStream, TestContext.Current.CancellationToken); } + [Fact] + public async Task ConnectRelayHostWithLegacySshPortDoesNotRequestSshGatewayKey() + { + var managementClient = new MockTunnelManagementClient + { + HostRelayUri = MockHostRelayUri, + }; + var relayHost = new TunnelRelayTunnelHost(managementClient, TestTS); + var tunnel = CreateRelayTunnel(); + tunnel.Ports = + [ + new TunnelPort + { + PortNumber = 22, + Protocol = "ssh", + }, + ]; + + using var serverSshSession = await ConnectRelayHostAsync( + relayHost, + tunnel, + cancellation: TestContext.Current.CancellationToken); + + Assert.Equal(1, managementClient.TunnelEndpointsUpdated); + Assert.Null(managementClient.LastTunnelEndpointUpdateOptions); + + await relayHost.DisposeAsync(); + } + [Fact] public async Task ConnectRelayHostAfterDisconnect() { diff --git a/go/tunnels/tunnel_port.go b/go/tunnels/tunnel_port.go index da25d172..11582dc5 100644 --- a/go/tunnels/tunnel_port.go +++ b/go/tunnels/tunnel_port.go @@ -39,7 +39,7 @@ type TunnelPort struct { // // Selection of a default port for a connection also depends on matching the connection // to the port `TunnelPort.Protocol`, so it is possible to configure separate defaults - // for distinct protocols like `TunnelProtocol.Http` and `TunnelProtocol.Ssh`. + // for distinct protocols like `TunnelProtocol.Http` and `TunnelProtocol.Tcp`. IsDefault bool `json:"isDefault,omitempty"` // Gets or sets a dictionary mapping from scopes to tunnel access tokens. @@ -61,7 +61,7 @@ type TunnelPort struct { // Gets or sets the username for the ssh service user is trying to forward. // - // Should be provided if the `TunnelProtocol` is Ssh. + // This property is retained for compatibility with legacy SSH tunnel ports. SshUser string `json:"sshUser,omitempty"` // Gets or sets web forwarding URIs. If set, it's a list of absolute URIs where the port diff --git a/go/tunnels/tunnel_protocol.go b/go/tunnels/tunnel_protocol.go index d68c4058..6305a76a 100644 --- a/go/tunnels/tunnel_protocol.go +++ b/go/tunnels/tunnel_protocol.go @@ -17,9 +17,6 @@ const ( // Unknown UDP protocol. TunnelProtocolUdp TunnelProtocol = "udp" - // SSH protocol. - TunnelProtocolSsh TunnelProtocol = "ssh" - // Remote desktop protocol. TunnelProtocolRdp TunnelProtocol = "rdp" diff --git a/go/tunnels/tunnels.go b/go/tunnels/tunnels.go index 5d89436a..61c4f390 100644 --- a/go/tunnels/tunnels.go +++ b/go/tunnels/tunnels.go @@ -10,7 +10,7 @@ import ( "github.com/rodaine/table" ) -const PackageVersion = "0.2.0" +const PackageVersion = "0.2.1" func (tunnel *Tunnel) requestObject() (*Tunnel, error) { convertedTunnel := &Tunnel{ diff --git a/java/src/main/java/com/microsoft/tunnels/contracts/TunnelPort.java b/java/src/main/java/com/microsoft/tunnels/contracts/TunnelPort.java index 51f2a4e1..7689b8b4 100644 --- a/java/src/main/java/com/microsoft/tunnels/contracts/TunnelPort.java +++ b/java/src/main/java/com/microsoft/tunnels/contracts/TunnelPort.java @@ -67,7 +67,7 @@ public class TunnelPort { * Selection of a default port for a connection also depends on matching the * connection to the port {@link TunnelPort#protocol}, so it is possible to configure * separate defaults for distinct protocols like {@link TunnelProtocol#http} and - * {@link TunnelProtocol#ssh}. + * {@link TunnelProtocol#tcp}. */ @Expose public boolean isDefault; @@ -105,7 +105,7 @@ public class TunnelPort { /** * Gets or sets the username for the ssh service user is trying to forward. * - * Should be provided if the {@link TunnelProtocol} is Ssh. + * This property is retained for compatibility with legacy SSH tunnel ports. */ @Expose public String sshUser; diff --git a/java/src/main/java/com/microsoft/tunnels/contracts/TunnelProtocol.java b/java/src/main/java/com/microsoft/tunnels/contracts/TunnelProtocol.java index 483e2f64..291331b7 100644 --- a/java/src/main/java/com/microsoft/tunnels/contracts/TunnelProtocol.java +++ b/java/src/main/java/com/microsoft/tunnels/contracts/TunnelProtocol.java @@ -23,11 +23,6 @@ public class TunnelProtocol { */ public static final String udp = "udp"; - /** - * SSH protocol. - */ - public static final String ssh = "ssh"; - /** * Remote desktop protocol. */ diff --git a/rs/src/contracts/tunnel_port.rs b/rs/src/contracts/tunnel_port.rs index fecb2b8b..08d03534 100644 --- a/rs/src/contracts/tunnel_port.rs +++ b/rs/src/contracts/tunnel_port.rs @@ -52,7 +52,7 @@ pub struct TunnelPort { // Selection of a default port for a connection also depends on matching the // connection to the port `TunnelPort.Protocol`, so it is possible to configure // separate defaults for distinct protocols like `TunnelProtocol.Http` and - // `TunnelProtocol.Ssh`. + // `TunnelProtocol.Tcp`. #[serde(default)] pub is_default: bool, @@ -79,7 +79,7 @@ pub struct TunnelPort { // Gets or sets the username for the ssh service user is trying to forward. // - // Should be provided if the `TunnelProtocol` is Ssh. + // This property is retained for compatibility with legacy SSH tunnel ports. #[serde(skip_serializing_if = "Option::is_none")] pub ssh_user: Option, diff --git a/rs/src/contracts/tunnel_protocol.rs b/rs/src/contracts/tunnel_protocol.rs index d0ca39a8..18a86952 100644 --- a/rs/src/contracts/tunnel_protocol.rs +++ b/rs/src/contracts/tunnel_protocol.rs @@ -13,9 +13,6 @@ pub const TUNNEL_PROTOCOL_TCP: &str = r#"tcp"#; // Unknown UDP protocol. pub const TUNNEL_PROTOCOL_UDP: &str = r#"udp"#; -// SSH protocol. -pub const TUNNEL_PROTOCOL_SSH: &str = r#"ssh"#; - // Remote desktop protocol. pub const TUNNEL_PROTOCOL_RDP: &str = r#"rdp"#; diff --git a/ts/src/connections/tunnelRelayTunnelHost.ts b/ts/src/connections/tunnelRelayTunnelHost.ts index 715fc073..03d2a2fc 100644 --- a/ts/src/connections/tunnelRelayTunnelHost.ts +++ b/ts/src/connections/tunnelRelayTunnelHost.ts @@ -3,7 +3,6 @@ import { TunnelConnectionMode, - TunnelProtocol, TunnelRelayTunnelEndpoint, TunnelPort, Tunnel, @@ -284,11 +283,10 @@ export class TunnelRelayTunnelHost extends TunnelConnectionSession implements Tu this.hostPublicKeys = [buffer.toString('base64')]; } - const tunnelHasSshPort = this.tunnel?.ports != null && this.tunnel.ports.find((v) => v.protocol === TunnelProtocol.Ssh); const endpointSignature = `${this.tunnel?.tunnelId}.${this.tunnel?.clusterId}:` + `${this.tunnel?.name}.${this.tunnel?.domain}:` + - `${tunnelHasSshPort}:${this.hostId}:${this.hostPublicKeys}`; + `${this.hostId}:${this.hostPublicKeys}`; if (!this.relayUri || this.endpointSignature !== endpointSignature) { if (!this.tunnel) { @@ -302,14 +300,7 @@ export class TunnelRelayTunnelHost extends TunnelConnectionSession implements Tu connectionMode: TunnelConnectionMode.TunnelRelay, }; - let additionalQueryParameters = undefined; - if (tunnelHasSshPort) { - additionalQueryParameters = { includeSshGatewayPublicKey: 'true' }; - } - - endpoint = await this.managementClient!.updateTunnelEndpoint(this.tunnel, endpoint, { - additionalQueryParameters: additionalQueryParameters, - }); + endpoint = await this.managementClient!.updateTunnelEndpoint(this.tunnel, endpoint); this.relayUri = endpoint.hostRelayUri!; this.endpointSignature = endpointSignature; diff --git a/ts/src/contracts/tunnelPort.ts b/ts/src/contracts/tunnelPort.ts index 4c5080c2..5051aee1 100644 --- a/ts/src/contracts/tunnelPort.ts +++ b/ts/src/contracts/tunnelPort.ts @@ -60,7 +60,7 @@ export interface TunnelPort { * Selection of a default port for a connection also depends on matching the * connection to the port {@link TunnelPort.protocol}, so it is possible to configure * separate defaults for distinct protocols like {@link TunnelProtocol.http} and - * {@link TunnelProtocol.ssh}. + * {@link TunnelProtocol.tcp}. */ isDefault?: boolean; @@ -93,7 +93,7 @@ export interface TunnelPort { /** * Gets or sets the username for the ssh service user is trying to forward. * - * Should be provided if the {@link TunnelProtocol} is Ssh. + * This property is retained for compatibility with legacy SSH tunnel ports. */ sshUser?: string; diff --git a/ts/src/contracts/tunnelProtocol.ts b/ts/src/contracts/tunnelProtocol.ts index 3a414704..1e03c744 100644 --- a/ts/src/contracts/tunnelProtocol.ts +++ b/ts/src/contracts/tunnelProtocol.ts @@ -22,11 +22,6 @@ export enum TunnelProtocol { */ Udp = 'udp', - /** - * SSH protocol. - */ - Ssh = 'ssh', - /** * Remote desktop protocol. */ diff --git a/ts/test/tunnels-test/mocks/mockTunnelManagementClient.ts b/ts/test/tunnels-test/mocks/mockTunnelManagementClient.ts index a690472d..0ba23d3d 100644 --- a/ts/test/tunnels-test/mocks/mockTunnelManagementClient.ts +++ b/ts/test/tunnels-test/mocks/mockTunnelManagementClient.ts @@ -143,6 +143,7 @@ export class MockTunnelManagementClient implements TunnelManagementClient { } public tunnelEndpointsUpdated: number = 0; + public lastTunnelEndpointUpdateOptions?: TunnelRequestOptions; updateTunnelEndpoint( tunnel: Tunnel, @@ -150,6 +151,7 @@ export class MockTunnelManagementClient implements TunnelManagementClient { options?: TunnelRequestOptions, ): Promise { this.tunnelEndpointsUpdated++; + this.lastTunnelEndpointUpdateOptions = options; if (!tunnel.endpoints) { tunnel.endpoints = []; } diff --git a/ts/test/tunnels-test/tunnelHostAndClientTests.ts b/ts/test/tunnels-test/tunnelHostAndClientTests.ts index c74f5196..e02d9c3d 100644 --- a/ts/test/tunnels-test/tunnelHostAndClientTests.ts +++ b/ts/test/tunnels-test/tunnelHostAndClientTests.ts @@ -769,6 +769,22 @@ export class TunnelHostAndClientTests { assert.strictEqual(relayHost.connectionStatus, ConnectionStatus.Disconnected); } + @test + public async connectRelayHostWithLegacySshPortDoesNotRequestSshGatewayKey() { + const managementClient = new MockTunnelManagementClient(); + managementClient.hostRelayUri = this.mockHostRelayUri; + const relayHost = new TunnelRelayTunnelHost(managementClient); + const tunnel = this.createRelayTunnel(); + tunnel.ports = [{ portNumber: 22, protocol: 'ssh' }]; + + await this.connectRelayHost({ relayHost, tunnel }); + + assert.equal(1, managementClient.tunnelEndpointsUpdated); + assert.strictEqual(managementClient.lastTunnelEndpointUpdateOptions, undefined); + + await relayHost.dispose(); + } + @test public async connectRelayHostAfterDisconnect() { const managementClient = new MockTunnelManagementClient();