# SFTP

> The sftp destination: host keys, keys and the RSA advisory.

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

dependencies.yml

```yaml
plugins:
  - sftp
```

### Profile fields

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)

### Options

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.

## Notes

`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](https://getdre.com/docs/plugins/#uploads-under-a-temporary-name)); `if_exists` is checked in the same step as the write (see [A file already at the path](https://getdre.com/docs/plugins/#a-file-already-at-the-path)). The [`postgres`](https://getdre.com/docs/plugin-postgres/) source’s `ssh:` block takes the same settings.

## RSA keys

RSA private keys sign through the `rsa` crate, which has a known timing weakness ([RUSTSEC-2023-0071](https://rustsec.org/advisories/RUSTSEC-2023-0071.html)) 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

Use the complete [SFTP delivery example](https://github.com/get-dre/dre/blob/v0.5.1/examples/sftp-delivery/) for a runnable local server and report. For a real receiving server, use its verified host key and a key file:

profiles.yml

```yaml
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
```

```yaml
# 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](https://getdre.com/docs/plugins/#uploads-under-a-temporary-name), [destination keys](https://getdre.com/docs/reference-report/#outputdestination) and [destination](https://getdre.com/docs/glossary/#destination).

[Edit page](https://github.com/get-dre/dre/edit/master/docs/plugin-sftp.md)

[Previous  
Azure Blob Storage](https://getdre.com/docs/plugin-azure_blob/)[Next  
FTP and FTPS](https://getdre.com/docs/plugin-ftp/)
