fatedier/frp · warning
unknown public key for remoteAddr %q
Error message
unknown public key for remoteAddr %q
What it means
Returned by the SSH gateway's PublicKeyCallback when the connecting client's public key (compared by marshaled key bytes) is not present in the authorized keys map. This is the SSH equivalent of 'permission denied (publickey)': the key file loaded fine, but the presented key was not listed. The remote address is included to help server operators correlate rejections in logs.
Source
Thrown at pkg/ssh/gateway.go:84
return nil, err
}
privateKey, err := ssh.ParsePrivateKey(privateKeyBytes)
if err != nil {
return nil, err
}
sshConfig.AddHostKey(privateKey)
sshConfig.NoClientAuth = cfg.AuthorizedKeysFile == ""
sshConfig.PublicKeyCallback = func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
authorizedKeysMap, err := loadAuthorizedKeysFromFile(cfg.AuthorizedKeysFile)
if err != nil {
log.Errorf("load authorized keys file error: %v", err)
return nil, fmt.Errorf("internal error")
}
user, ok := authorizedKeysMap[string(key.Marshal())]
if !ok {
return nil, fmt.Errorf("unknown public key for remoteAddr %q", conn.RemoteAddr())
}
return &ssh.Permissions{
Extensions: map[string]string{
"user": user,
},
}, nil
}
ln, err := net.Listen("tcp", net.JoinHostPort(bindAddr, strconv.Itoa(cfg.BindPort)))
if err != nil {
return nil, err
}
return &Gateway{
bindPort: cfg.BindPort,
ln: ln,
peerServerListener: peerServerListener,
sshConfig: sshConfig,
}, nilView on GitHub (pinned to 6c8a8d0a97)
Solutions
- Extract the public key of the key you connect with: ssh-keygen -y -f ~/.ssh/yourkey, and append it as a line to the authorized keys file.
- Confirm ssh is actually offering that key: connect with `ssh -v` and look at 'Offering public key' lines.
- Check the authorized_keys line is well-formed: <type> <base64> [comment], no line wrapping or trailing whitespace.
- Remember the map is keyed on key bytes — identical key material in different files still matches; a different comment on the same key also matches.
Example fix
# generate and register a dedicated key ssh-keygen -t ed25519 -f ~/.ssh/frp_gateway -N '' ssh-keygen -y -f ~/.ssh/frp_gateway >> /etc/frp/authorized_keys # connect ssh -i ~/.ssh/frp_gateway -p <gatewayPort> v0@<frps-host> "tcp 127.0.0.1:22"
Defensive patterns
Strategy: validation
Validate before calling
# client-side preflight: confirm your key is registered before connecting
ssh-keygen -y -f ~/.ssh/frp_gateway | awk '{print $1" "$2}' > /tmp/mypub
sort /tmp/mypub > /tmp/a; sort /etc/frp/authorized_keys | awk '{print $1" "$2}' > /tmp/b
cmp -s /tmp/a /tmp/b && echo registered || echo "key NOT in authorized_keys" Prevention
- Register the exact keypair you will connect with; document it per user.
- Use `ssh -v` to confirm which key is offered when auth fails.
- Keys match by material, not comment — edit the file only by whole lines.
When it happens
Trigger: Client runs `ssh -i wrongkey v0@frps-host -p port` where wrongkey's public half is not in AuthorizedKeysFile; or the user regenerated their keypair; or the authorized_keys entry was edited/truncated (key matches by exact marshaled bytes, not by comment or email suffix).
Common situations: Using the default SSH key (~/.ssh/id_ed25519) instead of the key registered with the gateway; authorized_keys entry copied with a broken base64 body or missing key-type prefix; multiple team members sharing one gateway file with stale entries.
Related errors
- internal error
- HTTP ${response.status}
- loginRespMsg.Error
- ps.Err
- couldn't generate OIDC token for login: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/8776215aac04705c.
Report an issue: GitHub.