reach own docker daemon over tcp on socket-proxy setups; re-run host detection when an environment is added or edited; keep scanner on the host socket
This commit is contained in:
8 files changed
+277
-84
No files matched your search
@@ -5,6 +5,7 @@ import { authorize } from '$lib/server/authorize';
|
||||
import { auditEnvironment } from '$lib/server/audit';
|
||||
import { invalidateTokenCacheForUser } from '$lib/server/api-tokens';
|
||||
import { refreshSubprocessEnvironments } from '$lib/server/subprocess-manager';
|
||||
import { resetHostDetection, detectHostDataDir } from '$lib/server/host-path';
|
||||
import { serializeLabels, parseLabels, MAX_LABELS } from '$lib/utils/label-colors';
|
||||
import { cleanPem } from '$lib/utils/pem';
|
||||
import { validateEnvName } from '$lib/utils/env-name';
|
||||
@@ -125,6 +126,11 @@ export const POST: RequestHandler = async (event) => {
|
||||
// Notify event collectors to pick up the new environment
|
||||
refreshSubprocessEnvironments();
|
||||
|
||||
// Re-run host detection: a socket-proxy deployment with no direct env at boot
|
||||
// can now reach Docker through this env without a restart (#1203). Fire-and-forget.
|
||||
resetHostDetection();
|
||||
void detectHostDataDir();
|
||||
|
||||
// Auto-assign Admin role to creator (Enterprise only)
|
||||
if (auth.isEnterprise && auth.authEnabled && auth.isAuthenticated && !auth.isAdmin) {
|
||||
const user = auth.user;
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getStacksDir } from '$lib/server/stacks';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
import { auditEnvironment } from '$lib/server/audit';
|
||||
import { refreshSubprocessEnvironments } from '$lib/server/subprocess-manager';
|
||||
import { resetHostDetection, detectHostDataDir } from '$lib/server/host-path';
|
||||
import { serializeLabels, parseLabels, MAX_LABELS } from '$lib/utils/label-colors';
|
||||
import { cleanPem } from '$lib/utils/pem';
|
||||
import { validateEnvName } from '$lib/utils/env-name';
|
||||
@@ -155,6 +156,11 @@ export const PUT: RequestHandler = async (event) => {
|
||||
return json({ error: 'Environment not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
// Re-run host detection: editing an env (e.g. pointing it at a socket proxy)
|
||||
// lets a socketless deployment reach Docker without a restart (#1203).
|
||||
resetHostDetection();
|
||||
void detectHostDataDir();
|
||||
|
||||
// Notify event collectors if collectActivity or collectMetrics setting changed
|
||||
if (data.collectActivity !== undefined || data.collectMetrics !== undefined) {
|
||||
refreshSubprocessEnvironments();
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
getOwnContainerId,
|
||||
getHostDockerSocket,
|
||||
getOwnDockerHost,
|
||||
getAutoDetectedDockerHost,
|
||||
getOwnExtraHosts,
|
||||
getOwnNetworkMode
|
||||
} from '$lib/server/host-path';
|
||||
@@ -17,7 +18,7 @@ const UPDATER_LABEL = 'dockhand.updater';
|
||||
|
||||
/** Get TCP Docker host if configured, null otherwise. */
|
||||
function getDockerTcpHost(): string | null {
|
||||
const dockerHost = process.env.DOCKER_HOST || getOwnDockerHost();
|
||||
const dockerHost = process.env.DOCKER_HOST || getOwnDockerHost() || getAutoDetectedDockerHost();
|
||||
return dockerHost?.startsWith('tcp://') ? dockerHost : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,59 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
import { getOwnContainerId, getOwnDockerHost } from '$lib/server/host-path';
|
||||
import { getRegistryManifestDigest, unixSocketRequest } from '$lib/server/docker';
|
||||
import { getOwnContainerId, getOwnDockerHost, getAutoDetectedDockerHost } from '$lib/server/host-path';
|
||||
import { getRegistryManifestDigest, unixSocketRequest, dockerFetch } from '$lib/server/docker';
|
||||
import { getEnvironments } from '$lib/server/db';
|
||||
import { compareVersions } from '$lib/utils/version';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
/** Fetch from the local Docker directly (not through environment routing) */
|
||||
function localDockerFetch(path: string, options: RequestInit = {}): Promise<Response> {
|
||||
const dockerHost = process.env.DOCKER_HOST || getOwnDockerHost();
|
||||
/**
|
||||
* When there is no local socket and no DOCKER_HOST (e.g. a socket-proxy setup
|
||||
* that mounts no docker.sock, #1203), find the environment whose daemon actually
|
||||
* runs the Dockhand container by inspecting our own container ID on each
|
||||
* candidate. This is deterministic even with several `direct` envs, one of which
|
||||
* is a genuinely remote host - the remote daemon returns 404 for our ID, so we
|
||||
* never pick it. Memoized: the answer is stable for the process lifetime.
|
||||
* Returns the env id, or null if none host us.
|
||||
*/
|
||||
let ownEnvIdMemo: number | null | undefined;
|
||||
async function resolveOwnEnvId(containerId: string): Promise<number | null> {
|
||||
if (ownEnvIdMemo !== undefined) return ownEnvIdMemo;
|
||||
|
||||
const envs = await getEnvironments();
|
||||
// Socket/local envs first (cheapest, almost always us), then direct.
|
||||
const candidates = [
|
||||
...envs.filter((e) => e.connectionType === 'socket' || !e.connectionType),
|
||||
...envs.filter((e) => e.connectionType === 'direct')
|
||||
];
|
||||
for (const env of candidates) {
|
||||
try {
|
||||
const res = await dockerFetch(`/containers/${containerId}/json`, {}, env.id);
|
||||
if (res.ok) {
|
||||
console.log(`[SelfUpdate] Dockhand runs on environment "${env.name}" (id ${env.id}, ${env.connectionType || 'socket'}); using it for update checks`);
|
||||
ownEnvIdMemo = env.id;
|
||||
return env.id;
|
||||
}
|
||||
} catch {
|
||||
// Env unreachable or does not host us; try the next candidate.
|
||||
}
|
||||
}
|
||||
console.log('[SelfUpdate] No configured environment hosts the Dockhand container; cannot reach Docker for update check');
|
||||
ownEnvIdMemo = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch from the Docker daemon running Dockhand itself (not via env routing,
|
||||
* which fails on private-registry images - see the private-registry fix).
|
||||
*
|
||||
* Order: explicit DOCKER_HOST tcp -> local socket if present -> the environment
|
||||
* that actually hosts our own container. The last path covers socket-proxy
|
||||
* setups with no docker.sock mount and no DOCKER_HOST (#1203), so the user does
|
||||
* not have to set DOCKER_HOST (which breaks scanner networking, #1204).
|
||||
*/
|
||||
async function localDockerFetch(path: string, options: RequestInit = {}): Promise<Response> {
|
||||
const dockerHost = process.env.DOCKER_HOST || getOwnDockerHost() || getAutoDetectedDockerHost();
|
||||
|
||||
if (dockerHost?.startsWith('tcp://')) {
|
||||
// TCP connection (socat proxy, socket-proxy, remote Docker)
|
||||
@@ -15,8 +61,21 @@ function localDockerFetch(path: string, options: RequestInit = {}): Promise<Resp
|
||||
return fetch(url, options);
|
||||
}
|
||||
|
||||
// Unix socket (default)
|
||||
const socketPath = process.env.DOCKER_SOCKET || '/var/run/docker.sock';
|
||||
if (existsSync(socketPath)) {
|
||||
return unixSocketRequest(socketPath, path, options);
|
||||
}
|
||||
|
||||
const containerId = getOwnContainerId();
|
||||
if (containerId) {
|
||||
const ownEnvId = await resolveOwnEnvId(containerId);
|
||||
if (ownEnvId !== null) {
|
||||
return dockerFetch(path, options, ownEnvId);
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing usable: fall through to the socket path so the caller gets the
|
||||
// original ENOENT rather than a silent success.
|
||||
return unixSocketRequest(socketPath, path, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
import { getOwnDockerHost } from '$lib/server/host-path';
|
||||
import { getOwnDockerHost, getAutoDetectedDockerHost } from '$lib/server/host-path';
|
||||
import { unixSocketRequest } from '$lib/server/docker';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
/** Fetch from the local Docker directly. Supports TCP and Unix socket. */
|
||||
function localDockerFetch(path: string): Promise<Response> {
|
||||
const dockerHost = process.env.DOCKER_HOST || getOwnDockerHost();
|
||||
const dockerHost = process.env.DOCKER_HOST || getOwnDockerHost() || getAutoDetectedDockerHost();
|
||||
if (dockerHost?.startsWith('tcp://')) {
|
||||
return fetch(dockerHost.replace('tcp://', 'http://') + path);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user