fatedier/frp · error

internal error

Error message

internal error

What it means

Returned by the SSH gateway's PublicKeyCallback when loadAuthorizedKeysFromFile fails while authenticating an incoming SSH connection. The message is intentionally generic ('internal error') so the client learns nothing about the server's filesystem, while the real cause (unreadable or unparseable authorized keys file) is written to the frps log via log.Errorf. Every SSH key-auth attempt re-reads the file, so a broken file rejects all key-based logins.

Source

Thrown at pkg/ssh/gateway.go:79

				err = os.WriteFile(cfg.AutoGenPrivateKeyPath, privateKeyBytes, 0o600)
			}
		}
	}
	if err != nil {
		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{

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Check the frps log for the 'load authorized keys file error: %v' line — it contains the real cause (ENOENT, EACCES, parse error).
  2. Verify the path in the frps sshd-gateway AuthorizedKeysFile setting exists and is a readable regular file (ls -l, run as the frps user).
  3. Fix permissions: the frps process user needs read access; in containers confirm the volume mount target matches the configured path.
  4. Validate the file format: one entry per line, comments with #, keys in OpenSSH authorized_keys format.
  5. Retry the SSH connection; the file is re-read on every auth attempt, so no frps restart is needed once fixed.

Example fix

# frps.toml — before
[sshServer]
authorizedKeysFile = "/etc/frp/authorized_keys"   # wrong path

# after
[sshServer]
authorizedKeysFile = "/etc/frp/authorized_keys"   # ensure: chmod 644, owned/readable by frps user, mounted in container
Defensive patterns

Strategy: validation

Validate before calling

// before starting the frp SSH gateway, verify the authorized keys file
func checkAuthorizedKeysFile(path string) error {
    if path == "" {
        return nil // NoClientAuth mode
    }
    f, err := os.Open(path)
    if err != nil {
        return fmt.Errorf("authorized keys file unreadable: %w", err)
    }
    defer f.Close()
    sc := bufio.NewScanner(f)
    n := 0
    for sc.Scan() {
        line := strings.TrimSpace(sc.Text())
        if line == "" || strings.HasPrefix(line, "#") {
            continue
        }
        fields := strings.Fields(line)
        if len(fields) < 2 {
            return fmt.Errorf("malformed authorized keys line %d", n+1)
        }
        n++
    }
    return sc.Err()
}

Prevention

When it happens

Trigger: cfg.AuthorizedKeysFile points to a file frps cannot read (missing path, permission denied, SELinux/AppArmor denial) or one containing malformed lines that make parsing fail. Any client connecting with `ssh -i key v0@server` then receives this error during authentication.

Common situations: AuthorizedKeysFile path typo in frps.toml; file owned by root while frps runs as a non-root service; empty or BOM-prefixed authorized_keys file; file deleted or rotated after frps start; containers where the file was not mounted into the expected path.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/42261bff411198cc. Report an issue: GitHub.