Public course previewThis is not your assigned passport. No identity, answers, or completion progress are stored here.How to start your passport
Guided lesson
01 / 06About 45 minuteschanges sign-in security
Establish Safe Euler SSH Access
Euler is ETH's service made of many managed computers for research calculations. Keep working key-only access or create a dedicated key without overwriting one. Test the direct connection and the euler SSH alias separately.
Where you will workOn the laptop or desktop in front of you
Euler is ETH's high-performance computing cluster. SSH, or Secure Shell,
connects your computer to its remote command shell.
File or proof
Where it belongs
What it establishes
Private key
Private on your own computer
Proof used for login; never uploaded
Public .pub file
Installed for your Euler account
Permits its matching private key
Key passphrase
Entered locally when prompted
Unlocks the private key, not the ETH account
Host fingerprint
Compared with the published host key
Identity of the remote SSH server
SSH alias euler
Local connection settings
Which host, username and private key SSH selects
An SSH alias is a short local name for these settings. IdentityFile is the setting
selecting the private-key path. An SSH key pair has both files. A successful ETH password test does not prove
key-only access. The tests disable password fallback so success means a key worked.
The alias helper has four operations: read the tested identity; back up existing
configuration; write an isolated Include file; validate the resolved settings.
Validation failure stops safely with recovery information. Do not improvise edits
or remove backups after a failure.
Inspect before creating. If key-only access already works, keep it. If a pair
exists but fails, diagnose it instead of replacing it. Compare direct access
with what the alias actually selects.
Distinguish the two password prompts
The first test deliberately uses your ETH password. Later key-only tests disable
that fallback; a prompt for the local key passphrase is allowed. Only the public
.pub file is installed remotely. A working key stays on its original computer.
Keep existing keys, install only the public file and inspect the resolved
alias settings before relying on it.
Work from your computer until a step names Euler. Keep a working key, diagnose a failed pair and create only after the absent-key marker.
Optional: using the terminal
A command is a line of text that tells a computer to do one task. You paste it into a terminal, the text application used for commands. A shell is the part inside that application that reads the command. PowerShell, zsh, and Bash are shells. Open only the application named for your computer below; it starts its configured shell. Follow the shell named below. You do not need to install a separate Bash or zsh application.
Your computer or local means the laptop or desktop in front of you. Remote means another computer reached through the network. A command affects whichever computer the step names, so check that label before pasting.
Windows: press the Windows key, type PowerShell, and open the result named PowerShell. Do not use Command Prompt, Git Bash, or WSL for a block labelled PowerShell.
macOS: press Command+Space, type Terminal, and press Return. Terminal normally starts the zsh shell. If it uses another shell, run zsh for this session.
Linux: open the app named Terminal from the applications menu. It normally starts the Bash shell. If it uses another shell, run bash for this session.
The prompt is the text beside the blinking cursor. For each card, check the named computer, choose Copy, paste only the command after the prompt, press Enter once, and wait for the prompt to return.
If you have never done this before, first run the harmless terminal-ready practice on the start page. It lets you practise copying, pasting, pressing Enter, and reading output before a setup command.
Confirm that this computer has working internet access. Euler SSH normally connects directly to euler.ethz.ch. ETH VPN, or Virtual Private Network, is the official secure connection for reaching ETH-only services from outside the campus network. If the address times out on a restricted network, try the campus network or ETH VPN before changing any SSH file.
Look forThis computer has a working network path for the direct Euler login test.
Explanation and help
What this means
Continue to the password-login test.
Safe recovery
Try the campus network or ETH VPN; do not change SSH keys to solve a timeout.
02
Test password login
On your computer
Enter the short ETH username, not an email address. A host fingerprint is a short identifier for the remote computer's SSH key. On a first connection, compare the displayed fingerprint with ETH's published host keys before accepting it. This test ignores your user SSH config and disables public-key authentication so it really tests the ETH password.
(
printf 'Short ETH username: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
ssh -F none -o PubkeyAuthentication=no -o PasswordAuthentication=yes -o KbdInteractiveAuthentication=yes -o PreferredAuthentications=keyboard-interactive,password "$eth_user@euler.ethz.ch" 'echo password-login-ok'
)
RunTerminal on Linux (Bash)
(
printf 'Short ETH username: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
ssh -F none -o PubkeyAuthentication=no -o PasswordAuthentication=yes -o KbdInteractiveAuthentication=yes -o PreferredAuthentications=keyboard-interactive,password "$eth_user@euler.ethz.ch" 'echo password-login-ok'
)
Look forAfter the ETH password, Euler prints password-login-ok.
If this differsStop and solve account, password, VPN, or host-key access first. Do not generate a key yet.
Explanation and help
What this means
Continue to read-only inspection.
03
Inspect existing SSH files without changing them
On your computer
OpenSSH is the ssh command-line program on your computer. Its .ssh folder contains keys and an optional config file. List that folder, print the numbered config if it exists, and ask OpenSSH to parse the current euler alias. This step is read-only. Do not edit or delete anything.
RunPowerShell on Windows
& {
$SshDir = Join-Path $env:USERPROFILE ".ssh"
Get-ChildItem -LiteralPath $SshDir -Force -ErrorAction SilentlyContinue
$Config = Join-Path $SshDir "config"
if (Test-Path -LiteralPath $Config) {
$i = 0
Get-Content -LiteralPath $Config | ForEach-Object { $i++; "{0,3}: {1}" -f $i, $_ }
ssh -G euler | Out-Null
if ($LASTEXITCODE -ne 0) { throw "STOP: OpenSSH cannot parse the current config; no file was changed" }
Write-Host "existing-config-parses"
} else {
Write-Host "No user SSH config yet."
}
}
RunTerminal on macOS (zsh)
(
ssh_dir="$HOME/.ssh"
ls -la "$ssh_dir" 2>/dev/null || printf 'No .ssh directory yet.\n'
config="$ssh_dir/config"
if [ -f "$config" ]; then
nl -ba "$config"
ssh -G euler >/dev/null || {
printf 'STOP: OpenSSH cannot parse the current config; no file was changed\n' >&2
exit 1
}
printf 'existing-config-parses\n'
else
printf 'No user SSH config yet.\n'
fi
)
RunTerminal on Linux (Bash)
(
ssh_dir="$HOME/.ssh"
ls -la "$ssh_dir" 2>/dev/null || printf 'No .ssh directory yet.\n'
config="$ssh_dir/config"
if [ -f "$config" ]; then
nl -ba "$config"
ssh -G euler >/dev/null || {
printf 'STOP: OpenSSH cannot parse the current config; no file was changed\n' >&2
exit 1
}
printf 'existing-config-parses\n'
else
printf 'No user SSH config yet.\n'
fi
)
Look forYou know which SSH files exist. An existing config also prints existing-config-parses.
If this differsStop if OpenSSH names a bad line. Use the SSH troubleshooting page to correct only that line; do not append another Host block or replace the file.
Explanation and help
What this means
Test existing key-only access.
04
Keep working key-only access
On your computer
First test an existing euler alias when it resolves to this Euler account. ProxyCommand and ProxyJump are settings that can reroute SSH through another connection; this test disables them and connects directly to euler.ethz.ch on the standard SSH port 22. If no suitable alias is available, test id_ed25519_euler, the standard key filename used by this guide, while ignoring user config. Keyboard-interactive login is another password-like login method. Every connection in this step disables both ETH-password and keyboard-interactive fallback, so success proves that a key worked.
RunPowerShell on Windows
& {
$EulerUser = Read-Host "Short ETH username"
if ($EulerUser -notmatch "^[A-Za-z0-9._-]+$") { throw "STOP: invalid ETH username" }
$Resolved = @(ssh -G euler 2>$null)
if ($LASTEXITCODE -ne 0) { throw "STOP: existing SSH config is invalid" }
$ResolvedHostMatch = $Resolved | Select-String '^hostname\s+' | Select-Object -First 1
$ResolvedUserMatch = $Resolved | Select-String '^user\s+' | Select-Object -First 1
$ResolvedHost = if ($ResolvedHostMatch) { ($ResolvedHostMatch.Line -split '\s+', 2)[1] } else { "" }
$ResolvedUser = if ($ResolvedUserMatch) { ($ResolvedUserMatch.Line -split '\s+', 2)[1] } else { "" }
$PublicIdentity = @($Resolved | Select-String '^identityfile\s+.*\.pub\s*$')
if ($ResolvedHost -eq "euler.ethz.ch" -and $ResolvedUser -eq $EulerUser) {
if ($PublicIdentity.Count -gt 0) { throw "STOP: the existing euler alias points IdentityFile at a .pub file" }
ssh -o Port=22 -o ProxyCommand=none -o ProxyJump=none -o ForwardAgent=no -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler "echo existing-alias-ok"
if ($LASTEXITCODE -eq 0) { return }
}
$KeyPath = Join-Path $env:USERPROFILE ".ssh\id_ed25519_euler"
$PrivateExists = Test-Path -LiteralPath $KeyPath
$PublicExists = Test-Path -LiteralPath "$KeyPath.pub"
if ($PrivateExists -xor $PublicExists) { throw "STOP: the standard key pair is incomplete; do not overwrite either file" }
if (-not $PrivateExists) { Write-Host "no-standard-key"; return }
ssh -F none -i $KeyPath -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no "$EulerUser@euler.ethz.ch" "echo standard-key-ok"
}
RunTerminal on macOS (zsh)
(
printf 'Short ETH username: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
resolved="$(ssh -G euler 2>/dev/null)" || {
printf 'STOP: existing SSH config is invalid\n' >&2
exit 1
}
resolved_host="$(printf '%s\n' "$resolved" | awk '$1 == "hostname" { print $2; exit }')"
resolved_user="$(printf '%s\n' "$resolved" | awk '$1 == "user" { print $2; exit }')"
if [ "$resolved_host" = "euler.ethz.ch" ] && [ "$resolved_user" = "$eth_user" ]; then
if printf '%s\n' "$resolved" | awk '$1 == "identityfile" && $2 ~ /[.]pub$/ { found=1 } END { exit !found }'; then
printf 'STOP: the existing euler alias points IdentityFile at a .pub file\n' >&2
exit 1
fi
if ssh -o Port=22 -o ProxyCommand=none -o ProxyJump=none -o ForwardAgent=no -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler 'echo existing-alias-ok'; then
exit 0
fi
fi
key="$HOME/.ssh/id_ed25519_euler"
private_exists=0; public_exists=0
[ -f "$key" ] && private_exists=1
[ -f "$key.pub" ] && public_exists=1
if [ "$private_exists" -ne "$public_exists" ]; then
printf 'STOP: the standard key pair is incomplete; do not overwrite either file\n' >&2
exit 1
fi
if [ "$private_exists" -eq 0 ]; then
printf 'no-standard-key\n'
exit 0
fi
ssh -F none -i "$key" -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no "$eth_user@euler.ethz.ch" 'echo standard-key-ok'
)
RunTerminal on Linux (Bash)
(
printf 'Short ETH username: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
resolved="$(ssh -G euler 2>/dev/null)" || {
printf 'STOP: existing SSH config is invalid\n' >&2
exit 1
}
resolved_host="$(printf '%s\n' "$resolved" | awk '$1 == "hostname" { print $2; exit }')"
resolved_user="$(printf '%s\n' "$resolved" | awk '$1 == "user" { print $2; exit }')"
if [ "$resolved_host" = "euler.ethz.ch" ] && [ "$resolved_user" = "$eth_user" ]; then
if printf '%s\n' "$resolved" | awk '$1 == "identityfile" && $2 ~ /[.]pub$/ { found=1 } END { exit !found }'; then
printf 'STOP: the existing euler alias points IdentityFile at a .pub file\n' >&2
exit 1
fi
if ssh -o Port=22 -o ProxyCommand=none -o ProxyJump=none -o ForwardAgent=no -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler 'echo existing-alias-ok'; then
exit 0
fi
fi
key="$HOME/.ssh/id_ed25519_euler"
private_exists=0; public_exists=0
[ -f "$key" ] && private_exists=1
[ -f "$key.pub" ] && public_exists=1
if [ "$private_exists" -ne "$public_exists" ]; then
printf 'STOP: the standard key pair is incomplete; do not overwrite either file\n' >&2
exit 1
fi
if [ "$private_exists" -eq 0 ]; then
printf 'no-standard-key\n'
exit 0
fi
ssh -F none -i "$key" -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no "$eth_user@euler.ethz.ch" 'echo standard-key-ok'
)
Look forThe final marker is existing-alias-ok, standard-key-ok, or no-standard-key.
If this differsIf the standard id_ed25519_euler pair exists but neither key-only test succeeds, use the next diagnostic step. If OpenSSH reports a .pub IdentityFile, correct that config line first.
Explanation and help
What this means
After existing-alias-ok, skip key generation and installation and continue at the alias step, which preserves the working identity. After standard-key-ok, also skip to the alias step. After no-standard-key, continue to key generation.
05
Diagnose an existing key pair without replacing it
On your computer
Run this only when id_ed25519_euler exists but its key-only test did not print standard-key-ok. Compare the private- and public-key fingerprints. This reads the key pair but does not change it. Open the diagnostic only for this failed existing-pair case. Stop if the pair differs; do not regenerate or overwrite either file.
Show the existing-key diagnostic (PowerShell on Windows)
RunPowerShell on Windows
& {
$KeyPath = Join-Path $env:USERPROFILE ".ssh\id_ed25519_euler"
$PrivateFingerprint = ssh-keygen -lf $KeyPath
if ($LASTEXITCODE -ne 0) { throw "STOP: private key could not be inspected" }
$PublicFingerprint = ssh-keygen -lf "$KeyPath.pub"
if ($LASTEXITCODE -ne 0) { throw "STOP: public key could not be inspected" }
$PrivateFingerprint
$PublicFingerprint
if (($PrivateFingerprint -split '\s+')[1] -ne ($PublicFingerprint -split '\s+')[1]) { throw "STOP: private and public keys do not match" }
Write-Host "key-pair-matches"
}
Show the existing-key diagnostic (Terminal on macOS (zsh))
RunTerminal on macOS (zsh)
(
key="$HOME/.ssh/id_ed25519_euler"
private_fingerprint="$(ssh-keygen -lf "$key")" || { printf 'STOP: private key could not be inspected\n' >&2; exit 1; }
public_fingerprint="$(ssh-keygen -lf "$key.pub")" || { printf 'STOP: public key could not be inspected\n' >&2; exit 1; }
printf '%s\n%s\n' "$private_fingerprint" "$public_fingerprint"
[ "$(printf '%s\n' "$private_fingerprint" | awk '{print $2}')" = "$(printf '%s\n' "$public_fingerprint" | awk '{print $2}')" ] || { printf 'STOP: private and public keys do not match\n' >&2; exit 1; }
printf 'key-pair-matches\n'
)
Show the existing-key diagnostic (Terminal on Linux (Bash))
RunTerminal on Linux (Bash)
(
key="$HOME/.ssh/id_ed25519_euler"
private_fingerprint="$(ssh-keygen -lf "$key")" || { printf 'STOP: private key could not be inspected\n' >&2; exit 1; }
public_fingerprint="$(ssh-keygen -lf "$key.pub")" || { printf 'STOP: public key could not be inspected\n' >&2; exit 1; }
printf '%s\n%s\n' "$private_fingerprint" "$public_fingerprint"
[ "$(printf '%s\n' "$private_fingerprint" | awk '{print $2}')" = "$(printf '%s\n' "$public_fingerprint" | awk '{print $2}')" ] || { printf 'STOP: private and public keys do not match\n' >&2; exit 1; }
printf 'key-pair-matches\n'
)
Look forBoth fingerprint lines contain the same SHA256 value and the final line is key-pair-matches.
If this differsStop and use the SSH help path. Never overwrite either key file or point IdentityFile at a .pub file.
Explanation and help
What this means
Keep this pair, skip key generation, and continue to Install only the public key.
06
Create the standard dedicated key only when absent
On your computer
Run this only after no-standard-key. The guard stops if either half of the pair exists. Choose a passphrase when ssh-keygen asks. If either file already exists, use the diagnostic and request help; creating a new key is not a repair for an unexplained existing-pair failure.
Show key creation only after no-standard-key (PowerShell on Windows)
RunPowerShell on Windows
& {
$SshDir = Join-Path $env:USERPROFILE ".ssh"
$KeyPath = Join-Path $SshDir "id_ed25519_euler"
New-Item -ItemType Directory -Force $SshDir | Out-Null
if ((Test-Path -LiteralPath $KeyPath) -or (Test-Path -LiteralPath "$KeyPath.pub")) { throw "STOP: standard key or public key already exists; nothing was overwritten" }
ssh-keygen -t ed25519 -a 100 -f $KeyPath -C "$env:USERNAME@euler"
if ($LASTEXITCODE -ne 0) { throw "STOP: ssh-keygen failed" }
icacls $KeyPath /inheritance:r
icacls $KeyPath /grant:r "${env:USERNAME}:(R)"
}
Show key creation only after no-standard-key (Terminal on macOS (zsh))
RunTerminal on macOS (zsh)
(
key="$HOME/.ssh/id_ed25519_euler"
mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh"
if [ -e "$key" ] || [ -e "$key.pub" ]; then printf 'STOP: standard key or public key already exists; nothing was overwritten\n' >&2; exit 1; fi
ssh-keygen -t ed25519 -a 100 -f "$key" -C "$USER@euler"
)
Show key creation only after no-standard-key (Terminal on Linux (Bash))
RunTerminal on Linux (Bash)
(
key="$HOME/.ssh/id_ed25519_euler"
mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh"
if [ -e "$key" ] || [ -e "$key.pub" ]; then printf 'STOP: standard key or public key already exists; nothing was overwritten\n' >&2; exit 1; fi
ssh-keygen -t ed25519 -a 100 -f "$key" -C "$USER@euler"
)
Look forBoth id_ed25519_euler and id_ed25519_euler.pub are created; the private key has restricted permissions.
If this differsStop at the first error and keep all pre-existing files unchanged.
Explanation and help
What this means
Install only the .pub file.
07
Install only the public key
On your computer
Run this on your own computer, not inside an Euler session. Send only id_ed25519_euler.pub through the working password login. Spaces in your Windows profile or key comment are supported; do not rename your profile or move your keys. The command preserves existing authorized_keys entries and avoids adding a duplicate exact line.
& {
$ErrorActionPreference = "Stop"
$EulerUser = Read-Host "Short ETH username used in the password test"
if ($EulerUser -notmatch "^[A-Za-z0-9._-]+$") { throw "STOP: invalid ETH username" }
$PublicKey = Join-Path $env:USERPROFILE ".ssh\id_ed25519_euler.pub"
if (-not (Test-Path -LiteralPath $PublicKey -PathType Leaf)) { throw "STOP: public key is missing" }
$KeyText = (Get-Content -LiteralPath $PublicKey -Raw -Encoding UTF8).Trim()
if ($KeyText -notmatch '^ssh-ed25519 [A-Za-z0-9+/]+={0,2}(?: [^\r\n]*)?$') { throw "STOP: expected one Ed25519 public-key line; keep the files and request help" }
$KeyData = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($KeyText))
$Script = @'
set -eu
key=$(printf %s __PUBLIC_KEY_DATA__ | base64 -d)
umask 077
mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
if grep -Fqx -- "$key" ~/.ssh/authorized_keys; then
:
else
status=$?
[ "$status" -eq 1 ] || exit "$status"
printf "\n%s\n" "$key" >> ~/.ssh/authorized_keys
fi
grep -Fqx -- "$key" ~/.ssh/authorized_keys
printf "public-key-installed\n"
'@
$Script = $Script.Replace('__PUBLIC_KEY_DATA__', $KeyData).Replace("`r", "")
$ScriptData = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Script + "`n"))
$ScriptData | ssh -F none -o PubkeyAuthentication=no -o PasswordAuthentication=yes -o KbdInteractiveAuthentication=yes -o PreferredAuthentications=keyboard-interactive,password "$EulerUser@euler.ethz.ch" "bash -o pipefail -c 'base64 -d | sh'"
if ($LASTEXITCODE -ne 0) { throw "STOP: public-key installation failed; keep both key files and request help with the error" }
}
RunTerminal on macOS (zsh)
(
printf 'Short ETH username used in the password test: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
public_key="$HOME/.ssh/id_ed25519_euler.pub"
test -f "$public_key" || { printf 'STOP: public key is missing\n' >&2; exit 1; }
cat "$public_key" | ssh -F none -o PubkeyAuthentication=no -o PasswordAuthentication=yes -o KbdInteractiveAuthentication=yes -o PreferredAuthentications=keyboard-interactive,password "$eth_user@euler.ethz.ch" 'set -eu
key="$(tr -d "\r")"
[ -n "$key" ] || { printf "STOP: empty public key\n" >&2; exit 1; }
umask 077
mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
if grep -Fqx -- "$key" ~/.ssh/authorized_keys; then
:
else
status=$?
[ "$status" -eq 1 ] || exit "$status"
printf "\n%s\n" "$key" >> ~/.ssh/authorized_keys
fi
grep -Fqx -- "$key" ~/.ssh/authorized_keys
printf "public-key-installed\n"'
)
RunTerminal on Linux (Bash)
(
printf 'Short ETH username used in the password test: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
public_key="$HOME/.ssh/id_ed25519_euler.pub"
test -f "$public_key" || { printf 'STOP: public key is missing\n' >&2; exit 1; }
cat "$public_key" | ssh -F none -o PubkeyAuthentication=no -o PasswordAuthentication=yes -o KbdInteractiveAuthentication=yes -o PreferredAuthentications=keyboard-interactive,password "$eth_user@euler.ethz.ch" 'set -eu
key="$(tr -d "\r")"
[ -n "$key" ] || { printf "STOP: empty public key\n" >&2; exit 1; }
umask 077
mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
if grep -Fqx -- "$key" ~/.ssh/authorized_keys; then
:
else
status=$?
[ "$status" -eq 1 ] || exit "$status"
printf "\n%s\n" "$key" >> ~/.ssh/authorized_keys
fi
grep -Fqx -- "$key" ~/.ssh/authorized_keys
printf "public-key-installed\n"'
)
Look forAfter the ETH password, Euler prints public-key-installed with no error. This confirms the exact public-key line is present; the next key-only test must still pass.
If this differsStop at any error, even if an older command printed public-key-installed. Keep both key files. For grep errors or public-key-installedn from the older Windows command, follow the linked recovery guide; do not delete authorized_keys or regenerate keys.
Explanation and help
What this means
Run the direct key-only test.
08
Prove direct key-only access
On your computer
Run the direct test with password and keyboard-interactive authentication disabled. Enter only the local key passphrase if prompted.
(
printf 'Short ETH username: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
ssh -F none -i "$HOME/.ssh/id_ed25519_euler" -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no "$eth_user@euler.ethz.ch" 'echo key-ok'
)
RunTerminal on Linux (Bash)
(
printf 'Short ETH username: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
ssh -F none -i "$HOME/.ssh/id_ed25519_euler" -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no "$eth_user@euler.ethz.ch" 'echo key-ok'
)
Look forEuler prints exactly key-ok without asking for the ETH password.
If this differsStop. Do not continue to the alias or euler-tunnel until this exact test passes.
Explanation and help
What this means
Create the isolated euler alias.
09
Create or update an isolated euler alias
On your computer
The SSH config stores named connection settings. This helper reads the tested identity, backs up existing files, writes one isolated passport.d/euler.conf and Include entry, then validates resolved settings. It preserves a tested existing key. Backups in passport-backups are not loaded by OpenSSH. It does not change Euler jobs or GitHub authentication. Read the four operations above before running it.
(
set -eu
printf 'Short ETH username: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
ssh_dir="$HOME/.ssh"; config="$ssh_dir/config"; include_dir="$ssh_dir/passport.d"; backup_dir="$ssh_dir/passport-backups"; euler_config="$include_dir/euler.conf"; key="$ssh_dir/id_ed25519_euler"
[ ! -L "$config" ] || { printf "STOP: %s is a symbolic link; no file was changed\n" "$config" >&2; exit 1; }
[ ! -L "$euler_config" ] || { printf "STOP: %s is a symbolic link; no file was changed\n" "$euler_config" >&2; exit 1; }
config_existed=0; euler_config_existed=0
if [ -e "$config" ]; then [ -f "$config" ] || { printf "STOP: %s is not a regular file\n" "$config" >&2; exit 1; }; config_existed=1; fi
if [ -e "$euler_config" ]; then [ -f "$euler_config" ] || { printf "STOP: %s is not a regular file\n" "$euler_config" >&2; exit 1; }; euler_config_existed=1; fi
resolved_before="$(ssh -G euler 2>/dev/null)" || {
printf 'STOP: existing SSH config is invalid; no file was changed\n' >&2
exit 1
}
resolved_host="$(printf '%s\n' "$resolved_before" | awk '$1 == "hostname" { print $2; exit }')"
resolved_user="$(printf '%s\n' "$resolved_before" | awk '$1 == "user" { print $2; exit }')"
if printf '%s\n' "$resolved_before" | awk '$1 == "identityfile" && $2 ~ /[.]pub$/ { found=1 } END { exit !found }'; then
printf 'STOP: existing IdentityFile points at a .pub file\n' >&2
exit 1
fi
use_existing=0
if [ "$resolved_host" = "euler.ethz.ch" ] && [ "$resolved_user" = "$eth_user" ]; then
if ssh -o Port=22 -o ProxyCommand=none -o ProxyJump=none -o ForwardAgent=no -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler 'exit 0'; then
use_existing=1
fi
fi
private_exists=0; public_exists=0
[ -f "$key" ] && private_exists=1
[ -f "$key.pub" ] && public_exists=1
if [ "$use_existing" -eq 0 ] && [ "$private_exists" -ne "$public_exists" ]; then printf 'STOP: standard key pair is incomplete\n' >&2; exit 1; fi
standard_key_works=0
if [ "$use_existing" -eq 0 ] && [ "$private_exists" -eq 1 ]; then
if ssh -F none -i "$key" -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no "$eth_user@euler.ethz.ch" 'exit 0'; then
standard_key_works=1
fi
fi
if [ "$use_existing" -eq 0 ] && [ "$standard_key_works" -eq 0 ]; then
printf 'STOP: no tested key-only identity is available\n' >&2
exit 1
fi
mkdir -p "$include_dir" "$backup_dir" && chmod 700 "$ssh_dir" "$include_dir" "$backup_dir"
stamp="$(date +%Y%m%d-%H%M%S)-$$"
config_backup="$backup_dir/config.$stamp"
euler_config_backup="$backup_dir/euler.conf.$stamp"
[ "$config_existed" -eq 0 ] || cp -p "$config" "$config_backup"
[ "$euler_config_existed" -eq 0 ] || cp -p "$euler_config" "$euler_config_backup"
restore_originals() {
restore_status=0
if [ "$config_existed" -eq 1 ]; then cp -p "$config_backup" "$config" || restore_status=1; else rm -f "$config" || restore_status=1; fi
if [ "$euler_config_existed" -eq 1 ]; then cp -p "$euler_config_backup" "$euler_config" || restore_status=1; else rm -f "$euler_config" || restore_status=1; fi
return "$restore_status"
}
euler_temporary="$euler_config.passport-new"
{
printf 'Host euler\n HostName euler.ethz.ch\n User %s\n' "$eth_user"
if [ "$use_existing" -eq 1 ]; then
identity_count=0
while IFS= read -r identity; do
[ -n "$identity" ] || continue
case "$identity" in *'"'*) printf 'STOP: unsupported quote in resolved identity path\n' >&2; exit 1;; esac
printf ' IdentityFile "%s"\n' "$identity"
identity_count=$((identity_count + 1))
done <<EOF
$(printf '%s\n' "$resolved_before" | awk '$1 == "identityfile" { $1=""; sub(/^[[:space:]]+/, ""); print }')
EOF
[ "$identity_count" -gt 0 ] || { printf 'STOP: working alias has no inspectable identity selection\n' >&2; exit 1; }
identities_only="$(printf '%s\n' "$resolved_before" | awk '$1 == "identitiesonly" { print $2; exit }')"
case "$identities_only" in yes|no) ;; *) printf 'STOP: invalid resolved IdentitiesOnly value\n' >&2; exit 1;; esac
printf ' IdentitiesOnly %s\n' "$identities_only"
else
printf ' IdentityFile ~/.ssh/id_ed25519_euler\n IdentitiesOnly yes\n'
fi
printf ' Port 22\n ProxyCommand none\n ProxyJump none\n PreferredAuthentications publickey\n PasswordAuthentication no\n KbdInteractiveAuthentication no\n ForwardAgent no\n'
} > "$euler_temporary"
chmod 600 "$euler_temporary"
ssh -F "$euler_temporary" -G euler >/dev/null || {
rm -f "$euler_temporary"
printf 'STOP: generated Euler block is invalid\n' >&2
exit 1
}
include='Include ~/.ssh/passport.d/*.conf'; temporary="$config.passport-new"
if [ -f "$config" ]; then
awk -v include="$include" 'BEGIN { print include } /^[[:space:]]*Include[[:space:]]+(~\/[.]ssh\/)?passport[.]d\/[*]([.]conf)?[[:space:]]*$/ { next } { print }' "$config" > "$temporary"
else
printf '%s\n' "$include" > "$temporary"
fi
chmod 600 "$temporary"
ssh -F "$temporary" -G euler >/dev/null || {
rm -f "$euler_temporary" "$temporary"
printf 'STOP: generated SSH config is invalid; the original files are unchanged\n' >&2
exit 1
}
if ! mv "$euler_temporary" "$euler_config" || ! mv "$temporary" "$config" || ! ssh -G euler >/dev/null; then
rm -f "$euler_temporary" "$temporary"
if restore_originals; then
printf 'STOP: install failed; the original SSH files were restored automatically\n' >&2
else
printf 'STOP: install failed and automatic restore was incomplete. Restore only config.%s and euler.conf.%s from %s\n' "$stamp" "$stamp" "$backup_dir" >&2
fi
exit 1
fi
)
RunTerminal on Linux (Bash)
(
set -eu
printf 'Short ETH username: '; read -r eth_user
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: invalid ETH username\n' >&2; exit 1;; esac
ssh_dir="$HOME/.ssh"; config="$ssh_dir/config"; include_dir="$ssh_dir/passport.d"; backup_dir="$ssh_dir/passport-backups"; euler_config="$include_dir/euler.conf"; key="$ssh_dir/id_ed25519_euler"
[ ! -L "$config" ] || { printf "STOP: %s is a symbolic link; no file was changed\n" "$config" >&2; exit 1; }
[ ! -L "$euler_config" ] || { printf "STOP: %s is a symbolic link; no file was changed\n" "$euler_config" >&2; exit 1; }
config_existed=0; euler_config_existed=0
if [ -e "$config" ]; then [ -f "$config" ] || { printf "STOP: %s is not a regular file\n" "$config" >&2; exit 1; }; config_existed=1; fi
if [ -e "$euler_config" ]; then [ -f "$euler_config" ] || { printf "STOP: %s is not a regular file\n" "$euler_config" >&2; exit 1; }; euler_config_existed=1; fi
resolved_before="$(ssh -G euler 2>/dev/null)" || {
printf 'STOP: existing SSH config is invalid; no file was changed\n' >&2
exit 1
}
resolved_host="$(printf '%s\n' "$resolved_before" | awk '$1 == "hostname" { print $2; exit }')"
resolved_user="$(printf '%s\n' "$resolved_before" | awk '$1 == "user" { print $2; exit }')"
if printf '%s\n' "$resolved_before" | awk '$1 == "identityfile" && $2 ~ /[.]pub$/ { found=1 } END { exit !found }'; then
printf 'STOP: existing IdentityFile points at a .pub file\n' >&2
exit 1
fi
use_existing=0
if [ "$resolved_host" = "euler.ethz.ch" ] && [ "$resolved_user" = "$eth_user" ]; then
if ssh -o Port=22 -o ProxyCommand=none -o ProxyJump=none -o ForwardAgent=no -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler 'exit 0'; then
use_existing=1
fi
fi
private_exists=0; public_exists=0
[ -f "$key" ] && private_exists=1
[ -f "$key.pub" ] && public_exists=1
if [ "$use_existing" -eq 0 ] && [ "$private_exists" -ne "$public_exists" ]; then printf 'STOP: standard key pair is incomplete\n' >&2; exit 1; fi
standard_key_works=0
if [ "$use_existing" -eq 0 ] && [ "$private_exists" -eq 1 ]; then
if ssh -F none -i "$key" -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no "$eth_user@euler.ethz.ch" 'exit 0'; then
standard_key_works=1
fi
fi
if [ "$use_existing" -eq 0 ] && [ "$standard_key_works" -eq 0 ]; then
printf 'STOP: no tested key-only identity is available\n' >&2
exit 1
fi
mkdir -p "$include_dir" "$backup_dir" && chmod 700 "$ssh_dir" "$include_dir" "$backup_dir"
stamp="$(date +%Y%m%d-%H%M%S)-$$"
config_backup="$backup_dir/config.$stamp"
euler_config_backup="$backup_dir/euler.conf.$stamp"
[ "$config_existed" -eq 0 ] || cp -p "$config" "$config_backup"
[ "$euler_config_existed" -eq 0 ] || cp -p "$euler_config" "$euler_config_backup"
restore_originals() {
restore_status=0
if [ "$config_existed" -eq 1 ]; then cp -p "$config_backup" "$config" || restore_status=1; else rm -f "$config" || restore_status=1; fi
if [ "$euler_config_existed" -eq 1 ]; then cp -p "$euler_config_backup" "$euler_config" || restore_status=1; else rm -f "$euler_config" || restore_status=1; fi
return "$restore_status"
}
euler_temporary="$euler_config.passport-new"
{
printf 'Host euler\n HostName euler.ethz.ch\n User %s\n' "$eth_user"
if [ "$use_existing" -eq 1 ]; then
identity_count=0
while IFS= read -r identity; do
[ -n "$identity" ] || continue
case "$identity" in *'"'*) printf 'STOP: unsupported quote in resolved identity path\n' >&2; exit 1;; esac
printf ' IdentityFile "%s"\n' "$identity"
identity_count=$((identity_count + 1))
done <<EOF
$(printf '%s\n' "$resolved_before" | awk '$1 == "identityfile" { $1=""; sub(/^[[:space:]]+/, ""); print }')
EOF
[ "$identity_count" -gt 0 ] || { printf 'STOP: working alias has no inspectable identity selection\n' >&2; exit 1; }
identities_only="$(printf '%s\n' "$resolved_before" | awk '$1 == "identitiesonly" { print $2; exit }')"
case "$identities_only" in yes|no) ;; *) printf 'STOP: invalid resolved IdentitiesOnly value\n' >&2; exit 1;; esac
printf ' IdentitiesOnly %s\n' "$identities_only"
else
printf ' IdentityFile ~/.ssh/id_ed25519_euler\n IdentitiesOnly yes\n'
fi
printf ' Port 22\n ProxyCommand none\n ProxyJump none\n PreferredAuthentications publickey\n PasswordAuthentication no\n KbdInteractiveAuthentication no\n ForwardAgent no\n'
} > "$euler_temporary"
chmod 600 "$euler_temporary"
ssh -F "$euler_temporary" -G euler >/dev/null || {
rm -f "$euler_temporary"
printf 'STOP: generated Euler block is invalid\n' >&2
exit 1
}
include='Include ~/.ssh/passport.d/*.conf'; temporary="$config.passport-new"
if [ -f "$config" ]; then
awk -v include="$include" 'BEGIN { print include } /^[[:space:]]*Include[[:space:]]+(~\/[.]ssh\/)?passport[.]d\/[*]([.]conf)?[[:space:]]*$/ { next } { print }' "$config" > "$temporary"
else
printf '%s\n' "$include" > "$temporary"
fi
chmod 600 "$temporary"
ssh -F "$temporary" -G euler >/dev/null || {
rm -f "$euler_temporary" "$temporary"
printf 'STOP: generated SSH config is invalid; the original files are unchanged\n' >&2
exit 1
}
if ! mv "$euler_temporary" "$euler_config" || ! mv "$temporary" "$config" || ! ssh -G euler >/dev/null; then
rm -f "$euler_temporary" "$temporary"
if restore_originals; then
printf 'STOP: install failed; the original SSH files were restored automatically\n' >&2
else
printf 'STOP: install failed and automatic restore was incomplete. Restore only config.%s and euler.conf.%s from %s\n' "$stamp" "$stamp" "$backup_dir" >&2
fi
exit 1
fi
)
Look forThe command finishes without a config error. A working existing identity is retained, or the tested id_ed25519_euler private key is selected. Prior files have timestamped backups, and a failed final install restores them automatically.
If this differsDo not append another host block. The command restores the original files after an install failure. If it reports an incomplete restore, use only the two timestamped files it names; otherwise open the Passport help link and include the error with private information removed.
Explanation and help
What this means
Display the resolved settings.
10
Verify the resolved SSH fields
On your computer
Run ssh -G, which displays resolved settings without connecting. Verify the target computer, port, short ETH username, private-key path, allowed login methods, proxy state, and agent-forwarding setting that OpenSSH will actually use.
Look forhostname is euler.ethz.ch; port is 22; user is the short ETH username; no identityfile ends in .pub; preferredauthentications is publickey; passwordauthentication and kbdinteractiveauthentication are no; forwardagent is no; no proxycommand or proxyjump line is present. A newly generated setup also shows id_ed25519_euler and identitiesonly yes.
If this differsStop and correct only passport.d/euler.conf. Keep unrelated SSH hosts and backup files unchanged.
Explanation and help
What this means
Run the final alias test.
11
Prove the alias is key-only
On your computer
Run the alias with ETH-password and keyboard-interactive fallback disabled. A prompt for the local key passphrase is allowed.
Look forEuler prints exactly config-ok without asking for the ETH password.
If this differsStop. Use the resolved fields and direct key-only result to diagnose the alias.
Explanation and help
What this means
Enter config-ok in the confirmation field and run Check my work.
12
Configure euler-tunnel only after config-ok
On your computer
Optional, only if you need VS Code on an allocated compute node: open the separate euler-tunnel reference after config-ok. The basic SSH lesson is complete without that extra setup.
Stop at the first failed gate. Do not regenerate repeatedly, overwrite keys,
replace the whole SSH config, or loosen permissions broadly. Use
Euler SSH troubleshooting
and share only the exact error plus the requested ssh -G fields, which exclude
credentials and private-key contents.
Inspect every path and backup before accepting an SSH repair. A .pub file in
IdentityFile, concatenated host blocks, or PowerShell pasted into Euler Bash
are configuration errors, not reasons to delete all SSH state.