macOS Tahoe 26.3 Broke SSH: Case Sensitivity Changes, DSA Key Deprecation & Complete Fix Guide

macOSTahoe ยท
macOS Tahoe 26.3 Broke SSH: Case Sensitivity Changes, DSA Key Deprecation & Complete Fix Guide

macOS Tahoe 26.3 silently changed SSH username handling to case-sensitive and disabled ssh-dss keys. Complete guide to fixing SSH connections, updating configs, and migrating to Ed25519 keys.

Apple released macOS Tahoe 26.3 on February 11, 2026, and for thousands of developers and system administrators, SSH connections immediately broke. No warning dialog. No migration assistant. No release note calling it out. One day you could ssh administrator@yourserver and the next day you got Connection closed by 10.10.83.28 port 22. If you relied on DSA keys to authenticate against legacy infrastructure, those stopped working entirely. This guide explains exactly what changed, why it happened, and how to fix every affected workflow -- from interactive sessions to automated CI/CD pipelines.

Key Takeaways

  • macOS Tahoe 26.3 enforces case-sensitive SSH usernames. Typing administrator instead of Administrator now results in an immediate connection closure with no password prompt.
  • ssh-dss (DSA) keys are permanently removed. OpenSSH 10.0, bundled with Tahoe 26.3, compiled out DSA support entirely. There is no configuration flag to re-enable it.
  • Post-quantum key exchange warnings now appear when connecting to servers that do not support mlkem768x25519-sha256 or sntrup761x25519-sha512.
  • Ed25519 is the recommended key type for all new SSH keys. It is faster, more secure, and produces smaller signatures than RSA.
  • Every ~/.ssh/config file should be audited for case mismatches in User directives and any references to ssh-dss in HostKeyAlgorithms or PubkeyAcceptedAlgorithms.

Table of Contents

  1. What Exactly Changed in macOS Tahoe 26.3 SSH
  2. Diagnosing SSH Connection Failures After the Update
  3. Understanding the Case Sensitivity Change
  4. Understanding the DSA Key Removal
  5. Post-Quantum Key Exchange Warnings
  6. Step-by-Step Fix: Case Sensitivity Issues
  7. Step-by-Step Fix: Migrating from DSA to Ed25519
  8. Complete SSH Config Audit Guide
  9. SSH Agent Changes and Keychain Integration
  10. Impact on CI/CD Pipelines and Automation
  11. Impact on Remote Development Workflows
  12. Workarounds for Legacy Infrastructure
  13. SSH Security Best Practices for 2026
  14. Frequently Asked Questions
  15. Conclusion

What Exactly Changed in macOS Tahoe 26.3 SSH

macOS Tahoe 26.3 ships with OpenSSH 10.0, which introduces three breaking changes that affect daily SSH workflows. Apple did not highlight any of these in the user-facing release notes, leaving developers to discover them through failed connections.

Change 1: Case-Sensitive Username Matching

Prior to Tahoe 26.3, the SSH client on macOS was lenient about username case. You could connect with ssh administrator@server, ssh Administrator@server, or even ssh ADMINISTRATOR@server, and the remote server would resolve the username correctly as long as the underlying operating system matched the account. After the update, the SSH client enforces strict case matching before the connection is even fully established. If the case does not match what the remote server expects, the connection is closed immediately -- before password authentication or key exchange completes.

Change 2: Complete DSA (ssh-dss) Removal

OpenSSH has been deprecating DSA since version 7.0 in 2015. The deprecation followed a clear timeline:

DateOpenSSH VersionDSA Status
20157.0Disabled by default, re-enable via config
20249.xCompile-time optional, enabled by default
Mid-20249.xCompile-time optional, disabled by default
April 202510.0Removed from codebase entirely
February 202610.0 (macOS Tahoe 26.3)Ships to all macOS users

With macOS Tahoe 26.3, there is no PubkeyAcceptedAlgorithms +ssh-dss workaround. The algorithm is not present in the binary. Any SSH configuration that references ssh-dss will produce a Bad key types error.

Change 3: Post-Quantum Key Exchange Warnings

OpenSSH 10.0 introduced a warning that triggers when connecting to servers that do not support post-quantum key exchange algorithms. The warning reads:

WARNING: connection is not using a post-quantum key exchange algorithm.
This session may be vulnerable to 'store now, decrypt later' attacks.

While this is a warning rather than a hard failure, it creates noise in terminal output and can break automated scripts that parse SSH output.

What Did Not Change

It is worth noting what remains the same:

  • RSA keys continue to work (with rsa-sha2-256 and rsa-sha2-512 signature algorithms)
  • ECDSA keys continue to work (though Ed25519 is preferred)
  • SSH port forwarding, tunneling, and ProxyJump all function identically
  • The ~/.ssh/known_hosts file format has not changed

If you are new to SSH on macOS or setting up a development environment from scratch, our macOS Tahoe developer setup guide covers the full initial configuration.


Diagnosing SSH Connection Failures After the Update

Before making any changes, you need to determine which of the three issues is affecting your connections. SSH verbose output is your primary diagnostic tool.

Step 1: Enable Verbose SSH Output

Run your failing SSH command with triple-verbose flags to see the full handshake negotiation:

ssh -vvv user@hostname

This produces detailed output showing every stage of the connection. Look for these specific patterns:

Case sensitivity failure pattern:

debug1: Connecting to 10.10.83.28 [10.10.83.28] port 22.
debug1: Connection established.
debug1: identity file /Users/you/.ssh/id_ed25519 type 3
debug3: receive packet: type 7
Connection closed by 10.10.83.28 port 22

The key indicator is that the connection closes after establishment but before any authentication attempt. There is no "Permission denied" message -- the connection simply drops.

DSA key failure pattern:

/Users/you/.ssh/config line 15: Bad key types '+ssh-dss'.

or:

Bad key types '+ssh-rsa,ssh-dss'

This error appears immediately when the SSH client parses your config file, before any connection attempt.

Post-quantum warning pattern:

WARNING: connection is not using a post-quantum key exchange algorithm.
This session may be vulnerable to 'store now, decrypt later' attacks.

This appears during key exchange but does not prevent the connection from completing.

Step 2: Check Your SSH Client Version

Verify which OpenSSH version is running:

ssh -V

Expected output on macOS Tahoe 26.3:

OpenSSH_10.0p1, LibreSSL 3.3.6

If you see an older version, you may have a Homebrew-installed OpenSSH taking priority. Check which binary is being used:

which ssh

The system SSH is at /usr/bin/ssh. If you see /opt/homebrew/bin/ssh or /usr/local/bin/ssh, you are using a Homebrew version that may behave differently.

Step 3: Test with Explicit Parameters

Isolate the issue by testing with explicit username case and key type:

# Test case sensitivity - try both cases
ssh -vvv Administrator@hostname
ssh -vvv administrator@hostname

# Test without any config file interference
ssh -F /dev/null -vvv user@hostname

# Test with a specific key
ssh -vvv -i ~/.ssh/id_ed25519 user@hostname

Terminal SSH Debug Output

Step 4: Review SSH Config for Errors

Parse your config file to identify any lines referencing deprecated algorithms:

# Check for any ssh-dss references
grep -n "ssh-dss" ~/.ssh/config

# Check for any ssh-dss references in system config
grep -n "ssh-dss" /etc/ssh/ssh_config

# Check all User directives for case review
grep -n "User " ~/.ssh/config

For a deeper understanding of Terminal usage on macOS Tahoe, refer to our macOS Tahoe Terminal guide.


Understanding the Case Sensitivity Change

Why This Change Breaks Connections

On most Unix-like systems, usernames are case-sensitive at the operating system level. A user named Administrator is technically a different account than administrator. However, the SSH protocol itself (RFC 4252) states that usernames are "case-sensitive" but acknowledges that server implementations may choose to handle this differently.

Before macOS Tahoe 26.3, the macOS SSH client was lenient. When you typed ssh administrator@server, the client would send the username as-is, and many SSH servers (particularly those running on Windows, macOS, or network appliances) would normalize the case during authentication. The connection would succeed because the server did the heavy lifting of matching administrator to the Administrator account.

With OpenSSH 10.0, the SSH client on macOS now performs a stricter pre-authentication check. If the server returns an early protocol message indicating a case mismatch, the connection is terminated before authentication proceeds. This means:

  • Connections to Windows servers (which historically normalized case) may break
  • Connections to network appliances (routers, switches, NAS devices) that use case-insensitive usernames may break
  • Connections to macOS servers where the admin account is Administrator (capital A) but scripts use administrator (lowercase a) will break
  • Connections to Linux servers are less likely to be affected since Linux usernames are already case-sensitive

Common Scenarios That Break

Scenario 1: macOS to macOS Remote Management

Many system administrators connect to remote Macs using:

ssh [email protected]

But the default macOS admin account is created as Administrator with a capital A. This worked before Tahoe 26.3 and breaks after.

Scenario 2: Network Appliance Management

Connecting to routers, switches, firewalls, and NAS devices often uses generic usernames:

ssh [email protected]
ssh [email protected]

If the device stores the username as Admin or ROOT, these connections will now fail silently.

Scenario 3: SSH Config Aliases

Many developers have SSH config entries with incorrect case that previously worked:

# ~/.ssh/config - this may now break
Host production
    HostName prod.example.com
    User deploy      # Server expects "Deploy"
    IdentityFile ~/.ssh/prod_key

Understanding the DSA Key Removal

The Security Case Against DSA

DSA (Digital Signature Algorithm) as implemented in SSH uses a fixed 1024-bit key length and SHA-1 for hashing. This combination provides approximately 80 bits of symmetric security -- far below the 128-bit minimum recommended by modern security standards.

The specific weaknesses of DSA keys:

PropertyDSA (ssh-dss)Ed25519RSA-4096
Key size1024 bits256 bits4096 bits
Hash algorithmSHA-1SHA-512SHA-256/512
Security level~80 bits~128 bits~128 bits
Signature size40 bytes64 bytes512 bytes
Sign speedSlowFastModerate
Verify speedSlowFastModerate
Vulnerability to biasHighNoneNone

DSA is particularly vulnerable to implementation flaws. If the random number generator used during signing has any bias, the private key can be recovered from a small number of signatures. This has been exploited in real-world attacks.

What "Removed" Actually Means

In previous macOS versions, DSA was disabled by default but could be re-enabled with configuration options:

# This used to work on macOS Sequoia and earlier
Host legacy-server
    HostKeyAlgorithms +ssh-dss
    PubkeyAcceptedAlgorithms +ssh-dss

In macOS Tahoe 26.3, these configuration lines themselves cause errors because the SSH binary no longer recognizes ssh-dss as a valid algorithm identifier. The error is:

/Users/you/.ssh/config line 8: Bad key types '+ssh-dss'.

This means:

  1. You cannot use DSA keys for authentication (public key login)
  2. You cannot accept DSA host keys from servers
  3. You cannot even reference ssh-dss in your configuration files without causing errors
  4. The ssh-keygen tool can no longer generate DSA keys

Checking If You Have DSA Keys

Before proceeding with migration, check your existing keys:

# List all SSH keys with their types
for key in ~/.ssh/*.pub; do
    echo "=== $key ==="
    ssh-keygen -l -f "$key"
done

Example output:

=== /Users/you/.ssh/id_dsa.pub ===
1024 SHA256:abc123... you@mac (DSA)          # This key must be replaced
=== /Users/you/.ssh/id_ed25519.pub ===
256 SHA256:xyz789... you@mac (ED25519)       # This key is fine
=== /Users/you/.ssh/id_rsa.pub ===
3072 SHA256:def456... you@mac (RSA)          # This key is fine

Any key identified as (DSA) must be replaced. If you find one, do not delete it yet -- you need it to identify which servers have it authorized.


Post-Quantum Key Exchange Warnings

What the Warning Means

OpenSSH 10.0 introduced post-quantum key exchange using the ML-KEM (previously CRYSTALS-Kyber) algorithm hybridized with X25519. The default key exchange algorithm is now mlkem768x25519-sha256, with sntrup761x25519-sha512 as a fallback.

When connecting to a server that supports neither of these algorithms, OpenSSH displays:

WARNING: connection is not using a post-quantum key exchange algorithm.
This session may be vulnerable to 'store now, decrypt later' attacks.

A "store now, decrypt later" attack means an adversary could record your encrypted SSH traffic today and decrypt it in the future when quantum computers become capable of breaking classical key exchange algorithms.

When You Will See This Warning

  • Connecting to servers running OpenSSH 8.x or earlier
  • Connecting to network appliances with embedded SSH implementations
  • Connecting to cloud instances with older SSH daemon versions
  • Connecting to GitHub, GitLab, or Bitbucket (may or may not support PQ algorithms yet)

Silencing the Warning

If you understand the risk and want to suppress the warning, add this to your ~/.ssh/config:

# Suppress post-quantum warnings globally
Host *
    WarnWeakCrypto no

Or for specific hosts only:

Host legacy-server
    HostName 192.168.1.100
    WarnWeakCrypto no

The ideal solution is to update your servers to OpenSSH 9.0 or later, which supports sntrup761x25519-sha512, or OpenSSH 9.9+, which supports the newer mlkem768x25519-sha256.


Step-by-Step Fix: Case Sensitivity Issues

Fix 1: Determine the Correct Username Case

First, verify the exact username on the remote system. The method depends on the remote OS:

For remote Linux/macOS servers:

# If you can still log in via console, physical access, or another SSH client
grep "your_username" /etc/passwd

# Or use id command
id

# Output example: uid=501(Administrator) gid=20(staff) groups=...

For remote Windows servers (OpenSSH):

# In PowerShell on the remote machine
whoami
# Output: DOMAIN\Administrator

For network appliances:

Check the device's web management interface or documentation for the exact username format. Common patterns:

  • Cisco IOS: typically lowercase (admin)
  • Juniper: typically lowercase (root)
  • Synology NAS: case-sensitive as created
  • QNAP NAS: case-sensitive as created

Fix 2: Update Your SSH Config File

Open your SSH config and correct all User directives:

# Open the config file
nano ~/.ssh/config

Before (broken):

Host production
    HostName prod.example.com
    User administrator
    IdentityFile ~/.ssh/prod_key

Host nas
    HostName nas.local
    User admin
    Port 22

After (fixed):

Host production
    HostName prod.example.com
    User Administrator
    IdentityFile ~/.ssh/prod_key

Host nas
    HostName nas.local
    User Admin
    Port 22

Fix 3: Update Shell Aliases and Scripts

Search for SSH commands in your shell configuration:

# Check bash aliases
grep -n "ssh " ~/.bashrc ~/.bash_profile 2>/dev/null

# Check zsh aliases
grep -n "ssh " ~/.zshrc ~/.zprofile 2>/dev/null

# Check for SSH commands in custom scripts
grep -rn "ssh " ~/scripts/ ~/bin/ 2>/dev/null

Update any hardcoded usernames to use the correct case.

Fix 4: Update Environment Variables

Some tools use environment variables for SSH usernames:

# Check for SSH-related environment variables
env | grep -i ssh
env | grep -i user

# Common variables to check
echo $DEPLOY_USER
echo $SSH_USER
echo $REMOTE_USER

Fix 5: Verify the Fix

After making changes, verify each connection:

# Test with verbose output
ssh -v production

# Expected: successful authentication
# debug1: Authentication succeeded (publickey).

Step-by-Step Fix: Migrating from DSA to Ed25519

This section walks through the complete process of replacing DSA keys with Ed25519 keys. If you need background on Terminal commands, our Mac Terminal mastery guide covers all the fundamentals.

SSH Key Migration Flow

Phase 1: Inventory Your DSA Keys

Before generating new keys, document where your DSA keys are used:

# Step 1: Find all DSA keys on your system
find ~/.ssh -name "*.pub" -exec grep -l "ssh-dss" {} \;

# Step 2: For each DSA key, check which servers authorize it
# Note the public key fingerprint
ssh-keygen -l -f ~/.ssh/id_dsa.pub

# Step 3: Check known_hosts for DSA host keys
grep "ssh-dss" ~/.ssh/known_hosts | awk '{print $1}'

Create a migration checklist:

# Create a migration tracking file
cat > ~/ssh_migration_checklist.txt << 'EOF'
SSH DSA Key Migration Checklist
===============================
Date: $(date)

DSA Keys Found:
- [ ] ~/.ssh/id_dsa -> Used for: _______________
- [ ] ~/.ssh/id_dsa.pub -> Authorized on: _______________

Servers with DSA Host Keys:
- [ ] server1.example.com
- [ ] 192.168.1.100

Automation/Scripts Using DSA:
- [ ] deploy.sh
- [ ] backup-cron.sh
- [ ] CI/CD pipeline: _______________
EOF

Phase 2: Generate New Ed25519 Keys

Generate a new Ed25519 key pair with a meaningful comment:

# Generate Ed25519 key with descriptive comment
ssh-keygen -t ed25519 -C "yourname@macbook-2026-02" -f ~/.ssh/id_ed25519

# You'll be prompted for a passphrase - USE ONE
# A passphrase protects your private key if your machine is compromised

If you need separate keys for different purposes (recommended for security isolation):

# Key for work servers
ssh-keygen -t ed25519 -C "work-servers-2026" -f ~/.ssh/id_ed25519_work

# Key for personal projects
ssh-keygen -t ed25519 -C "personal-2026" -f ~/.ssh/id_ed25519_personal

# Key for CI/CD automation
ssh-keygen -t ed25519 -C "cicd-automation-2026" -f ~/.ssh/id_ed25519_cicd

Verify the generated keys:

# Check the new key
ssh-keygen -l -f ~/.ssh/id_ed25519.pub

# Expected output:
# 256 SHA256:abcdef123456... yourname@macbook-2026-02 (ED25519)

Phase 3: Deploy New Keys to Servers

For each server that previously used your DSA key, deploy the new Ed25519 public key:

Method 1: Using ssh-copy-id (if you can still authenticate)

If you have another working authentication method (password, different key):

# Deploy the new key using password authentication
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

# Or specify a working key for authentication during deployment
ssh-copy-id -i ~/.ssh/id_ed25519.pub -o "IdentityFile=~/.ssh/id_rsa" user@server

Method 2: Manual deployment (if ssh-copy-id is not available)

# Copy the public key content
cat ~/.ssh/id_ed25519.pub | pbcopy

# SSH into the server using whatever method works (password, console, etc.)
ssh user@server

# On the remote server, add the key
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "PASTE_YOUR_PUBLIC_KEY_HERE" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Method 3: For servers you cannot SSH into

If the DSA key was your only authentication method and it no longer works:

  1. Use the server's web management console (if available)
  2. Access via physical console or IPMI/iLO/iDRAC
  3. Use a jump host that still has access
  4. Contact the server administrator

Phase 4: Update SSH Config

Replace DSA key references in your SSH config:

# Before
Host old-server
    HostName legacy.example.com
    User admin
    IdentityFile ~/.ssh/id_dsa
    HostKeyAlgorithms +ssh-dss
    PubkeyAcceptedAlgorithms +ssh-dss

# After
Host old-server
    HostName legacy.example.com
    User Admin
    IdentityFile ~/.ssh/id_ed25519_work

Phase 5: Clean Up Known Hosts

Remove stale DSA host key entries:

# Find servers with DSA host keys in known_hosts
grep "ssh-dss" ~/.ssh/known_hosts

# Remove specific host entries (the host key will be re-accepted on next connection)
ssh-keygen -R hostname_or_ip

# Or remove all DSA entries at once
# First backup the file
cp ~/.ssh/known_hosts ~/.ssh/known_hosts.backup.$(date +%Y%m%d)

# Then remove DSA entries
grep -v "ssh-dss" ~/.ssh/known_hosts > ~/.ssh/known_hosts.tmp
mv ~/.ssh/known_hosts.tmp ~/.ssh/known_hosts

Phase 6: Verify Everything Works

Test each server connection:

# Test connections with verbose output
ssh -v old-server

# Verify the correct key is being offered
# Look for: debug1: Offering public key: /Users/you/.ssh/id_ed25519_work ED25519

# Run a full connectivity test for all configured hosts
for host in $(grep "^Host " ~/.ssh/config | awk '{print $2}' | grep -v '\*'); do
    echo "Testing: $host"
    ssh -o ConnectTimeout=5 -o BatchMode=yes "$host" echo "OK" 2>&1
    echo "---"
done

Phase 7: Retire the DSA Key

Once all servers are migrated and verified:

# Archive the old DSA keys (don't delete immediately)
mkdir -p ~/.ssh/deprecated
mv ~/.ssh/id_dsa ~/.ssh/deprecated/id_dsa.retired.$(date +%Y%m%d)
mv ~/.ssh/id_dsa.pub ~/.ssh/deprecated/id_dsa.pub.retired.$(date +%Y%m%d)
chmod 400 ~/.ssh/deprecated/id_dsa.retired.*

Keep the archived keys for at least 90 days in case you discover a server you missed.


Complete SSH Config Audit Guide

After addressing individual issues, perform a complete audit of your SSH configuration. This is especially important if you have been using SSH on macOS for years and have accumulated configuration entries across multiple macOS upgrades.

Audit Step 1: Check for Deprecated Algorithms

# Check for ANY deprecated algorithm references
echo "=== Checking ~/.ssh/config ==="
grep -n -E "(ssh-dss|ssh-dsa|diffie-hellman-group1-sha1|diffie-hellman-group14-sha1|hmac-sha1-96|hmac-md5)" ~/.ssh/config 2>/dev/null

echo ""
echo "=== Checking /etc/ssh/ssh_config ==="
grep -n -E "(ssh-dss|ssh-dsa|diffie-hellman-group1-sha1)" /etc/ssh/ssh_config 2>/dev/null

echo ""
echo "=== Checking /etc/ssh/ssh_config.d/ ==="
grep -rn -E "(ssh-dss|ssh-dsa)" /etc/ssh/ssh_config.d/ 2>/dev/null

Audit Step 2: Validate Config Syntax

# Test SSH config syntax without connecting
ssh -G test-host 2>&1 | head -5

# If there are errors, they will appear as:
# /Users/you/.ssh/config line X: Bad key types '...'

Audit Step 3: Review All User Directives

# Extract all User directives and the hosts they apply to
awk '/^Host /{host=$2} /User /{print host, $0}' ~/.ssh/config

Review the output and verify the case of each username.

Audit Step 4: Check File Permissions

SSH is strict about file permissions. Incorrect permissions can cause silent failures:

# Check and fix SSH directory permissions
echo "=== Directory permissions ==="
ls -la ~/.ssh/

echo ""
echo "=== Expected permissions ==="
echo "~/.ssh/           -> drwx------ (700)"
echo "~/.ssh/config     -> -rw------- (600)"
echo "~/.ssh/id_*       -> -rw------- (600) for private keys"
echo "~/.ssh/*.pub      -> -rw-r--r-- (644) for public keys"
echo "~/.ssh/authorized_keys -> -rw------- (600)"
echo "~/.ssh/known_hosts     -> -rw-r--r-- (644)"

# Fix permissions if needed
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config 2>/dev/null
chmod 600 ~/.ssh/id_* 2>/dev/null
chmod 644 ~/.ssh/*.pub 2>/dev/null
chmod 600 ~/.ssh/authorized_keys 2>/dev/null

Here is a complete, modern SSH config template that works with macOS Tahoe 26.3:

# ~/.ssh/config
# Updated for macOS Tahoe 26.3 / OpenSSH 10.0
# Last audited: 2026-02-28

# ============================================
# Global defaults
# ============================================
Host *
    # Use Ed25519 keys by default
    IdentityFile ~/.ssh/id_ed25519

    # Only offer keys explicitly configured for each host
    IdentitiesOnly yes

    # macOS Keychain integration
    UseKeychain yes
    AddKeysToAgent yes

    # Modern key exchange preferences
    KexAlgorithms mlkem768x25519-sha256,[email protected],curve25519-sha256,[email protected]

    # Connection stability
    ServerAliveInterval 60
    ServerAliveCountMax 3

    # Security hardening
    HashKnownHosts yes
    StrictHostKeyChecking ask

# ============================================
# Work servers
# ============================================
Host prod-web
    HostName prod-web.example.com
    User Deploy
    IdentityFile ~/.ssh/id_ed25519_work
    Port 22

Host staging
    HostName staging.example.com
    User Deploy
    IdentityFile ~/.ssh/id_ed25519_work
    Port 22

# ============================================
# Personal servers
# ============================================
Host homelab
    HostName 192.168.1.100
    User admin
    IdentityFile ~/.ssh/id_ed25519_personal

# ============================================
# Jump host configuration
# ============================================
Host internal-*
    ProxyJump bastion.example.com
    User admin
    IdentityFile ~/.ssh/id_ed25519_work

Host bastion.example.com
    User jump-user
    IdentityFile ~/.ssh/id_ed25519_work
    ForwardAgent no

SSH Agent Changes and Keychain Integration

macOS Tahoe 26.3 also brings subtle changes to how the SSH agent interacts with the macOS Keychain.

Keychain Integration

The macOS-specific UseKeychain directive continues to work in Tahoe 26.3, but its behavior has been refined:

# Add your new Ed25519 key to the macOS Keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

# Verify the key was added
ssh-add -l

If the key was added successfully, you will see:

256 SHA256:abcdef123456... yourname@macbook-2026-02 (ED25519)

Persistent Key Loading

Ensure your SSH config includes these directives to maintain key persistence across reboots:

Host *
    UseKeychain yes
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519

Without UseKeychain yes, you will need to run ssh-add after every reboot.

Checking the SSH Agent

# Check if the agent is running
echo $SSH_AUTH_SOCK

# List loaded keys
ssh-add -l

# If no keys are loaded, add them
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

For more details on macOS security configurations, see our macOS Tahoe security privacy guide.


Impact on CI/CD Pipelines and Automation

The SSH changes in macOS Tahoe 26.3 have significant implications for automated workflows. If your CI/CD pipeline runs on macOS runners (GitHub Actions, GitLab CI, Jenkins on Mac), these changes will affect you after the runner OS is upgraded.

GitHub Actions

If you use macos-latest or macos-15 runners, the SSH binary will eventually be updated. Audit your workflows now:

# .github/workflows/deploy.yml
# BEFORE (potentially broken)
- name: Deploy
  run: |
    ssh administrator@${{ secrets.DEPLOY_HOST }} "cd /app && git pull"

# AFTER (fixed)
- name: Deploy
  run: |
    ssh Administrator@${{ secrets.DEPLOY_HOST }} "cd /app && git pull"

Check your deployment secrets:

  • DEPLOY_USER -- verify the case matches the remote account
  • SSH_PRIVATE_KEY -- verify it is not a DSA key
  • KNOWN_HOSTS -- verify no DSA host key entries

GitLab CI

# .gitlab-ci.yml
deploy:
  before_script:
    # Check key type before deploying
    - ssh-keygen -l -f ~/.ssh/id_deploy
    # Ensure it is NOT DSA
    - |
      if ssh-keygen -l -f ~/.ssh/id_deploy | grep -q "DSA"; then
        echo "ERROR: DSA key detected. Migration required."
        exit 1
      fi
  script:
    # Use correct case for username
    - ssh -o StrictHostKeyChecking=no Deploy@$DEPLOY_HOST "deploy.sh"

Jenkins

For Jenkins pipelines running on macOS agents:

pipeline {
    agent { label 'macos' }
    stages {
        stage('Pre-flight SSH Check') {
            steps {
                sh '''
                    # Verify SSH version
                    ssh -V

                    # Check for DSA keys in credentials
                    for key in ~/.ssh/*.pub; do
                        if ssh-keygen -l -f "$key" | grep -q "DSA"; then
                            echo "WARNING: DSA key found: $key"
                            exit 1
                        fi
                    done

                    echo "SSH pre-flight check passed"
                '''
            }
        }
    }
}

Ansible Playbooks

If you run Ansible from a macOS control node:

# ansible.cfg
[defaults]
# Ensure correct SSH binary
ssh_executable = /usr/bin/ssh

# Force specific SSH arguments
[ssh_connection]
ssh_args = -o IdentitiesOnly=yes -o IdentityFile=~/.ssh/id_ed25519_ansible

Check your Ansible inventory for username case:

# inventory.ini
# BEFORE
[webservers]
web1.example.com ansible_user=administrator

# AFTER
[webservers]
web1.example.com ansible_user=Administrator

Automated Backup Scripts

Review any cron jobs or launchd tasks that use SSH:

# Find all cron jobs with SSH references
crontab -l 2>/dev/null | grep -i ssh

# Find launchd plists with SSH references
grep -rl "ssh" ~/Library/LaunchAgents/ /Library/LaunchAgents/ /Library/LaunchDaemons/ 2>/dev/null

# Check rsync backup scripts (rsync uses SSH by default)
grep -rn "rsync.*@" ~/scripts/ ~/bin/ 2>/dev/null

Impact on Remote Development Workflows

VS Code Remote SSH

Visual Studio Code's Remote SSH extension stores connection configurations that may be affected:

# VS Code SSH config location
cat ~/Library/Application\ Support/Code/User/settings.json | grep -A5 "remote.SSH"

# Check VS Code's SSH config references
grep -n "ssh" ~/Library/Application\ Support/Code/User/settings.json 2>/dev/null

Update your VS Code SSH targets to use correct username case and non-DSA keys.

JetBrains Gateway / Remote Development

JetBrains IDEs store SSH configurations separately:

# Check JetBrains SSH configs
find ~/Library/Application\ Support/JetBrains -name "*.xml" -exec grep -l "ssh" {} \; 2>/dev/null

tmux and Persistent Sessions

If you use tmux or screen over SSH, your reconnection scripts need updating:

# Common tmux reconnect alias - check username case
alias tm='ssh -t Administrator@server "tmux attach || tmux new"'

For a comprehensive developer environment setup, refer to our macOS Tahoe developer ultimate guide.


Workarounds for Legacy Infrastructure

Some environments cannot be immediately updated. Network appliances, embedded systems, and legacy servers may require DSA for years to come. Here are practical workarounds, ordered from most to least recommended.

Set up a Linux bastion host that still supports DSA and use it as a proxy:

# ~/.ssh/config
Host legacy-device
    HostName 192.168.1.1
    User admin
    ProxyJump bastion

Host bastion
    HostName bastion.example.com
    User jump
    IdentityFile ~/.ssh/id_ed25519

The bastion host runs an older OpenSSH (or one compiled with DSA support) and handles the DSA negotiation with the legacy device. Your Mac only needs to authenticate to the bastion using Ed25519.

Workaround 2: Use a Docker Container

Run an older OpenSSH client inside a Docker container:

# Create a Dockerfile for legacy SSH
cat > ~/legacy-ssh/Dockerfile << 'EOF'
FROM alpine:3.18
RUN apk add --no-cache openssh-client
ENTRYPOINT ["ssh"]
EOF

# Build it
docker build -t legacy-ssh ~/legacy-ssh/

# Use it to connect to legacy devices
docker run --rm -it \
    -v ~/.ssh:/root/.ssh:ro \
    legacy-ssh \
    -o HostKeyAlgorithms=+ssh-dss \
    -o PubkeyAcceptedKeyTypes=+ssh-dss \
    admin@legacy-device

Create a shell alias for convenience:

# Add to ~/.zshrc
alias legacy-ssh='docker run --rm -it -v ~/.ssh:/root/.ssh:ro legacy-ssh'

# Usage
legacy-ssh -o HostKeyAlgorithms=+ssh-dss admin@legacy-device

Workaround 3: Install an Older OpenSSH via Homebrew

You can compile OpenSSH 9.7p1 (the last version with DSA support) from source:

# Install dependencies
brew install openssl@3

# Download OpenSSH 9.7p1 source
curl -O https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.7p1.tar.gz
tar xzf openssh-9.7p1.tar.gz
cd openssh-9.7p1

# Configure with DSA support and custom prefix
./configure \
    --prefix=$HOME/openssh97 \
    --with-ssl-dir=$(brew --prefix openssl@3)

# Build and install
make -j$(sysctl -n hw.ncpu)
make install

# Use the older SSH binary
~/openssh97/bin/ssh -o HostKeyAlgorithms=+ssh-dss admin@legacy-device

Create an alias for legacy connections:

# Add to ~/.zshrc
alias ssh-legacy='~/openssh97/bin/ssh -o HostKeyAlgorithms=+ssh-dss -o PubkeyAcceptedKeyTypes=+ssh-dss'

# Usage
ssh-legacy admin@old-router

Workaround 4: Use a Linux Virtual Machine

If you use UTM, Parallels, or VMware Fusion:

  1. Set up a lightweight Linux VM (Alpine or Ubuntu Server)
  2. SSH from your Mac to the VM (using Ed25519)
  3. From the VM, SSH to the legacy device (using DSA)

This provides complete isolation and does not require modifying your macOS SSH installation.

Long-Term: Plan Device Replacement

If you are relying on DSA because your network appliances do not support modern algorithms, that is a clear signal those devices have reached end of life from a security perspective. Plan a replacement timeline:

  • Routers/Switches: Most enterprise vendors have supported Ed25519 since 2018
  • NAS devices: Synology, QNAP, and TrueNAS all support Ed25519
  • Legacy servers: If running OpenSSH older than 6.5, the entire system needs updating

SSH Security Best Practices for 2026

With the macOS Tahoe 26.3 changes as a catalyst, now is a good time to implement comprehensive SSH security improvements.

Key Management Best Practices

1. Use separate keys for separate roles:

# Work infrastructure
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work -C "work-2026"

# Personal projects
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_personal -C "personal-2026"

# CI/CD and automation
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_automation -C "automation-2026"

# Git hosting (GitHub, GitLab)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_git -C "git-2026"

2. Always use a passphrase:

A passphrase encrypts your private key at rest. If your machine is compromised, the attacker cannot use your SSH keys without the passphrase. Combined with macOS Keychain integration, you only need to type the passphrase once.

# When prompted during key generation:
Enter passphrase (empty for no passphrase): [enter a strong passphrase]
Enter same passphrase again: [confirm]

3. Rotate keys annually:

Include the year in your key comments so you know when they were generated:

ssh-keygen -t ed25519 -C "you@company-2026-Q1" -f ~/.ssh/id_ed25519_work_2026

When the year rolls over, generate new keys, deploy them, and retire the old ones.

4. Set correct file permissions:

# Private keys: owner read only
chmod 400 ~/.ssh/id_ed25519*
chmod 600 ~/.ssh/id_ed25519*.pub

# Actually, public keys can be world-readable
chmod 644 ~/.ssh/id_ed25519*.pub

# SSH directory itself
chmod 700 ~/.ssh

# Config file
chmod 600 ~/.ssh/config

Server-Side Hardening

If you manage SSH servers, take this opportunity to harden them:

# /etc/ssh/sshd_config recommended settings

# Disable password authentication (use keys only)
PasswordAuthentication no

# Disable root login
PermitRootLogin no

# Only allow specific users
AllowUsers deploy admin

# Use modern key exchange
KexAlgorithms mlkem768x25519-sha256,[email protected],curve25519-sha256

# Use modern host key algorithms
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256

# Use modern ciphers
Ciphers [email protected],[email protected],[email protected]

# Use modern MACs
MACs [email protected],[email protected]

# Rate limiting
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30

After changing sshd_config, always test before disconnecting:

# Validate the config
sudo sshd -t

# If no errors, restart sshd
sudo systemctl restart sshd

# IMPORTANT: Keep your current session open and test with a NEW connection
# before closing the existing one

Monitoring and Alerting

Set up monitoring for SSH authentication events:

# Monitor auth.log for failed attempts (Linux servers)
tail -f /var/log/auth.log | grep "sshd"

# Monitor macOS system log for SSH events
log stream --predicate 'process == "sshd"' --level info

Troubleshooting Common Errors

Error: "Connection closed by X.X.X.X port 22"

Cause: Username case mismatch.

Fix:

# Determine the correct username
ssh -vvv user@host 2>&1 | grep "debug1: Sending"

# Try the opposite case
ssh Administrator@host  # if 'administrator' failed
ssh administrator@host  # if 'Administrator' failed

Error: "Bad key types '+ssh-dss'"

Cause: SSH config references the removed DSA algorithm.

Fix:

# Find and edit the offending line
grep -n "ssh-dss" ~/.ssh/config

# Remove or replace the algorithm reference
# Before: HostKeyAlgorithms +ssh-dss
# After:  (remove the entire line, or replace with modern algorithms)

Error: "Bad key types '+ssh-rsa,ssh-dss'"

Cause: SSH config has a compound algorithm list including DSA.

Fix:

# Before
HostKeyAlgorithms +ssh-rsa,ssh-dss
PubkeyAcceptedAlgorithms +ssh-rsa,ssh-dss

# After (remove ssh-dss, keep ssh-rsa if needed for legacy RSA)
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa

Note: ssh-rsa (with SHA-1 signatures) is also deprecated. Use rsa-sha2-256 or rsa-sha2-512 if you must use RSA:

HostKeyAlgorithms rsa-sha2-512,rsa-sha2-256
PubkeyAcceptedAlgorithms rsa-sha2-512,rsa-sha2-256

Error: "No matching host key type found"

Cause: The remote server only offers DSA host keys.

Fix: This requires action on the server side. Generate new host keys on the server:

# On the remote server (if you have console access)
sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
sudo ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
sudo systemctl restart sshd

If you cannot update the server, use the jump host or Docker workarounds described above.

Error: "WARNING: connection is not using a post-quantum key exchange algorithm"

Cause: Remote server does not support post-quantum key exchange.

Fix:

# Suppress for a specific host
# Add to ~/.ssh/config:
Host old-server
    WarnWeakCrypto no

# Or suppress globally (not recommended)
Host *
    WarnWeakCrypto no

Error: "Permission denied (publickey)"

Cause: After key migration, the new key is not authorized on the server.

Fix:

# Check which key is being offered
ssh -v user@host 2>&1 | grep "Offering public key"

# Deploy the correct key
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host

Error: "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED"

Cause: After removing DSA host key entries from known_hosts, the server is offering a different host key type.

Fix:

# Remove the old host key entry
ssh-keygen -R hostname

# Reconnect and accept the new host key
ssh user@hostname
# You'll be prompted to verify the new host key fingerprint

Understanding the Broader Context

The SSH changes in macOS Tahoe 26.3 are part of a larger trend across the industry. Understanding this context helps you prepare for future changes rather than reacting to each one.

OpenSSH Release Timeline

VersionDateKey Changes
7.02015DSA disabled by default
8.22020SHA-1 signatures deprecated for RSA
8.82022SHA-1 RSA signatures disabled by default
9.02022Post-quantum KEx (sntrup761) enabled by default
9.92024ML-KEM post-quantum support added
10.02025DSA removed entirely, ML-KEM default

macOS has historically lagged behind these releases by 6-12 months, which is why the DSA removal is hitting macOS users in early 2026 despite being in OpenSSH 10.0 since April 2025.

What to Expect Next

Based on the OpenSSH development roadmap:

  • SHA-1 RSA signatures (ssh-rsa) will likely be fully removed in OpenSSH 11.x
  • ECDSA with NIST curves may be deprecated in favor of Ed25519
  • Post-quantum signatures (not just key exchange) will be added
  • Certificate-based authentication will be increasingly promoted over raw public keys

Prepare now by:

  1. Ensuring all your keys are Ed25519
  2. Moving to rsa-sha2-256/512 if you must use RSA
  3. Testing post-quantum key exchange with your servers
  4. Evaluating SSH certificate infrastructure for your organization

For more details on what changed in the broader macOS Tahoe 26.3 update, see our macOS Tahoe 26.3 release guide.


Quick Reference: Commands You Need

Here is a consolidated reference of every command mentioned in this guide, organized by task:

Diagnosis

# Check SSH version
ssh -V

# Verbose connection test
ssh -vvv user@host

# Check which SSH binary is running
which ssh

# Test without config file
ssh -F /dev/null user@host

# Check current key types
for key in ~/.ssh/*.pub; do
    echo "=== $key ==="
    ssh-keygen -l -f "$key"
done

Key Generation

# Generate Ed25519 key (recommended)
ssh-keygen -t ed25519 -C "description-YYYY" -f ~/.ssh/id_ed25519

# Generate RSA key (if Ed25519 is not supported by target)
ssh-keygen -t rsa -b 4096 -C "description-YYYY" -f ~/.ssh/id_rsa

Key Deployment

# Deploy key to remote server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host

# Add key to macOS Keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

Config Auditing

# Find DSA references in config
grep -n "ssh-dss" ~/.ssh/config /etc/ssh/ssh_config

# Find all User directives
grep -n "User " ~/.ssh/config

# Validate config syntax
ssh -G test-host

# Fix permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config
chmod 400 ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub

Cleanup

# Remove old host key
ssh-keygen -R hostname

# Remove all DSA entries from known_hosts
cp ~/.ssh/known_hosts ~/.ssh/known_hosts.bak
grep -v "ssh-dss" ~/.ssh/known_hosts > ~/.ssh/known_hosts.tmp
mv ~/.ssh/known_hosts.tmp ~/.ssh/known_hosts

Frequently Asked Questions

Can I downgrade OpenSSH on macOS Tahoe 26.3 to restore DSA support?

You cannot replace the system OpenSSH binary on macOS due to System Integrity Protection (SIP). However, you can install an older version via Homebrew or compile from source to a custom location. The system ssh at /usr/bin/ssh will always be the OpenSSH 10.0 version that shipped with Tahoe 26.3. See the "Workarounds for Legacy Infrastructure" section for detailed instructions on building OpenSSH 9.7p1 from source.

Is the username case sensitivity change permanent or a bug?

Based on the OpenSSH 10.0 release notes and the direction of the project, this appears to be intentional behavior aligning with the SSH protocol specification (RFC 4252), which defines usernames as case-sensitive. Apple has not commented on whether this is a bug, and there is no indication it will be reverted. You should treat this as the permanent behavior and update your configurations accordingly.

Will my RSA keys stop working too?

RSA keys with SHA-2 signatures (rsa-sha2-256 and rsa-sha2-512) continue to work and will be supported for the foreseeable future. However, RSA keys with SHA-1 signatures (ssh-rsa) have been disabled by default since OpenSSH 8.8 and will likely be fully removed in a future version. If you use RSA keys, verify they are at least 3072 bits and that your servers accept SHA-2 signatures. For new keys, Ed25519 is the preferred choice.

How do I fix SSH connections from macOS to Windows Server?

Windows Server's OpenSSH implementation has historically been case-insensitive for usernames. After the macOS Tahoe 26.3 update, you need to use the exact case that Windows recognizes. For domain accounts, use DOMAIN\Username or [email protected]. For local accounts, check the account name in Computer Management. Update your ~/.ssh/config accordingly:

Host windows-server
    HostName win.example.com
    User DOMAIN\\AdminUser
    IdentityFile ~/.ssh/id_ed25519

Does this affect SFTP and SCP too?

Yes. Both sftp and scp use the same SSH client library and are subject to identical case sensitivity enforcement and DSA removal. Any SFTP clients (like Cyberduck, Transmit, or FileZilla) that use the system SSH binary will also be affected. GUI SFTP clients that bundle their own SSH library (such as Cyberduck with its own libssh2 build) may not be affected, but this depends on the specific application.


Conclusion

macOS Tahoe 26.3 shipped SSH changes that broke established workflows without adequate warning. The case-sensitive username enforcement, complete DSA removal, and post-quantum key exchange warnings each require attention, but they are all straightforward to resolve once you understand what changed.

The immediate action items are:

  1. Audit your ~/.ssh/config for case mismatches in User directives and remove any ssh-dss references
  2. Generate Ed25519 keys to replace any remaining DSA keys
  3. Deploy the new keys to all servers in your inventory
  4. Update automation scripts and CI/CD pipelines with correct username case and key paths
  5. Silence or resolve post-quantum warnings based on your security posture

These changes, while disruptive, push the macOS SSH ecosystem toward stronger security defaults. Ed25519 is faster, more secure, and produces smaller keys than DSA. Case-sensitive usernames align with the SSH protocol specification. Post-quantum key exchange protects against future threats. The pain of migration is real, but the security improvements are substantive.

If you are setting up a new development environment on macOS Tahoe, start with our macOS Tahoe developer setup guide to get everything configured correctly from the beginning. For broader security configuration, our macOS Tahoe security privacy guide covers the full security stack beyond SSH.