SSH
SSH is the encrypted protocol for remote login and Git transport. This page covers key generation, ssh-agent, the 1Password SSH agent, commit signing, and setting up Windows as an SSH server.
Configuration#
The settings below are independent. Use the ones you need.
Generate an SSH key#
Generate an Ed25519 key. Replace your_email@example.com with your email address:
Start ssh-agent on Windows#
On Windows, start the ssh-agent service before adding keys:
Connect to GitHub over the HTTPS port#
Add this to ~/.ssh/config:
Configure a proxy#
Add this to a Host block in ~/.ssh/config:
Use the 1Password SSH agent#
The private key stays in 1Password and never touches disk. The 1Password agent handles both SSH authentication and commit signing, and each use requires biometric approval:
Only public keys stay on disk. ssh uses them to locate the matching private key in the agent.
Add this to ~/.ssh/config:
Key points:
- Put the wildcard block at the end of the file. Most ssh_config options take the first matching value, so a specific
Hostmust come first to override the wildcard. - Put only
IdentityAgentin the wildcard block. agent.toml decides which keys the agent offers and in what order. - Point
IdentityFileat the public key (.pub). ssh uses it to find the matching private key in the agent. No private key file is needed locally. - Put
IdentityFileandIdentitiesOnlyin the specificHost.IdentityFileis cumulative. UnderHost *, the host that needs a dedicated key gets the general key first. In the specificHost,IdentitiesOnly yesapplies only to that host, and every other host keeps using the agent.
Configure the 1Password key allowlist#
List the keys to offer in ~/.config/1Password/ssh/agent.toml. Set item to the 1Password item title:
- Without this file, the agent only offers SSH keys from the default vault (Personal / Private / Employee). If your keys are in custom vaults, this file is required.
- The agent offers keys in the order they appear in the file. With several keys, use the order to stay under the six-attempt limit most servers impose.
- Item titles must match exactly. If you rename an item in 1Password, update this file too. See Troubleshooting.
Sign commits with 1Password#
-
Add this to
~/.gitconfig. Setsigningkeyto the full public key: -
Create
~/.config/git/allowed_signerswith one signer per line:Without this file, local
git log --show-signaturefails to find a trusted signer. -
Add the public key to GitHub a second time, with key type Signing Key. Authentication keys and signing keys are added separately.
Name keys consistently#
The comment at the end of a public key plays no part in authentication. It only identifies the key. Use the same value in these three places, and use the item title rather than an email address:
Do not blank the comment. With several entries in authorized_keys and no comments, you have to compare fingerprints line by line with ssh-keygen -lf.
Browser autofill inserts the public key without a comment. If the target platform writes the key verbatim into authorized_keys, use ssh-copy-id instead. It carries the comment from the local .pub.
Remove Git identity from servers#
If you do not commit on a server, remove the Git identity settings:
Without user.email, git commit fails with *** Please tell me who you are., which prevents accidental commits on the server. Options without personal data, such as insteadOf, can stay for clone and pull.
For a one-off commit, pass the identity on the command line:
Keep a fallback login path#
If the only copy of the private key is in 1Password and password login is disabled, there is a single way in. A locked account, a service outage, or a lost device without a saved Emergency Kit locks you out.
Use the cloud console's VNC or serial console as a fallback. It bypasses SSH and adds no attack surface. Verify once that it works.
Install a public key on a Windows server#
Administrator accounts on Windows do not read ~/.ssh/authorized_keys. The Windows sshd_config ends with this block:
For any account in the Administrators group, the key must go into C:\ProgramData\ssh\administrators_authorized_keys. A key in the home directory is ignored without an error.
-
On the macOS client, run this command. Replace
<user>and<host>with the server's user name and address. The first run asks for a password:All three parts are required:
[IO.File]::WriteAllText: writes no BOM, and the trailing[char]10makes the line ending LF. With a BOM or CRLF, sshd treats the key as malformed.icacls /inheritance:r: tightens permissions. With looser permissions, sshd silently ignores the whole file.Restart-Service sshd: restarts sshd.
-
Verify on the server:
Set PowerShell 7 as the default shell on Windows#
An SSH session into Windows starts in cmd.exe by default. Switching to PowerShell 7 takes three registry values:
Write them in two stages. A wrong DefaultShell breaks ssh host 'command', and the registry then has to be fixed at the machine itself.
-
Optional: install PowerShell 7 if it is missing:
-
Write the first two values:
-
From the client, confirm that command mode works:
-
Write the third value:
No sshd restart is needed. New sessions pick up the change.
On a machine with only Windows PowerShell 5.1, use C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe as the path. 5.1 and 7 have separate $PROFILE files (WindowsPowerShell\ and PowerShell\), so settings in one do not apply to the other.
Usage#
Everyday commands for keys and connections.
View a public key#
macOS / Linux:
Windows (PowerShell):
Test the SSH connection#
Test the connection to GitHub:
Add a key to ssh-agent#
macOS / Linux:
Windows (PowerShell):
List keys in ssh-agent#
List the keys that have been added:
Remove a key from ssh-agent#
Remove a specific key on macOS / Linux:
Windows (PowerShell):
Remove all keys:
Agent forwarding#
Before you enable agent forwarding, note:
- Root on the remote host can read the agent socket under
/tmpand use your key to reach any machine that trusts you. Enable forwarding only for machines whose ownership is clear. - Forwarding is per session. Long-lived sessions (tmux, multiplexer panes) inherit the socket path of the connection that created them, and it stops working when that connection closes. Pinning the path with a symlink does not help.
- Do not set
commit.gpgSign = trueon remote hosts, orgit commitfails in any session without an agent. - For automation, use a dedicated deploy key instead of a forwarded personal key.
Verify the 1Password setup#
Check the agent, authentication, and signing. Replace host, FILE, and OWNER/REPO with real values:
Before you delete a local private key, run ssh-keygen -Y sign. ssh-add -l only proves that the agent knows about the key. A successful signature proves that it holds a usable private key.
Troubleshooting#
The problems below are listed by symptom.
Connection succeeds with the wrong identity#
If your default key is also a valid account on the same server, the server accepts it. The connection and commands work, but the identity is wrong.
Compare the fingerprint on the Server accepts key line of ssh -v with the output of ssh-add -l. Then set IdentityFile and IdentitiesOnly yes in that host's Host block. See Use the 1Password SSH agent.
Load key reports invalid format#
Cause: the 1Password item was renamed but item in agent.toml was not updated, so the agent offers no keys (The agent has no identities). ssh then reads the public key as if it were private. SSH, Git, and signing all fail at once.
- Change
itemin~/.config/1Password/ssh/agent.tomlto the current item title. - Run
ssh-add -land confirm that the agent lists the key.
GitHub shows commits as Unverified#
Cause: the public key was added only as an authentication key, not as a signing key.
- Add the public key to GitHub again, with key type Signing Key.
- Check
verification.reasonwith thegh apicommand in Verify the 1Password setup:unsigned: the commit has no signature.unknown_key: the platform does not recognize the key (not added, or added as an Authentication key).bad_email: the email linked to the signing key does not match the commit author.
Windows server still asks for a password#
The cause is usually one of the following, and the log gives no clear hint:
- The account is in the Administrators group, and the key was written to
authorized_keysin the home directory. - Permissions on
administrators_authorized_keyswere not tightened, so sshd ignores the whole file. - The file has a BOM or CRLF line endings, so sshd treats the key as malformed.
Write the key again as described in Install a public key on a Windows server, and run the verification commands there.
SSH sessions show errors after switching to PowerShell#
While the default shell is cmd.exe, the profile never loads. After switching to PowerShell, existing problems in the profile show up for the first time.
On Windows 11 24H2 and later, the most common one is "untrusted mount point": SSH sessions cannot read some symbolic links and junctions, so tools that rely on them (fnm, the WinGet shims, pnpm, vite-plus) fail. To fix it, recreate blocked junctions with mklink /J, put %LOCALAPPDATA%\Microsoft\WinGet\Packages\<package ID> at the front of the user PATH to bypass the WinGet shims, and install or upgrade locally or over Remote Desktop.