Skip to content

Security: S3 backup credentials (access key + secret) logged in plaintext to Dokploy service stdout #4621

@tyler-b4innova

Description

@tyler-b4innova

Summary

S3 destination credentials are logged in plaintext to Dokploy's structured logger output (service stdout) every time a backup job runs. Any operator who can read Dokploy service logs — including log aggregators, syslog forwarders, or anyone with docker service logs dokploy access — can recover S3 access keys and secret access keys.

Version

Dokploy v0.29.4 through at least v0.29.8 (current latest). Not addressed by PR #4566 (restore logging refactor) or PR #4579 (registry password fix).

Environment

Any Dokploy instance with at least one configured S3 backup destination and an enabled backup schedule.

Root cause

In packages/server/src/utils/backups/utils.ts, getS3Credentials() returns rclone flags with credentials embedded as inline flag values:

`--s3-access-key-id="${accessKey}"`,
`--s3-secret-access-key="${secretAccessKey}"`,

getBackupCommand() then logs the fully assembled rcloneCommand string (which includes those flags verbatim) via logger.info:

logger.info(
  { containerSearch, backupCommand, rcloneCommand, logPath },
  `Executing backup command: ${backup.databaseType} ${backup.backupType}`,
);

This structured log line — containing the full rclone rcat --s3-access-key-id="..." --s3-secret-access-key="..." ... string — is emitted to Dokploy's stdout on every backup run, not just on errors.

Additionally, keepLatestNBackups() in packages/server/src/utils/backups/index.ts constructs equivalent rclone commands (rcloneList, rcloneDelete) with the same credential flags inline, and surfaces them via console.error(error) on failure — also reaching stdout.

What is leaked

  • S3 / object-storage access key ID
  • S3 / object-storage secret access key
  • Bucket name, region, endpoint

One log line per backup run (database type × configured schedule frequency).

What is NOT leaked

No credentials appear in the UI, in Dokploy deployment log files written to disk, or in notification webhooks — only in the service process stdout captured by the container runtime.

Suggested fix

  1. Do not log the rclone command string. Remove rcloneCommand from the logger.info payload in getBackupCommand(), or log only a redacted summary (e.g. rclone rcat :s3:<bucket>/...).

  2. Pass credentials via environment variables instead of inline flags. rclone supports RCLONE_S3_ACCESS_KEY_ID and RCLONE_S3_SECRET_ACCESS_KEY environment variables, which avoids embedding secrets in the command string at all (they never appear in process args or logs).

  3. Alternatively, use an rclone config file written to a temp path with 0600 permissions, and pass --config <path> — credentials stay out of the command line entirely.

Approach 2 (env vars) is the lowest-change fix and consistent with how the recently merged PR #4579 handled registry passwords.

Related code

  • packages/server/src/utils/backups/utils.tsgetS3Credentials(), getBackupCommand()
  • packages/server/src/utils/backups/index.tskeepLatestNBackups()
  • All per-database backup runners (postgres.ts, mysql.ts, mariadb.ts, mongo.ts, libsql.ts, compose.ts) build and pass the rclone command constructed from getS3Credentials()

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions