# RouterOS SSH Key Deployment (v7.18+)

Deploying SSH public keys to RouterOS v7 via CLI over SSH is surprisingly finicky. This doc catalogs what works and what doesn't.

## What DOES work

### Method A — `/file/add name="" contents=""` (v7 clean, no FTP needed)

This is the most reliable method discovered. Works on RouterOS 7.18.2 (CCR2004):

```
# Create file with the public key content
/file/add name="wisp-key.txt" contents="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... wisp-backup"

# Import the key for a user
/user ssh-keys import public-key-file="wisp-key.txt" user=shonuff

# Clean up the source file
/file/remove [find name="wisp-key.txt"]
```

**Limitations:** The `contents=` value is part of the SSH command string. RouterOS SSH session seems to have a ~170 character line limit. The Ed25519 key line is ~92 chars — fits fine. An RSA 4096 key line (~500+ chars) would NOT fit.

**Escaping:** The key content does NOT contain `$`, `\`, or `"` in the base64-encoded section, so double quotes around `contents=` work without escaping. If you ever deploy a key that does contain these, use hex encoding (Method C below).

### Method B — `/user ssh-keys/add user= key=` (native, no file at all)

RouterOS v7 supports creating the SSH key directly without a file intermediary. This is the cleanest approach:

```routeros
/user ssh-keys/add user=shonuff key="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... wisp-backup"
```

Same line-length limitation as Method A (~170 chars over SSH). For keys that exceed this, use Method A (`/file/add contents=`) which is verified compatible.

**Note:** This method is verified working on RouterOS 7.18.2. The `user ssh-keys print` command requires the `full` group to read (or a user with policy privileges) — `write` group gets "Permission denied" when listing but the key import itself succeeds and SSH key auth works. Test via `ssh -i key user@host /system identity print`.

### Method C — Hex-encoded script (for long keys)

### Method C — Hex-encoded script (for long keys)

For RSA keys longer than the SSH line limit, write the key as a RouterOS script using hex escapes:

```
/system script add name=deploy-key source=":local key \"\\73\\73\\68...\"; :put \$key file=wisp-key.txt"
/system script run deploy-key
/user ssh-keys import public-key-file=wisp-key.txt user=wisp-backup
```

Generate the hex escape string from Python:

```python
key = "ssh-ed25519 ..."
hex_escaped = ''.join(f'\\{b:02X}' for b in key.encode())
```

## What does NOT work

### ❌ `:put ... file=` — Syntax error on RouterOS 7.18

```
:put "abc123" file=test.txt  # expected end of command
```

The `file=` parameter does not exist on `:put` in RouterOS 7.18. This is a frequently-seen outdated suggestion in forum posts. It was removed or never existed in 7.x.

### ❌ `:put ... file="test.txt"` — Same error, with or without quotes.

### ❌ `:convert from=base64` with long strings truncated by SSH line limit

The `:convert from=base64` command itself works (verified with short test), but the base64 string + command prefix exceeds RouterOS's SSH line length limit (~170 chars).

### ❌ SCP/SFTP — "exec request failed on channel 0"

MikroTik RouterOS does not support the SCP or SFTP subsystems over SSH (verified on 7.18.2). Even with `scp -O` legacy protocol flag, the router rejects the channel request.

### ❌ FTP — disabled by default

FTP service is installed but disabled (`/ip service print` shows `X` flag). You can temporarily enable it, but the `write` user group has `!ftp` policy by default. The `full` group does have FTP. Enabling FTP+changing user group is possible but a hassle vs Method A.

## Key verification

After importing, verify key-based auth works:

```bash
ssh -i ~/.ssh/wisp_rsa user@192.168.88.1 /system identity print
```

Once an SSH key is assigned to a RouterOS user, password-based SSH login is automatically disabled for that user (RouterOS v7 behavior, not a bug).

## Permission notes

- **`write` group:** Can *use* SSH keys (key auth works), but trying `/user ssh-keys print` returns "Permission denied". Import still succeeds (`exit 0`). Test via `ssh -i key user@host /system resource print` instead.
- **`full` group:** Can list and manage SSH keys. If you need to verify keys from SSH, use `full` or `read` group with correct policy.
- The `!ftp` policy in `write` group blocks both FTP *and* `/tool/fetch` — anything that tries the FTP subsystem. Not a bug, documented behavior.

## Cleanup

Remove the intermediate `.txt` key file after successful import:

```
/file/remove [find name="wisp-key.txt"]
```
