Skip to content

Commit 49fbaf6

Browse files
authored
Support flexible auth user (#100)
1 parent 19d1779 commit 49fbaf6

File tree

6 files changed

+96
-731
lines changed

6 files changed

+96
-731
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@
441441
"@gitpod/gitpod-protocol": "main-gha",
442442
"@gitpod/local-app-api-grpcweb": "main-gha",
443443
"@gitpod/public-api": "main-gha",
444-
"@gitpod/supervisor-api-grpc": "main-gha",
444+
"@gitpod/supervisor-api-grpcweb": "main-gha",
445445
"@improbable-eng/grpc-web-node-http-transport": "^0.14.0",
446446
"@microsoft/dev-tunnels-ssh": "^3.11.24",
447447
"@microsoft/dev-tunnels-ssh-keys": "^3.11.24",

src/local-ssh/ipc/extensionServiceServer.ts

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { ExtensionServiceDefinition, ExtensionServiceImplementation, GetWorkspaceAuthInfoRequest, GetWorkspaceAuthInfoResponse, PingRequest, SendErrorReportRequest, SendLocalSSHUserFlowStatusRequest } from '../../proto/typescript/ipc/v1/ipc';
6+
import { ExtensionServiceDefinition, ExtensionServiceImplementation, GetWorkspaceAuthInfoRequest, GetWorkspaceAuthInfoResponse, PingRequest } from '../../proto/typescript/ipc/v1/ipc';
77
import { Disposable } from '../../common/dispose';
88
import { ILogService } from '../../services/logService';
99
import { ISessionService } from '../../services/sessionService';
1010
import { CallContext, ServerError, Status } from 'nice-grpc-common';
1111
import { IHostService } from '../../services/hostService';
1212
import { Server, createChannel, createClient, createServer } from 'nice-grpc';
13-
import { ITelemetryService, UserFlowTelemetryProperties } from '../../common/telemetry';
13+
import { ITelemetryService } from '../../common/telemetry';
1414
import { Configuration } from '../../configuration';
1515
import { timeout } from '../../common/async';
1616
import { BrowserHeaders } from 'browser-headers';
1717
import { ControlServiceClient, ServiceError } from '@gitpod/supervisor-api-grpcweb/lib/control_pb_service';
1818
import { NodeHttpTransport } from '@improbable-eng/grpc-web-node-http-transport';
19-
import { CreateSSHKeyPairRequest } from '@gitpod/supervisor-api-grpcweb/lib/control_pb';
19+
import { CreateSSHKeyPairRequest, CreateSSHKeyPairResponse } from '@gitpod/supervisor-api-grpcweb/lib/control_pb';
2020
import * as ssh2 from 'ssh2';
2121
import { ParsedKey } from 'ssh2-streams';
2222
import { WrapError } from '../../common/utils';
@@ -66,15 +66,15 @@ class ExtensionServiceImpl implements ExtensionServiceImplementation {
6666
private async getWorkspaceSSHKey(ownerToken: string, workspaceUrl: string, signal: AbortSignal) {
6767
const url = new URL(workspaceUrl);
6868
url.pathname = '/_supervisor/v1';
69-
const privateKey = await wrapSupervisorAPIError(() => new Promise<string>((resolve, reject) => {
69+
const { privateKey, userName } = await wrapSupervisorAPIError(() => new Promise<CreateSSHKeyPairResponse.AsObject>((resolve, reject) => {
7070
const metadata = new BrowserHeaders();
7171
metadata.append('x-gitpod-owner-token', ownerToken);
7272
const client = new ControlServiceClient(url.toString(), { transport: NodeHttpTransport() });
7373
client.createSSHKeyPair(new CreateSSHKeyPairRequest(), metadata, (err, resp) => {
7474
if (err) {
7575
return reject(err);
7676
}
77-
resolve(resp!.toObject().privateKey);
77+
resolve(resp!.toObject());
7878
});
7979
}), { signal });
8080

@@ -83,7 +83,7 @@ class ExtensionServiceImpl implements ExtensionServiceImplementation {
8383
throw new Error('Error while parsing workspace SSH private key');
8484
}
8585

86-
return (parsedResult as ParsedKey).getPrivatePEM();
86+
return { sshkey: (parsedResult as ParsedKey).getPrivatePEM(), username: userName };
8787
}
8888

8989
async getWorkspaceAuthInfo(request: GetWorkspaceAuthInfoRequest, _context: CallContext): Promise<GetWorkspaceAuthInfoResponse> {
@@ -126,6 +126,7 @@ class ExtensionServiceImpl implements ExtensionServiceImplementation {
126126
let ownerToken = '';
127127
let workspaceHost = '';
128128
let sshkey = '';
129+
let username = '';
129130
if (wsData.phase === 'running') {
130131
ownerToken = await this.sessionService.getAPI().getOwnerToken(actualWorkspaceId, _context.signal);
131132

@@ -137,7 +138,7 @@ class ExtensionServiceImpl implements ExtensionServiceImplementation {
137138
actualWorkspaceUrl = actualWorkspaceUrl.replace(actualWorkspaceId, workspaceId);
138139
}
139140

140-
sshkey = await this.getWorkspaceSSHKey(ownerToken, actualWorkspaceUrl, _context.signal);
141+
({ sshkey, username } = await this.getWorkspaceSSHKey(ownerToken, actualWorkspaceUrl, _context.signal));
141142
}
142143

143144
return {
@@ -149,6 +150,7 @@ class ExtensionServiceImpl implements ExtensionServiceImplementation {
149150
ownerToken,
150151
sshkey,
151152
phase: wsData.phase,
153+
username,
152154
};
153155
} catch (e) {
154156
let code = Status.INTERNAL;
@@ -169,41 +171,6 @@ class ExtensionServiceImpl implements ExtensionServiceImplementation {
169171
}
170172
}
171173

172-
// TODO remove from protocol, don't pass sensitive info back and forth, only once for auth, daemon should do telemetry directly
173-
async sendLocalSSHUserFlowStatus(request: SendLocalSSHUserFlowStatusRequest, _context: CallContext): Promise<{}> {
174-
if (!request.flowStatus || request.flowStatus === '') {
175-
return {};
176-
}
177-
const flow: UserFlowTelemetryProperties = {
178-
flow: 'local_ssh',
179-
workspaceId: request.workspaceId,
180-
instanceId: request.instanceId,
181-
daemonVersion: request.daemonVersion,
182-
userId: request.userId,
183-
gitpodHost: request.gitpodHost,
184-
failureCode: request.flowFailureCode,
185-
};
186-
this.telemetryService.sendUserFlowStatus(request.flowStatus, flow);
187-
return {};
188-
}
189-
190-
// TODO remove from protocol, don't pass sensitive info back and forth, only once for auth, daemon should do telemetry directly
191-
// local ssh daemon should be own component in reporting?
192-
async sendErrorReport(request: SendErrorReportRequest, _context: CallContext): Promise<{}> {
193-
const err = new Error(request.errorMessage);
194-
err.name = `${request.errorName}[local-ssh]`;
195-
err.stack = request.errorStack;
196-
this.telemetryService.sendTelemetryException(err, {
197-
gitpodHost: request.gitpodHost,
198-
workspaceId: request.workspaceId,
199-
instanceId: request.instanceId,
200-
daemonVersion: request.daemonVersion,
201-
extensionVersion: request.extensionVersion,
202-
userId: request.userId,
203-
});
204-
return {};
205-
}
206-
207174
async ping(_request: PingRequest, _context: CallContext): Promise<{}> {
208175
return {};
209176
}

src/local-ssh/proxy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ class WebSocketSSHProxy {
344344

345345
await session.connect(stream);
346346

347-
const ok = await session.authenticate({ username: 'gitpod', publicKeys: [await importKey(workspaceInfo.sshkey)] });
347+
const ok = await session.authenticate({ username: workspaceInfo.username || 'gitpod', publicKeys: [await importKey(workspaceInfo.sshkey)] });
348348
if (!ok) {
349349
throw new FailedToProxyError('TUNNEL.AuthenticateSSHKeyFailed');
350350
}

src/proto/ipc/v1/ipc.proto

Lines changed: 1 addition & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,76 +4,12 @@ package ipc.v1;
44

55
service ExtensionService {
66
rpc GetWorkspaceAuthInfo (GetWorkspaceAuthInfoRequest) returns (GetWorkspaceAuthInfoResponse) {}
7-
rpc SendLocalSSHUserFlowStatus (SendLocalSSHUserFlowStatusRequest) returns (SendLocalSSHUserFlowStatusResponse) {}
8-
rpc SendErrorReport (SendErrorReportRequest) returns (SendErrorReportResponse) {}
97
rpc Ping (PingRequest) returns (PingResponse) {}
108
}
119

1210
message PingRequest {}
1311
message PingResponse {}
1412

15-
message SendErrorReportRequest {
16-
string workspace_id = 1;
17-
string instance_id = 2;
18-
string error_name = 3;
19-
string error_message = 4;
20-
string error_stack = 5;
21-
string daemon_version = 6;
22-
string extension_version = 7;
23-
string gitpod_host = 8;
24-
string user_id = 9;
25-
}
26-
27-
message SendErrorReportResponse {}
28-
29-
message SendLocalSSHUserFlowStatusRequest {
30-
31-
enum ConnType {
32-
CONN_TYPE_UNSPECIFIED = 0;
33-
CONN_TYPE_SSH = 1;
34-
CONN_TYPE_TUNNEL = 2;
35-
}
36-
37-
enum Status {
38-
STATUS_UNSPECIFIED = 0;
39-
STATUS_SUCCESS = 1;
40-
STATUS_FAILURE = 2;
41-
}
42-
43-
enum Code {
44-
CODE_UNSPECIFIED = 0;
45-
// CODE_NO_WORKSPACE_AUTO_INFO is used if failed to get workspace auto info
46-
CODE_NO_WORKSPACE_AUTO_INFO = 1;
47-
// CODE_TUNNEL_CANNOT_CREATE_WEBSOCKET is used if failed to create websocket to supervisor
48-
CODE_TUNNEL_CANNOT_CREATE_WEBSOCKET = 2;
49-
// CODE_TUNNEL_FAILED_FORWARD_SSH_PORT is used if failed to forward ssh port in supervisor
50-
CODE_TUNNEL_FAILED_FORWARD_SSH_PORT = 3;
51-
// CODE_TUNNEL_NO_PRIVATEKEY when failed to create private key in supervisor
52-
CODE_TUNNEL_NO_PRIVATEKEY = 4;
53-
// CODE_TUNNEL_NO_ESTABLISHED_CONNECTION is used if the tunnel is not established
54-
CODE_TUNNEL_NO_ESTABLISHED_CONNECTION = 5;
55-
// CODE_SSH_CANNOT_CONNECT is used if failed to direct connect to ssh gateway
56-
CODE_SSH_CANNOT_CONNECT = 6;
57-
}
58-
59-
Status status = 1;
60-
string workspace_id = 2;
61-
string instance_id = 3;
62-
Code failure_code = 5;
63-
// DEPRECATED string failure_reason = 6
64-
reserved 6;
65-
string daemon_version = 7;
66-
// DEPRECATED string extension_version = 8
67-
reserved 8;
68-
ConnType conn_type = 9;
69-
string gitpod_host = 10;
70-
string user_id = 11;
71-
string flow_status = 12;
72-
string flow_failure_code = 13;
73-
}
74-
75-
message SendLocalSSHUserFlowStatusResponse {}
76-
7713
message GetWorkspaceAuthInfoRequest {
7814
string workspace_id = 1;
7915
string gitpod_host = 2;
@@ -88,4 +24,5 @@ message GetWorkspaceAuthInfoResponse {
8824
string user_id = 6;
8925
string sshkey = 7;
9026
string phase = 8;
27+
string username = 9;
9128
}

0 commit comments

Comments
 (0)