mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
* fix: strip credentials from service-level API responses
Registry passwords and S3 destination credentials were being returned
in service `.one` tRPC endpoints to any user with service-level read
access. Reported by Nihon Kohden Corporation security team.
- Strip registry `password` from `findApplicationById` via Drizzle `columns: { password: false }`
- Strip destination `accessKey`/`secretAccessKey` from all DB service finders (postgres, mysql, mariadb, mongo, libsql, compose, backup, volume-backups)
- Add `findRegistryByIdWithCredentials` for internal use only
- Builders and upload utils now load registry credentials by ID at execution time
- `createRollback` enriches `fullContext` with registry credentials before persisting to DB so rollback execution has what it needs
- Remove `findApplicationByIdWithCredentials` and `ApplicationNestedWithCredentials` — no longer needed
- Backup execution utils load full destination via `findDestinationById` at runtime instead of reading from the joined relation
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
246 lines
5.9 KiB
TypeScript
246 lines
5.9 KiB
TypeScript
import { findRegistryByIdWithCredentials } from "@dokploy/server/services/registry";
|
|
import type { InferResultType } from "@dokploy/server/types/with";
|
|
import type { CreateServiceOptions } from "dockerode";
|
|
import { getRegistryTag, uploadImageRemoteCommand } from "../cluster/upload";
|
|
import {
|
|
calculateResources,
|
|
generateBindMounts,
|
|
generateConfigContainer,
|
|
generateFileMounts,
|
|
generateVolumeMounts,
|
|
prepareEnvironmentVariables,
|
|
} from "../docker/utils";
|
|
import { getRemoteDocker } from "../servers/remote-docker";
|
|
import { getDockerCommand } from "./docker-file";
|
|
import { getHerokuCommand } from "./heroku";
|
|
import { getNixpacksCommand } from "./nixpacks";
|
|
import { getPaketoCommand } from "./paketo";
|
|
import { getRailpackCommand } from "./railpack";
|
|
import { getStaticCommand } from "./static";
|
|
|
|
// NIXPACKS codeDirectory = where is the path of the code directory
|
|
// HEROKU codeDirectory = where is the path of the code directory
|
|
// PAKETO codeDirectory = where is the path of the code directory
|
|
// DOCKERFILE codeDirectory = where is the exact path of the (Dockerfile)
|
|
export type ApplicationNested = InferResultType<
|
|
"applications",
|
|
{
|
|
mounts: true;
|
|
security: true;
|
|
redirects: true;
|
|
ports: true;
|
|
registry: { columns: { password: false } };
|
|
buildRegistry: { columns: { password: false } };
|
|
rollbackRegistry: { columns: { password: false } };
|
|
deployments: true;
|
|
environment: { with: { project: true } };
|
|
}
|
|
>;
|
|
|
|
export const getBuildCommand = async (application: ApplicationNested) => {
|
|
let command = "";
|
|
|
|
if (application.sourceType !== "docker") {
|
|
const { buildType } = application;
|
|
switch (buildType) {
|
|
case "nixpacks":
|
|
command = getNixpacksCommand(application);
|
|
break;
|
|
case "heroku_buildpacks":
|
|
command = getHerokuCommand(application);
|
|
break;
|
|
case "paketo_buildpacks":
|
|
command = getPaketoCommand(application);
|
|
break;
|
|
case "static":
|
|
command = getStaticCommand(application);
|
|
break;
|
|
case "dockerfile":
|
|
command = getDockerCommand(application);
|
|
break;
|
|
case "railpack":
|
|
command = getRailpackCommand(application);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (
|
|
application.registry ||
|
|
application.buildRegistry ||
|
|
application.rollbackRegistry
|
|
) {
|
|
command += await uploadImageRemoteCommand(application);
|
|
}
|
|
|
|
return command;
|
|
};
|
|
|
|
export const mechanizeDockerContainer = async (
|
|
application: ApplicationNested,
|
|
) => {
|
|
const {
|
|
appName,
|
|
env,
|
|
mounts,
|
|
cpuLimit,
|
|
memoryLimit,
|
|
memoryReservation,
|
|
cpuReservation,
|
|
command,
|
|
args,
|
|
ports,
|
|
} = application;
|
|
|
|
const resources = calculateResources({
|
|
memoryLimit,
|
|
memoryReservation,
|
|
cpuLimit,
|
|
cpuReservation,
|
|
});
|
|
|
|
const volumesMount = generateVolumeMounts(mounts);
|
|
|
|
const {
|
|
HealthCheck,
|
|
RestartPolicy,
|
|
Placement,
|
|
Labels,
|
|
Mode,
|
|
RollbackConfig,
|
|
UpdateConfig,
|
|
Networks,
|
|
StopGracePeriod,
|
|
EndpointSpec,
|
|
Ulimits,
|
|
} = generateConfigContainer(application);
|
|
|
|
const bindsMount = generateBindMounts(mounts);
|
|
const filesMount = generateFileMounts(appName, application);
|
|
const envVariables = prepareEnvironmentVariables(
|
|
env,
|
|
application.environment.project.env,
|
|
application.environment.env,
|
|
);
|
|
|
|
const image = await getImageName(application);
|
|
const authConfig = await getAuthConfig(application);
|
|
const docker = await getRemoteDocker(application.serverId);
|
|
|
|
const settings: CreateServiceOptions = {
|
|
authconfig: authConfig,
|
|
Name: appName,
|
|
TaskTemplate: {
|
|
ContainerSpec: {
|
|
HealthCheck,
|
|
Image: image,
|
|
Env: envVariables,
|
|
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
|
...(StopGracePeriod !== null &&
|
|
StopGracePeriod !== undefined && { StopGracePeriod }),
|
|
...(command && {
|
|
Command: command.split(" "),
|
|
}),
|
|
...(args &&
|
|
args.length > 0 && {
|
|
Args: args,
|
|
}),
|
|
...(Ulimits && { Ulimits }),
|
|
Labels,
|
|
},
|
|
Networks,
|
|
RestartPolicy,
|
|
Placement,
|
|
Resources: {
|
|
...resources,
|
|
},
|
|
},
|
|
Mode,
|
|
RollbackConfig,
|
|
EndpointSpec: EndpointSpec
|
|
? EndpointSpec
|
|
: {
|
|
Ports: ports.map((port) => ({
|
|
PublishMode: port.publishMode,
|
|
Protocol: port.protocol,
|
|
TargetPort: port.targetPort,
|
|
PublishedPort: port.publishedPort,
|
|
})),
|
|
},
|
|
UpdateConfig,
|
|
};
|
|
|
|
try {
|
|
const service = docker.getService(appName);
|
|
const inspect = await service.inspect();
|
|
|
|
await service.update({
|
|
version: Number.parseInt(inspect.Version.Index),
|
|
...settings,
|
|
TaskTemplate: {
|
|
...settings.TaskTemplate,
|
|
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.log(error);
|
|
if (authConfig) {
|
|
await docker.createService(authConfig, settings);
|
|
} else {
|
|
await docker.createService(settings);
|
|
}
|
|
}
|
|
};
|
|
|
|
const getImageName = async (application: ApplicationNested) => {
|
|
const { appName, sourceType, dockerImage, registry, buildRegistry } =
|
|
application;
|
|
const imageName = `${appName}:latest`;
|
|
if (sourceType === "docker") {
|
|
return dockerImage || "ERROR-NO-IMAGE-PROVIDED";
|
|
}
|
|
|
|
if (registry) {
|
|
const r = await findRegistryByIdWithCredentials(registry.registryId);
|
|
return getRegistryTag(r, imageName);
|
|
}
|
|
if (buildRegistry) {
|
|
const r = await findRegistryByIdWithCredentials(buildRegistry.registryId);
|
|
return getRegistryTag(r, imageName);
|
|
}
|
|
|
|
return imageName;
|
|
};
|
|
|
|
export const getAuthConfig = async (application: ApplicationNested) => {
|
|
const {
|
|
registry,
|
|
buildRegistry,
|
|
username,
|
|
password,
|
|
sourceType,
|
|
registryUrl,
|
|
} = application;
|
|
|
|
if (sourceType === "docker") {
|
|
if (username && password) {
|
|
return { password, username, serveraddress: registryUrl || "" };
|
|
}
|
|
} else if (registry) {
|
|
const r = await findRegistryByIdWithCredentials(registry.registryId);
|
|
return {
|
|
password: r.password,
|
|
username: r.username,
|
|
serveraddress: r.registryUrl,
|
|
};
|
|
} else if (buildRegistry) {
|
|
const r = await findRegistryByIdWithCredentials(buildRegistry.registryId);
|
|
return {
|
|
password: r.password,
|
|
username: r.username,
|
|
serveraddress: r.registryUrl,
|
|
};
|
|
}
|
|
|
|
return undefined;
|
|
};
|