Skip to content

SFTP

SFTP servers: the sftp destination, from the sftp package. Declare the package once in dependencies.yml; DRE installs it on demand:

dependencies.yml
plugins:
- sftp

Set in profiles.yml, under destinations:, in a target with type: sftp.

Field Default Notes
host (required) SFTP server.
port 22 Port.
username (required) User name.
accept_unknown_host Trust a host missing from known_hosts.
password Password (or set private_key_path) Secret: use env_var().
private_key_path Private key file (instead of a password) A file path; ~/ is your home directory.
private_key_passphrase The private key’s passphrase. Secret: use env_var().
known_hosts_path Known_hosts file (default ~/.ssh/known_hosts) A file path; ~/ is your home directory.
private_key The private key’s text, e.g. from env_var() (instead of private_key_path) Secret: use env_var().
host_key_fingerprint Pinned host key, SHA256:… (otherwise ~/.ssh/known_hosts is used)
use_agent Sign in with the keys in your SSH agent (SSH_AUTH_SOCK), instead of a password or key file.
allow_rsa_keys False refuses RSA private keys; true uses them without the RUSTSEC-2023-0071 warning.
connect_timeout 30s How long to wait for a connection (30s, 2m, or seconds) A duration (30s, 2m) or seconds.
timeout 60s How long a read or write may make no progress before it fails. A duration (30s, 2m) or seconds.
retries 3 How many times to try again after a temporary error (0: never)

Set in the report’s output.destination entry.

Option Type Default Notes
if_exists string overwrite When a file is already at the path: overwrite it, fail with error, or number the new one. One of overwrite, error, number.
atomic boolean true Upload under a temporary name, then rename, so no half-written file appears.
temp_dir string Where the temporary file goes (on the same server), for receivers that pick up any new file.

host, port (22), username, and password, private_key_path or private_key (+ private_key_passphrase). private_key is the key’s text, for when it can’t be a file (a CI secret: private_key: "{{ env_var('SFTP_KEY') }}"); a key stored on one line with literal \n gets its line breaks back. Set private_key_path or private_key, not both. The host key is checked against known_hosts_path (default ~/.ssh/known_hosts) or a pinned host_key_fingerprint (SHA256:...). In private_key_path and known_hosts_path, a leading ~/ is your home directory. Unknown hosts are refused unless accept_unknown_host is true. With use_agent set to true, it signs in with the keys in your SSH agent (SSH_AUTH_SOCK; the OpenSSH agent’s pipe on Windows) instead of a password or key file, so the key never enters DRE. Missing directories are created. Uploads go under a temporary name first (atomic, temp_dir: see Uploads under a temporary name); if_exists is checked in the same step as the write (see A file already at the path). The postgres source’s ssh: block takes the same settings.

RSA private keys sign through the rsa crate, which has a known timing weakness (RUSTSEC-2023-0071) and no fixed release yet. This applies to sftp and to the postgres source’s ssh: tunnel, and only to RSA keys DRE reads itself (private_key_path, private_key). Ed25519 and ECDSA keys, passwords and use_agent: true don’t use it.

The practical risk is low: an attack needs precise timings of many signatures, and DRE signs once per connection, a few times a run. So DRE keeps RSA keys working and warns once per run. allow_rsa_keys changes that:

Value What happens
unset RSA keys work, with a warning naming the advisory.
true RSA keys work without the warning: you’ve read this, and the key is only for a server you trust (a bank’s SFTP server that accepts nothing else, say).
false RSA keys are refused, with what to do instead: for teams that want them blocked.

To move off RSA: ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519, add the .pub file on the server (its authorized_keys, or the bank’s key upload), and point private_key_path at the new key. Or keep the RSA key in your SSH agent and set use_agent: true: OpenSSH signs, not DRE.

Deliver a file without exposing a partial upload

Section titled “Deliver a file without exposing a partial upload”

Use the complete SFTP delivery example for a runnable local server and report. For a real receiving server, use its verified host key and a key file:

profiles.yml
destinations:
partner:
targets:
dev: {deliver: false}
prod:
type: sftp
host: sftp.example.com
username: reporting
private_key_path: ~/.ssh/id_ed25519
known_hosts_path: ~/.ssh/known_hosts
# within a report's output
destination:
profile: partner
path: "incoming/report-{{ run.date.yyyymmdd }}.csv"
atomic: true
temp_dir: staging
if_exists: error

temp_dir and the final path must allow a rename on the same server. The receiving system should watch incoming, not staging. sftp/host-key-mismatch requires verifying the server identity with its administrator before updating the pin; disabling the check hides a changed server. sftp/file-exists needs a new dated name or a deliberate replacement policy. See atomic upload behavior, destination keys and destination.