From f0a48bc6537e841fcdca81c50c09f95d044fbdc1 Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Mon, 20 Nov 2023 12:25:25 +0000 Subject: [PATCH] Prevent keys from leaking across registries Signed-off-by: Prabhu Subramanian --- docker.js | 83 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/docker.js b/docker.js index 23ba695d02..5e68695c07 100644 --- a/docker.js +++ b/docker.js @@ -107,8 +107,14 @@ export const getOnlyDirs = (srcpath, dirName) => { ].filter((d) => d.endsWith(dirName)); }; -const getDefaultOptions = () => { +const getDefaultOptions = (forRegistry) => { let authTokenSet = false; + if (!forRegistry && process.env.DOCKER_SERVER_ADDRESS) { + forRegistry = process.env.DOCKER_SERVER_ADDRESS; + } + if (forRegistry) { + forRegistry = forRegistry.replace("http://", "").replace("https://", ""); + } const opts = { enableUnixSockets: true, throwHttpErrors: true, @@ -129,11 +135,7 @@ const getDefaultOptions = () => { if (configJson.auths) { // Check if there are hardcoded tokens for (const serverAddress of Object.keys(configJson.auths)) { - if ( - process.env.DOCKER_SERVER_ADDRESS && - process.env.DOCKER_SERVER_ADDRESS.trim().length && - process.env.DOCKER_SERVER_ADDRESS !== serverAddress - ) { + if (forRegistry && forRegistry !== serverAddress) { continue; } if (configJson.auths[serverAddress].auth) { @@ -150,11 +152,7 @@ const getDefaultOptions = () => { } else if (configJson.credHelpers) { // Support for credential helpers for (const serverAddress of Object.keys(configJson.credHelpers)) { - if ( - process.env.DOCKER_SERVER_ADDRESS && - process.env.DOCKER_SERVER_ADDRESS.trim().length && - process.env.DOCKER_SERVER_ADDRESS !== serverAddress - ) { + if (forRegistry && forRegistry !== serverAddress) { continue; } if (configJson.credHelpers[serverAddress]) { @@ -191,15 +189,12 @@ const getDefaultOptions = () => { process.env.DOCKER_USER && process.env.DOCKER_PASSWORD && process.env.DOCKER_EMAIL && - process.env.DOCKER_SERVER_ADDRESS + forRegistry ) { const authPayload = { username: process.env.DOCKER_USER, email: process.env.DOCKER_EMAIL, - serveraddress: process.env.DOCKER_SERVER_ADDRESS.replace( - "http://", - "" - ).replace("https://", "") + serveraddress: forRegistry }; if (process.env.DOCKER_USER === "") { authPayload.IdentityToken = process.env.DOCKER_PASSWORD; @@ -264,11 +259,11 @@ const getDefaultOptions = () => { return opts; }; -export const getConnection = async (options) => { +export const getConnection = async (options, forRegistry) => { if (isContainerd) { return undefined; } else if (!dockerConn) { - const defaultOptions = getDefaultOptions(); + const defaultOptions = getDefaultOptions(forRegistry); const opts = Object.assign( {}, { @@ -360,8 +355,8 @@ export const getConnection = async (options) => { return dockerConn; }; -export const makeRequest = async (path, method = "GET") => { - const client = await getConnection(); +export const makeRequest = async (path, method = "GET", forRegistry) => { + const client = await getConnection({}, forRegistry); if (!client) { return undefined; } @@ -451,7 +446,7 @@ export const parseImageName = (fullImageName) => { export const getImage = async (fullImageName) => { let localData = undefined; let pullData = undefined; - const { repo, tag, digest } = parseImageName(fullImageName); + const { registry, repo, tag, digest } = parseImageName(fullImageName); let repoWithTag = `${repo}:${tag !== "" ? tag : ":latest"}`; // Fetch only the latest tag if none is specified if (tag === "" && digest === "") { @@ -505,7 +500,11 @@ export const getImage = async (fullImageName) => { } } try { - localData = await makeRequest(`images/${repoWithTag}/json`); + localData = await makeRequest( + `images/${repoWithTag}/json`, + "GET", + registry + ); if (localData) { return localData; } @@ -513,10 +512,14 @@ export const getImage = async (fullImageName) => { // ignore } try { - localData = await makeRequest(`images/${repo}/json`); + localData = await makeRequest(`images/${repo}/json`, "GET", registry); } catch (err) { try { - localData = await makeRequest(`images/${fullImageName}/json`); + localData = await makeRequest( + `images/${fullImageName}/json`, + "GET", + registry + ); if (localData) { return localData; } @@ -532,7 +535,8 @@ export const getImage = async (fullImageName) => { try { pullData = await makeRequest( `images/create?fromImage=${fullImageName}`, - "POST" + "POST", + registry ); if ( pullData && @@ -554,7 +558,8 @@ export const getImage = async (fullImageName) => { } pullData = await makeRequest( `images/create?fromImage=${repoWithTag}`, - "POST" + "POST", + registry ); } catch (err) { // continue regardless of error @@ -564,7 +569,11 @@ export const getImage = async (fullImageName) => { if (DEBUG_MODE) { console.log(`Trying with ${repoWithTag}`); } - localData = await makeRequest(`images/${repoWithTag}/json`); + localData = await makeRequest( + `images/${repoWithTag}/json`, + "GET", + registry + ); if (localData) { return localData; } @@ -573,7 +582,7 @@ export const getImage = async (fullImageName) => { if (DEBUG_MODE) { console.log(`Trying with ${repo}`); } - localData = await makeRequest(`images/${repo}/json`); + localData = await makeRequest(`images/${repo}/json`, "GET", registry); if (localData) { return localData; } @@ -584,7 +593,11 @@ export const getImage = async (fullImageName) => { if (DEBUG_MODE) { console.log(`Trying with ${fullImageName}`); } - localData = await makeRequest(`images/${fullImageName}/json`); + localData = await makeRequest( + `images/${fullImageName}/json`, + "GET", + registry + ); } catch (err) { // continue regardless of error } @@ -804,7 +817,7 @@ export const exportImage = async (fullImageName) => { if (!localData) { return undefined; } - const { tag, digest } = parseImageName(fullImageName); + const { registry, tag, digest } = parseImageName(fullImageName); // Fetch only the latest tag if none is specified if (tag === "" && digest === "") { fullImageName = fullImageName + ":latest"; @@ -842,10 +855,16 @@ export const exportImage = async (fullImageName) => { } } } else { - const client = await getConnection(); + const client = await getConnection({}, registry); try { if (DEBUG_MODE) { - console.log(`About to export image ${fullImageName} to ${tempDir}`); + if (registry && registry.trim().length) { + console.log( + `About to export image ${fullImageName} from ${registry} to ${tempDir}` + ); + } else { + console.log(`About to export image ${fullImageName} to ${tempDir}`); + } } await stream.pipeline( client.stream(`images/${fullImageName}/get`),