diff --git a/next.config.ts b/next.config.ts index a548b3c..abda98f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -14,6 +14,12 @@ const nextConfig: NextConfig = { images: { remotePatterns: [{ protocol: 'https', hostname: 'avatars.githubusercontent.com', pathname: '/u/**' }], }, + logging: { + incomingRequests: { + // Noisy route - VSC makes a lot of requests. + ignore: [/^\/api\/auth-vsc\//], + }, + }, } export default nextConfig diff --git a/nginx.conf.template b/nginx.conf.template index be2e29c..8b4c585 100644 --- a/nginx.conf.template +++ b/nginx.conf.template @@ -2,6 +2,8 @@ worker_processes auto; daemon off; pid $NGINX_PID_PATH; error_log $NGINX_ERROR_LOG_PATH; +# Run worker processes as root so that they can access root-owned Unix domain sockets. +user root; events { worker_connections 1024; diff --git a/src/app/admin/components/SessionRow.tsx b/src/app/admin/components/SessionRow.tsx index a09b197..977151a 100644 --- a/src/app/admin/components/SessionRow.tsx +++ b/src/app/admin/components/SessionRow.tsx @@ -23,7 +23,7 @@ export function SessionRow({ info }: { info: EditorSessionInfo }) { {info.projectName}
- port {info.port} + UUID {info.sessionId} diff --git a/src/lib/server/collabServer.ts b/src/lib/server/collabServer.ts index 9227d15..7049774 100644 --- a/src/lib/server/collabServer.ts +++ b/src/lib/server/collabServer.ts @@ -54,13 +54,15 @@ export class CollabServerHandle { // prettier-ignore [ ...BWRAP_ARGS, + // We don't need internet access. + '--unshare-net', '--ro-bind', getCollabServerDir(), '/workspace/.collab-server', - '--bind', this.socketDir, '/workspace/.collab-sockets', + '--bind', this.socketDir, '/workspace/.sockets/collab-server', // Mount project files as writable for the collaboration server. '--bind', this.projectDir, '/workspace/project', '/usr/bin/node', '/workspace/.collab-server/server.ts', - `/workspace/.collab-sockets/${COLLAB_SOCKET_FILENAME}`, + `/workspace/.sockets/collab-server/${COLLAB_SOCKET_FILENAME}`, '/workspace/project', ], { stdio: 'inherit' }, diff --git a/src/lib/server/editorSessions.ts b/src/lib/server/editorSessions.ts index 9d24c09..439c7c6 100644 --- a/src/lib/server/editorSessions.ts +++ b/src/lib/server/editorSessions.ts @@ -16,12 +16,8 @@ export interface EditorSessionInfo { ownerUsername: string projectId: string projectName: string - port: number } -const BASE_PORT = 3010 -const MAX_PORT = 3999 - export class EditorSessionManager { /** projectId ↦ [servers for that project] * @@ -42,8 +38,6 @@ export class EditorSessionManager { * Same invariant as in {@link vscServers}. */ private collabServers = new Map() - private availablePorts = new Set(Array.from({ length: MAX_PORT - BASE_PORT + 1 }, (_, i) => BASE_PORT + i)) - constructor() { this.vscServerEvents.addListener('close', s => { if ((this.vscServers.get(s.project.id) ?? []).length === 0) { @@ -81,20 +75,15 @@ export class EditorSessionManager { const projectSessions = this.vscServers.get(project.id) ?? [] let session = projectSessions.find(s => s.viewer.id === viewer.id) if (!session) { - const port = this.availablePorts.values().next().value - if (port === undefined) throw new Error('no available ports') - this.availablePorts.delete(port) - const projectDir = path.join(getWorkspacesDir(), owner.name, project.id) const collabServer = this.findCollabServer(project, projectDir) - session = new VscodeServerHandle(viewer, owner, project, projectDir, collabServer.socketDir, port) + session = new VscodeServerHandle(viewer, owner, project, projectDir, collabServer.socketDir) session.addDisposable(async () => { this.vscServers.set( project.id, (this.vscServers.get(project.id) ?? []).filter(s => s !== session), ) this.vscServerEvents.emit('close', session!) - this.availablePorts.add(port) }) // Insertion happens in same transaction as failed lookup (before any `await`) this.vscServers.set(project.id, [...(this.vscServers.get(project.id) ?? []), session]) @@ -154,7 +143,6 @@ export class EditorSessionManager { ownerUsername: project.user.name, projectId, projectName: project.name, - port: s.port, }) } } diff --git a/src/lib/server/vscodeServer.ts b/src/lib/server/vscodeServer.ts index c2d0965..1f9134a 100644 --- a/src/lib/server/vscodeServer.ts +++ b/src/lib/server/vscodeServer.ts @@ -65,6 +65,9 @@ async function waitForNginxRoute(path: string, timeoutMs = 10_000): Promise { + await fs.rm(this.socketDir, { recursive: true, force: true }) + }) + // Every user gets their own VSCode server configuration, and set of installed extensions. // Openvscode-server derives --user-data-dir and --extensions-dir from --server-data-dir: // https://github.com/gitpod-io/openvscode-server/blob/2bfb814c5215c51a10e80c2cb1b58ed91068ad8b/src/vs/server/node/server.main.ts @@ -193,7 +201,8 @@ export class VscodeServerHandle { // but users can still write files directly if needed. // Lake and other CLI tools do such writes. '--bind', this.projectDir, sandboxProjectDir, - '--bind', this.collabSocketDir, '/workspace/.collab-sockets', + '--bind', this.collabSocketDir, '/workspace/.sockets/collab-server', + '--bind', this.socketDir, '/workspace/.sockets/openvscode-server', ...overlayArgs, '--setenv', 'HOME', '/workspace', '--setenv', 'ELAN_HOME', '/workspace/.elan', @@ -208,8 +217,7 @@ export class VscodeServerHandle { ...devArgs, '--', '/workspace/.openvscode-server/bin/openvscode-server', - '--host', '127.0.0.1', - '--port', String(this.port), + '--socket-path', `/workspace/.sockets/openvscode-server/${VSCODE_SOCKET_FILENAME}`, '--without-connection-token', `--server-base-path=${this.vscodeIframePath}`, '--server-data-dir', '/workspace/.vscode-remote',