Skip to content

SSH Key access fails with "Internal Server Connection Error" / "Key check failed" after upgrade to Gitea 1.27 #38903

Description

@bwenrich

Gitea Version

1.27.0

What happened?

After upgrading from Gitea v1.26.4 to v1.27.0, all clients using SSH Keys were no longer able to git pull/push. It also happens when upgrading to v1.27.1.
This had previously been functioning OK for a long time, possibly back to v1.8 in 2019.

I have resolved this after finding a somewhat similar problem mentioned in #35776 and using their diagnostic steps, but wanted to create this issue for:

  • Improving search results for users who might find the same errors in the future
  • Getting feedback on changes in v1.27 that might have caused the changed behavior

Symptom

Clients get an "error: Internal Server Connection Error" when doing git clone / push / pull to Gitea 1.27. All clients using SSH Key for authentication were impacted. ex:

 git clone ssh://git@sub.example.com:11/sshtest/myrepo.git
Cloning into 'myrepo'...
error:
error: Internal Server Connection Error
error:
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

$ git remote -v
origin  ssh://git@sub.example.com:11/domain.tld/container-tools.git (fetch)
origin  ssh://git@sub.example.com:11/domain.tld/container-tools.git (push)
$ git pull
error:
error: Internal Server Connection Error
error:
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

Note: I also run SSH on a custom port for legacy reasons (SSH_PORT = 11), so was also considering if something about the syntax for cloning repos from other ports was involved...

Investigation

Connection with the SSH Key succeeds at the OS level, but fails from Gitea. This confirms that the authorized_keys file is working & has correct permissions, but something specific to Gitea config/access is causing the problem.

Creating a new client with a new SSH Key did not make any difference

$ ssh -v -T -p 11 git@sub.example.com
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Connecting to sub.example.com [nnn.nnn.nnn.nnn] port 11.
debug1: Connection established.
   # [...]
debug1: Offering public key: /home/sshtest/.ssh/id_ed25519 ED25519 SHA256:<<removed>>
debug1: Server accepts key: /home/sshtest/.ssh/id_ed25519 ED25519 SHA256:<<removed>>
Authenticated to sub.example.com ([nnn.nnn.nnn.nnn]:11) using "publickey".
  # [...]
debug1: Remote: /home/git/.ssh/authorized_keys:46: key options: command
error:
error: Key check failed
error:

Running the check from gitea binary locally also fails with no additional information

git@sub.example.com:~$ /usr/local/bin/gitea --config=/etc/gitea/app.ini serv key-50
error:
error: Key check failed
error:

Each time the connection with SSH Key is attempted, a pair of TLS errors is visible in the Gitea log, ex:

2026/08/12 23:01:34 modules/log/misc.go:72:(*loggerToWriter).Write() [I] http: TLS handshake error from 127.0.0.1:51922: no certificate available for '127.0.0.1'
2026/08/12 23:01:34 modules/log/misc.go:72:(*loggerToWriter).Write() [I] http: TLS handshake error from 127.0.0.1:51930: no certificate available for '127.0.0.1'

This hints that the SSH Key validation does something in the API internally, but wasn't sure yet what/why it is doing.

From the comments on the other Issue, set RUN_MODE = dev, and tried the tests again. Now, the URL triggering the TLS error is visible.

git@sub.example.com:~$ /usr/local/bin/gitea --config=/etc/gitea/app.ini serv key-50
error:
error: Key check failed
error:
Gitea: Failed to check provided key: unable to contact gitea "GET https://0.0.0.0:443/api/internal/serv/none/50": Get "https://0.0.0.0:443/api/internal/serv/none/50": remote error: tls: internal error

Checking with curl, Gitea is indeed unable to serve HTTPS for 0.0.0.0. This makes some sense, as only the real URL is included in ACME certs / SNI would not be functional for the wrong host.

$ curl -vkI https://0.0.0.0:443
*   Trying 0.0.0.0:443...
* Connected to 0.0.0.0 (127.0.0.1) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS alert, internal error (592):
* OpenSSL/3.0.13: error:0A000438:SSL routines::tlsv1 alert internal error
* Closing connection
curl: (35) OpenSSL/3.0.13: error:0A000438:SSL routines::tlsv1 alert internal error

Inside app.ini, the LOCAL_ROOT_URL defines that URL where the 0.0.0.0 is coming unexpectedly - I don't remember why I have specifically configured this, but I think it matches the Gitea Cheat Sheet anyway...

[server]
ROOT_URL            = https://sub.example.com
PROTOCOL            = https
HTTP_PORT           = 443
SSL_MIN_VERSION     = TLSv1.2
REDIRECT_OTHER_PORT = true
PORT_TO_REDIRECT    = 80
DOMAIN              = sub.example.com
LOCAL_ROOT_URL      = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
DISABLE_SSH         = false
SSH_PORT            = 11
# [...]

I do not set HTTP_ADDR and the Cheat Sheet suggests is defaults to 0.0.0.0. However I think that using it in LOCAL_ROOT_URL causes some internal logic to be overridden, as the Cheat Sheet also says that it defaults to localhost when listen on 0.0.0.0

Removing the LOCAL_ROOT_URL from app.ini also causes a failure, but this time the failing URL is for https://localhost:443

error:
error: Key check failed
error:
Gitea: Failed to check provided key: unable to contact gitea "GET https://localhost:443/api/internal/serv/none/50": Get "https://localhost:443/api/internal/serv/none/50": remote error: tls: internal error

Setting the LOCAL_ROOT_URL using my actual domain name instead of HTTP_ADDR fixes the problem.

Key Findings

Some of the error messages and diagnostic steps were not immediately helpful, but I found these to be the most important points

  • The SSH Key is accepted by the OS but rejected by Gitea
  • Each time the SSH Key access is attempted, a TLS error is visible in the gitea logs
  • With gitea in RUN_MODE = dev, the URL triggering the TLS error is shown in the gitea serv key-## output
  • The URL showing in the gitea serv key-## error is from LOCAL_ROOT_URL in app.ini

Resolution

Change the LOCAL_ROOT_URL to use the actual domain name (prevent using 0.0.0.0 or localhost)

[server]
DOMAIN          = sub.example.com
#LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
LOCAL_ROOT_URL  = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
  # or
LOCAL_ROOT_URL  = %(PROTOCOL)s://sub.example.com:%(HTTP_PORT)s/

Root Cause

  • Misconfigured LOCAL_ROOT_URL in app.ini
  • Changes in SSH Key validation in v1.27? I saw some validation items mentioned in the release notes but could not tell if this was an expected behavior.

How are you running Gitea?

Manual binary install
Ubuntu 24.04 (systemd service)
PosgreSQL 16(?)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions