8000 docker(install): opt to expose local tcp address by crazy-max · Pull Request #531 · docker/actions-toolkit · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

docker(install): opt to expose local tcp address #531

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,18 @@ jobs:
let includes = [];
for (const os of ['ubuntu-latest', 'macos-13', 'windows-latest']) {
for (const test of tests) {
if (os === 'macos-13' && test === 'docker/install.test.itg.ts') {
includes.push({ os: os, test: test, docker_install_type: 'image', docker_install_version: '27.3.1' });
includes.push({ os: os, test: test, docker_install_type: 'image', docker_install_version: 'master' });
includes.push({ os: os, test: test, docker_install_type: 'image', docker_install_version: 'latest' });
includes.push({ os: os, test: test, docker_install_type: 'archive', docker_install_version: 'v26.1.4' });
includes.push({ os: os, test: test, docker_install_type: 'archive', docker_install_version: 'latest' });
if (test === 'docker/install.test.itg.ts') {
includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: '27.3.1' });
includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: 'master' });
includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: 'latest' });
includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'archive', docker_install_version: 'v26.1.4' });
includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'archive', docker_install_version: 'latest' });
if (os === 'ubuntu-latest') {
includes.push({ os: os, test: test, test_name: 'rootless', docker_install_type: 'image', docker_install_version: 'latest' });
includes.push({ os: os, test: test, test_name: 'rootless', docker_install_type: 'archive', docker_install_version: 'latest' });
}
includes.push({ os: os, test: test, test_name: 'tcp', docker_install_type: 'image', docker_install_version: 'latest' });
includes.push({ os: os, test: test, test_name: 'tcp', docker_install_type: 'archive', docker_install_version: 'latest' });
} else {
includes.push({ os: os, test: test });
}
Expand Down Expand Up @@ -176,8 +182,16 @@ jobs:
run: yarn install
-
name: Test
run: |
yarn test:itg-coverage --runTestsByPath __tests__/${{ matrix.test }} --coverageDirectory=./coverage
uses: actions/github-script@v7
with:
script: |
const testName = `${{ matrix.test_name }}`;
let args = ['test:itg-coverage'];
if (testName) {
args.push(`--testNamePattern=^${testName} `);
}
args.push(`--runTestsByPath`, `__tests__/${{ matrix.test }}`, `--coverageDirectory=./coverage`);
await exec.exec('yarn', args);
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CTN_BUILDER_NAME: ${{ steps.builder.outputs.name }}
Expand Down
30 changes: 30 additions & 0 deletions __tests__/docker/install.test.itg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,36 @@ describe('rootless', () => {
);
});

describe('tcp', () => {
// prettier-ignore
test.each(getSources(false))(
'install %s', async (source) => {
await ensureNoSystemContainerd();
const install = new Install({
source: source,
runDir: tmpDir(),
contextName: 'foo',
daemonConfig: `{"debug":true}`,
localTCPPort: 2378
});
await expect(
tryInstall(install, async () => {
const out = await Docker.getExecOutput(['info'], {
env: Object.assign({}, process.env, {
DOCKER_HOST: 'tcp://localhost:2378',
DOCKER_CONTENT_TRUST: 'false'
}) as {
[key: string]: string;
}
});
expect(out.exitCode).toBe(0);
})
).resolves.not.toThrow();
},
30 * 60 * 1000
);
});

async function tryInstall(install: Install, extraCheck?: () => Promise<void>): Promise<void> {
try {
await install.download();
Expand Down
37 changes: 27 additions & 10 deletions src/docker/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ param(
[string]$RunDir,

[Parameter(Mandatory = $true)]
[string]$DockerHost,
[string]$DockerHostSocket,

[Parameter(Mandatory = $false)]
[string]$DockerHostTCP,

[Parameter(Mandatory = $false)]
[string]$DaemonConfig)
Expand Down Expand Up @@ -82,7 +85,7 @@ if (Get-Service docker -ErrorAction SilentlyContinue) {
$env:Path = "$ToolDir;" + [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
Write-Host "Path: $env:Path"

$env:DOCKER_HOST = $DockerHost
$env:DOCKER_HOST = $DockerHostSocket
Write-Host "DOCKER_HOST: $env:DOCKER_HOST"

if ($DaemonConfig) {
Expand All @@ -91,16 +94,21 @@ if ($DaemonConfig) {
$DaemonConfig | Out-File -FilePath "$env:ProgramData\\Docker\\config\\daemon.json"
}

$arguments = @(
"--host=$DockerHostSocket",
"--data-root=$RunDir\\\\moby-root",
"--exec-root=$RunDir\\\\moby-exec",
"--pidfile=$RunDir\\\\docker.pid",
"--register-service"
)
if ($DockerHostTCP) {
$arguments += "--host=$DockerHostTCP"
}

Write-Host "Creating service"
New-Item -ItemType Directory "$RunDir\\moby-root" -ErrorAction SilentlyContinue | Out-Null
New-Item -ItemType Directory "$RunDir\\moby-exec" -ErrorAction SilentlyContinue | Out-Null
Start-Process -Wait -NoNewWindow "$ToolDir\\dockerd" \`
-ArgumentList \`
"--host=$DockerHost", \`
"--data-root=$RunDir\\moby-root", \`
"--exec-root=$RunDir\\moby-exec", \`
"--pidfile=$RunDir\\docker.pid", \`
"--register-service"
Start-Process -Wait -NoNewWindow "$ToolDir\\dockerd" -ArgumentList $arguments
Write-Host "Starting service"
Start-Service -Name docker
Write-Host "Service started successfully!"
Expand Down Expand Up @@ -231,6 +239,11 @@ provision:
export DEBIAN_FRONTEND=noninteractive
if [ "{{srcType}}" == "archive" ]; then
curl -fsSL https://get.docker.com | sh -s -- --channel {{srcArchiveChannel}} --version {{srcArchiveVersion}}
sed -i 's|^ExecStart=.*|ExecStart=/usr/bin/dockerd -H fd://{{#if localTCPPort}} -H tcp://0.0.0.0:2375{{/if}} --containerd=/run/containerd/containerd.sock|' /usr/lib/systemd/system/docker.service
systemctl daemon-reload
systemctl restart docker
systemctl status docker.socket || true
systemctl status docker.service || true
elif [ "{{srcType}}" == "image" ]; then
arch=$(uname -m)
case $arch in
Expand All @@ -250,7 +263,7 @@ provision:
wget https://raw.githubusercontent.com/moby/moby/{{gitCommit}}/contrib/init/systemd/docker.socket \
-O /etc/systemd/system/docker.socket

sed -i 's|^ExecStart=.*|ExecStart=/usr/local/bin/dockerd -H fd://|' /etc/systemd/system/docker.service
sed -i 's|^ExecStart=.*|ExecStart=/usr/local/bin/dockerd -H fd://{{#if localTCPPort}} -H tcp://0.0.0.0:2375{{/if}}|' /etc/systemd/system/docker.service
sed -i 's|containerd.service||' /etc/systemd/system/docker.service
if ! getent group docker; then
groupadd --system docker
Expand Down Expand Up @@ -285,6 +298,10 @@ hostResolver:
portForwards:
- guestSocket: "/var/run/docker.sock"
hostSocket: "{{dockerSock}}"
{{#if localTCPPort}}
- guestPort: 2375
hostPort: {{localTCPPort}}
{{/if}}

audio:
# EXPERIMENTAL
Expand Down
40 changes: 26 additions & 14 deletions src/docker/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface InstallOpts {
contextName?: string;
daemonConfig?: string;
rootless?: boolean;
localTCPPort?: number;
}

interface LimaImage {
Expand All @@ -65,28 +66,31 @@ interface LimaImage {
}

export class Install {
private runDir: string;
private readonly runDir: string;
private readonly source: InstallSource;
private readonly contextName: string;
private readonly daemonConfig?: string;
private readonly rootless: boolean;
private readonly localTCPPort?: number;

private _version: string | undefined;
private _toolDir: string | undefined;
private rootless: boolean;

private gitCommit: string | undefined;

private readonly limaInstanceName = 'docker-actions-toolkit';

constructor(opts: InstallOpts) {
this.runDir = opts.runDir;
this.rootless = opts.rootless || false;
this.source = opts.source || {
type: 'archive',
version: 'latest',
channel: 'stable'
};
this.contextName = opts.contextName || 'setup-docker-action';
this.daemonConfig = opts.daemonConfig;
this.rootless = opts.rootless || false;
this.localTCPPort = opts.localTCPPort;
}

get toolDir(): string {
Expand Down Expand Up @@ -268,6 +272,7 @@ export class Install {
customImages: Install.limaCustomImages(),
daemonConfig: limaDaemonConfig,
dockerSock: `${limaDir}/docker.sock`,
localTCPPort: this.localTCPPort,
gitCommit: this.gitCommit,
srcType: src.type,
srcArchiveVersion: this._version, // Use the resolved version (e.g. latest -> 27.4.0)
Expand Down Expand Up @@ -376,8 +381,10 @@ export class Install {
await Exec.exec('sudo', ['sh', '-c', 'echo 0 > /proc/sys/kernel/apparmor_restrict_unprivileged_userns']);
}
}

const cmd = `${dockerPath} --host="${dockerHost}" --config-file="${daemonConfigPath}" --exec-root="${this.runDir}/execroot" --data-root="${this.runDir}/data" --pidfile="${this.runDir}/docker.pid"`;
let cmd = `${dockerPath} --host="${dockerHost}" --config-file="${daemonConfigPath}" --exec-root="${this.runDir}/execroot" --data-root="${this.runDir}/data" --pidfile="${this.runDir}/docker.pid"`;
if (this.localTCPPort) {
cmd += ` --host="tcp://127.0.0.1:${this.localTCPPort}"`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: Exposing the docker socket over TCP without TLS is deprecated and will be a hard failure in future:

WARN[2024-12-13T11:24:36.737970212Z] Binding to IP address without --tlsverify is insecure and gives root access on this machine to everyone who has access to your network.  host="tcp://127.0.0.1:1234"
WARN[2024-12-13T11:24:36.738007253Z] Binding to an IP address, even on localhost, can also give access to scripts run in a browser. Be safe out there!  host="tcp://127.0.0.1:1234"
WARN[2024-12-13T11:24:36.738011878Z] [DEPRECATION NOTICE] In future versions this will be a hard failure preventing the daemon from starting! Learn more at: https://docs.docker.com/go/api-security/  host="tcp://127.0.0.1:1234"

See: moby/moby#41285

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do agree but hope there will be some opt-in env var to still avoid TLS for development purpose.

}
core.info(`[command] ${cmd}`); // https://github.com/actions/toolkit/blob/3d652d3133965f63309e4b2e1c8852cdbdcb3833/packages/exec/src/toolrunner.ts#L47
let sudo = 'sudo';
if (this.rootless) {
Expand Down Expand Up @@ -438,7 +445,7 @@ EOF`,
}

private async installWindows(): Promise<string> {
const dockerHost = 'npipe:////./pipe/setup_docker_action';
const dockerHostSocket = 'npipe:////./pipe/setup_docker_action';

let daemonConfig = undefined;
const daemonConfigPath = path.join(this.runDir, 'daemon.json');
Expand All @@ -460,24 +467,29 @@ EOF`,
});
}

const params = {
ToolDir: this.toolDir,
RunDir: this.runDir,
DockerHostSocket: dockerHostSocket,
DaemonConfig: daemonConfigStr
};
if (this.localTCPPort) {
params['DockerHostTCP'] = `tcp://127.0.0.1:${this.localTCPPort}`;
}

await core.group('Install Docker daemon service', async () => {
const setupCmd = await Util.powershellCommand(setupDockerWinPs1(), {
ToolDir: this.toolDir,
RunDir: this.runDir,
DockerHost: dockerHost,
DaemonConfig: daemonConfigStr
});
const setupCmd = await Util.powershellCommand(setupDockerWinPs1(), params);
await Exec.exec(setupCmd.command, setupCmd.args);
const logCmd = await Util.powershellCommand(dockerServiceLogsPs1());
await Exec.exec(logCmd.command, logCmd.args);
});

await core.group('Create Docker context', async () => {
await Docker.exec(['context', 'create', this.contextName, '--docker', `host=${dockerHost}`]);
await Docker.exec(['context', 'create', this.contextName, '--docker', `host=${dockerHostSocket}`]);
await Docker.exec(['context', 'use', this.contextName]);
});

return dockerHost;
return dockerHostSocket;
}

public async tearDown(): Promise<void> {
Expand Down
Loading
0