mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(docker): auto-bind container ports to host ports (#17307)
Drive-by: make sure docker container does not expose ports on `0.0.0.0` and instead registers to localhost. This way websocket and vnc ports are not exposed to the public internet.
This commit is contained in:
parent
462fa7d79d
commit
705bc28e92
@ -96,18 +96,31 @@ interface ContainerInfo {
|
||||
}
|
||||
|
||||
export async function containerInfo(): Promise<ContainerInfo|undefined> {
|
||||
const containerId = await findRunningDockerContainerId();
|
||||
if (!containerId)
|
||||
const container = await findRunningDockerContainer();
|
||||
if (!container)
|
||||
return undefined;
|
||||
const logLines = await dockerApi.getContainerLogs(containerId);
|
||||
const logLines = await dockerApi.getContainerLogs(container.containerId);
|
||||
|
||||
const containerUrlToHostUrl = (address: string) => {
|
||||
const url = new URL(address);
|
||||
const portBinding = container.portBindings.find(binding => binding.containerPort === +url.port);
|
||||
if (!portBinding)
|
||||
return undefined;
|
||||
|
||||
url.host = portBinding.ip;
|
||||
url.port = portBinding.hostPort + '';
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
const WS_LINE_PREFIX = 'Listening on ws://';
|
||||
const webSocketLine = logLines.find(line => line.startsWith(WS_LINE_PREFIX));
|
||||
const NOVNC_LINE_PREFIX = 'novnc is listening on ';
|
||||
const novncLine = logLines.find(line => line.startsWith(NOVNC_LINE_PREFIX));
|
||||
return novncLine && webSocketLine ? {
|
||||
wsEndpoint: 'ws://' + webSocketLine.substring(WS_LINE_PREFIX.length),
|
||||
vncSession: novncLine.substring(NOVNC_LINE_PREFIX.length),
|
||||
} : undefined;
|
||||
if (!novncLine || !webSocketLine)
|
||||
return undefined;
|
||||
const wsEndpoint = containerUrlToHostUrl('ws://' + webSocketLine.substring(WS_LINE_PREFIX.length));
|
||||
const vncSession = containerUrlToHostUrl(novncLine.substring(NOVNC_LINE_PREFIX.length));
|
||||
return wsEndpoint && vncSession ? { wsEndpoint, vncSession } : undefined;
|
||||
}
|
||||
|
||||
export async function ensureContainerOrDie(): Promise<ContainerInfo> {
|
||||
@ -149,11 +162,11 @@ export async function ensureContainerOrDie(): Promise<ContainerInfo> {
|
||||
}
|
||||
|
||||
export async function stopContainer() {
|
||||
const containerId = await findRunningDockerContainerId();
|
||||
if (!containerId)
|
||||
const container = await findRunningDockerContainer();
|
||||
if (!container)
|
||||
return;
|
||||
await dockerApi.stopContainer({
|
||||
containerId,
|
||||
containerId: container.containerId,
|
||||
waitUntil: 'removed',
|
||||
});
|
||||
}
|
||||
@ -176,10 +189,10 @@ async function findDockerImage(imageName: string): Promise<dockerApi.DockerImage
|
||||
return images.find(image => image.names.includes(imageName));
|
||||
}
|
||||
|
||||
async function findRunningDockerContainerId(): Promise<string|undefined> {
|
||||
async function findRunningDockerContainer(): Promise<dockerApi.DockerContainer|undefined> {
|
||||
const containers = await dockerApi.listContainers();
|
||||
const dockerImage = await findDockerImage(VRT_IMAGE_NAME);
|
||||
const container = dockerImage ? containers.find(container => container.imageId === dockerImage.imageId) : undefined;
|
||||
return container?.state === 'running' ? container.containerId : undefined;
|
||||
return container?.state === 'running' ? container : undefined;
|
||||
}
|
||||
|
||||
|
||||
@ -25,11 +25,18 @@ export interface DockerImage {
|
||||
names: string[];
|
||||
}
|
||||
|
||||
export interface PortBinding {
|
||||
ip: string;
|
||||
hostPort: number;
|
||||
containerPort: number;
|
||||
}
|
||||
|
||||
export interface DockerContainer {
|
||||
containerId: string;
|
||||
imageId: string;
|
||||
state: 'created'|'restarting'|'running'|'removing'|'paused'|'exited'|'dead';
|
||||
names: string[];
|
||||
portBindings: PortBinding[];
|
||||
}
|
||||
|
||||
export async function listContainers(): Promise<DockerContainer[]> {
|
||||
@ -38,7 +45,12 @@ export async function listContainers(): Promise<DockerContainer[]> {
|
||||
containerId: container.Id,
|
||||
imageId: container.ImageID,
|
||||
state: container.State,
|
||||
names: container.Names
|
||||
names: container.Names,
|
||||
portBindings: container.Ports?.map((portInfo: any) => ({
|
||||
ip: portInfo.IP,
|
||||
hostPort: portInfo.PublicPort,
|
||||
containerPort: portInfo.PrivatePort,
|
||||
})) ?? [],
|
||||
}));
|
||||
}
|
||||
|
||||
@ -56,7 +68,7 @@ export async function launchContainer(options: LaunchContainerOptions): Promise<
|
||||
const PortBindings: any = {};
|
||||
for (const port of (options.ports ?? [])) {
|
||||
ExposedPorts[`${port}/tcp`] = {};
|
||||
PortBindings[`${port}/tcp`] = [{ HostPort: port + '' }];
|
||||
PortBindings[`${port}/tcp`] = [{ HostPort: '0', HostIp: '127.0.0.1' }];
|
||||
}
|
||||
const container = await postJSON(`/containers/create` + (options.name ? '?name=' + options.name : ''), {
|
||||
Cmd: options.command,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user