Skip to content

android: plumb ExtraRootCAs into wgengine so DERP trusts user/system CA - #842

Open
aefuimn wants to merge 1 commit into
tailscale:mainfrom
aefuimn:release-branch/1.102
Open

android: plumb ExtraRootCAs into wgengine so DERP trusts user/system CA#842
aefuimn wants to merge 1 commit into
tailscale:mainfrom
aefuimn:release-branch/1.102

Conversation

@aefuimn

@aefuimn aefuimn commented Aug 22, 2026

Copy link
Copy Markdown

Summary

Allows the Go engine (libtailscale) to trust a custom self-signed CA for DERP relay connections when it is provided as a user-installed CA (or via App.getUserCACertsPEM()).

The Android app already trusts such a CA for the control client (tsd.System.ExtraRootCAs is passed to ipnlocal). However that pool was not forwarded to wgengine.Config, so magicsock fell back to the system root pool for DERP connections and rejected the CA:

derphttp.Client.Recv: connecting to derp-999 ...
x509: certificate signed by unknown authority

This left the health warnings tls-connection-failed and no-derp-connection even though the control plane itself connected fine.

Change

  • libtailscale/backend.go
    • Wire ExtraRootCAs: sys.ExtraRootCAs into wgengine.Config so DERP TLS uses the same trust pool as the control client (userspace.go -> magicsock).

Notes for reviewers

  • How the CA is trusted: App.getUserCACertsPEM() returns user-installed CA certs (AndroidCAStore accounts with a user: prefix). When the CA is installed this way, sys.ExtraRootCAs is non-nil and this change makes DERP trust it. When the CA is instead installed at the system level, getUserCACertsPEM() returns empty / ExtraRootCAs stays nil, and DERP relies on the engine's native SystemCertPool() — this change is then a no-op.
  • Applies cleanly on release-branch/1.102 (HEAD aea8f60).

Testing / verification

  • Built libtailscale.aar + debug APK successfully.
  • Verified on a Mate 40 Pro (HarmonyOS 4.2.0) against a self-hosted headscale server: after the CA is trusted, no-derp-connection / tls-connection-failed clear and DERP (derp-999, CN) connects.

Updates #20961

@aefuimn
aefuimn force-pushed the release-branch/1.102 branch from a29c19f to 5edaf49 Compare August 22, 2026 05:44
@bradfitz

Copy link
Copy Markdown
Member

Please sent the sys.ExtraRootCAs change separately, with DCO.

As for the LazyColumn crash, is this just a dup of #840? But see my comment at #840 (comment)

@bradfitz bradfitz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sent the sys.ExtraRootCAs change separately, with DCO.

As for the LazyColumn crash, is this just a dup of #840? But see my comment at #840 (comment)

@aefuimn
aefuimn force-pushed the release-branch/1.102 branch from 5edaf49 to 0551be0 Compare August 23, 2026 05:00
@bradfitz bradfitz changed the title android: fix peer-list LazyColumn crash and trust self-signed headscale CA android: plumb ExtraRootCAs into wgengine so DERP trusts user/system CA Aug 23, 2026
@bradfitz

Copy link
Copy Markdown
Member

This should be fixed on main before a release branch, and then cherry-pick (with -x) into the release branch.

@aefuimn
aefuimn changed the base branch from release-branch/1.102 to main August 23, 2026 05:27
App.getUserCACertsPEM() feeds user-installed CAs into tsd.System.ExtraRootCAs,
which ipnlocal passes to the control client. That pool was not forwarded to
wgengine.Config, so magicsock fell back to the system root pool for DERP
connections and rejected a self-signed CA (e.g. for a self-hosted headscale
control server) with "x509: certificate signed by unknown authority".

Pass sys.ExtraRootCAs through to wgengine.Config (userspace -> magicsock) so
DERP TLS trusts the same pool as the control client.

Updates #20961

Signed-off-by: aefuimn <33046665+aefuimn@users.noreply.github.com>
@aefuimn
aefuimn force-pushed the release-branch/1.102 branch from 0551be0 to 0a3f05d Compare August 23, 2026 05:38
@aefuimn
aefuimn requested a review from bradfitz August 23, 2026 05:41
@aefuimn

aefuimn commented Aug 23, 2026

Copy link
Copy Markdown
Author

This should be fixed on main before a release branch, and then cherry-pick (with -x) into the release branch.

I've changed the target branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants