from line 1186
11861186
11871187To find out who connected to a remote machine, read the SSH server's log on that machine, where a login from Desktop looks like one from a terminal. With Desktop on macOS or Linux, expect an entry each time Desktop connects, not one for each session.
11881188
1189You won't find Desktop's SSH connections in Claude Code's [metrics and events](/docs/en/monitoring-usage#available-metrics-and-events). To find out from them which remote machine an SSH session ran on, see [Attribute telemetry to Desktop SSH sessions](/docs/en/monitoring-usage#attribute-telemetry-to-desktop-ssh-sessions). For Desktop's own record of its connections, see [Get Desktop's logs for an SSH problem](#get-desktops-logs-for-an-ssh-problem).
1190
1191#### Connections that close before login
1192
1193If the SSH server's log shows connections that close before any login, they can be Desktop's tests of the SSH port. Desktop tests whether a machine answers, for example after a connection to it failed. It opens a connection to the port and closes it at once, without sending anything. Expect one test or several in a row, for example eight in about two minutes.
1194
1195If nothing bans or refuses addresses for such connections, you don't have to do anything about Desktop's tests. If an address that Desktop connects from is refused or banned, check [whether the SSH server itself refuses the address](#check-whether-the-ssh-server-itself-refuses-the-address) and [whether fail2ban counts Desktop's tests](#check-whether-fail2ban-counts-desktops-tests).
1196
1197##### Recognize a test in the SSH server's log
1198
1199Compare the lines in your log with those that an OpenSSH server on Linux writes for a test at the default `LogLevel`, in the row for your server's version. You can see the lines in the last column instead if the server's greeting arrives before Desktop closes the connection:
1200
1201| OpenSSH version | Lines for a test | Lines if the greeting arrives first |
1202| - | - | - |
1203| 6.6 to 7.9 | A line with `Did not receive identification string from 203.0.113.7` | The same line |
1204| 8.0 to 8.2 | `error: kex_exchange_identification: Connection closed by remote host` | `error: kex_exchange_identification: read: Connection reset by peer` |
1205| 8.3 to 9.4 | `error: kex_exchange_identification: Connection closed by remote host`, then `Connection closed by 203.0.113.7 port 51234` | `error: kex_exchange_identification: read: Connection reset by peer`, then `Connection reset by 203.0.113.7 port 51234` |
1206| 9.5 to 10.2 | `Connection closed by 203.0.113.7 port 51234` | `error: kex_exchange_identification: read: Connection reset by peer`, then `Connection reset by 203.0.113.7 port 51234` |
1207| 10.3 to 10.5 | `Connection closed by 203.0.113.7 port 51234 [preauth]` | `error: kex_exchange_identification: read: Connection reset by peer [preauth]`, then `Connection reset by 203.0.113.7 port 51234 [preauth]` |
1208
1209Other programs that open the port and close it can leave the same lines. Where a line has an address, check that it is one that a computer with Desktop connects from.
1210
1211The lines of 8.0 to 8.2 have no address. To get one, set `LogLevel VERBOSE` in `sshd_config` and run `sshd -T | grep -i loglevel` as root. If it prints `loglevel VERBOSE`, reload the SSH server. Then look for a line with `Connection from 203.0.113.7 port 51234` before the test's line.
1212
1213<a id="check-whether-the-ssh-server-itself-refuses-the-address" />
1214
1215##### Check whether the SSH server itself refuses the address
1216
1217If connections are refused from an address that many computers with Desktop share, look in the SSH server's log for a line that ends like this one:
1218
1219```text theme={null}
1220drop connection #0 from [203.0.113.7]:51234 on [192.0.2.10]:22 penalty: connections without attempting authentication
1221```
1222
1223With the default `LogLevel`, OpenSSH 9.8 and later can write such a line when refusing an address for connections that closed without trying to log in, as Desktop's tests do. With the default settings, you don't see such a line after a few tests alone.
1224
1225To prevent the refusal, set `PerSourcePenaltyExemptList` in `sshd_config` to the addresses that people connect from, for example `PerSourcePenaltyExemptList 203.0.113.7`. The server then applies none of its penalties to those addresses, including those for failed logins. To check the file, run `sshd -T | grep -i persourcepenalt` as root. If it prints your list, reload the SSH server.
1226
1227<a id="check-whether-fail2ban-counts-desktops-tests" />
1228
1229##### Check whether fail2ban counts Desktop's tests
1230
1231If fail2ban 0.10.5 or later banned an address that Desktop connects from, find your jail's mode in `jail.conf`, `jail.local` or under `jail.d/`:
1232
1233* **A `filter` line that names a mode in brackets**, such as `filter = sshd[mode=aggressive]`: use that mode.
1234* **Only the stock `filter` line**, with `[mode=%(mode)s]`: use the `mode` line, or `normal` if there is none.
1235
1236If the mode is `normal`, fail2ban doesn't count the lines of Desktop's tests, so look for another cause of the ban.
1237
1238For another mode, copy the lines from your own log to a file on the remote machine. Copy each line whole, from its date to its end, and both lines where a test leaves two. Run `fail2ban-regex` there with the file and your jail's mode, for example `aggressive`:
1239
1240```bash theme={null}
1241fail2ban-regex -o ip lines.log "sshd[mode=aggressive]"
1242```
1243
1244For each failure that fail2ban counts, the command prints the address on a line of its own, such as `203.0.113.7`. In that case, [prevent a ban](#prevent-a-ban-by-fail2ban). If it prints nothing, fail2ban counts none of those lines in that mode, so look for another cause of the ban.
1245
1246<a id="prevent-a-ban-by-fail2ban" />
1247
1248##### Prevent a ban by fail2ban
1249
1250To keep fail2ban from banning an address for Desktop's tests, change one of two settings of the jail, each at a cost:
1251
1252* **`mode`**: set it to `normal` wherever your jail sets it, in a `mode` line or in the brackets of a line such as `filter = sshd[mode=aggressive]`. You give up every count that only the other modes make, for example of clients that connect and leave without trying to log in.
1253* **`ignoreip`**: set it to the addresses that people connect from, for example `ignoreip = 203.0.113.7`. fail2ban then counts nothing from those addresses, failed logins included.
1254
11891255#### Use MCP servers set up on the remote machine
11901256
11911257To use an [MCP server](/docs/en/mcp) from `~/.claude.json` or a project's `.mcp.json` in an SSH session, set it up in those files on the remote machine. Claude Code starts their stdio servers there, so install the commands those servers run on the remote machine. For servers from `claude_desktop_config.json`, see [Limits while Desktop is disconnected](#limits-while-desktop-is-disconnected).
from line 1699
16331699| `Claude Code won't start on <host>` | `[CLI_UNRESPONSIVE]` | Wait a minute, then try again. If it keeps failing, run the command that the message names on the remote machine to see whether Claude Code answers there. |
16341700
16351701For any other message, check the [remote machine requirements](#remote-machine-requirements-for-ssh-sessions).
1702
1703<a id="get-desktops-logs-for-an-ssh-problem" />
16361704
16371705### Get Desktop's logs for an SSH problem
16381706
No line in this hunk matches that.