Programming languages

Go "x509: certificate relies on legacy Common Name field" fix

By · Last verified

Error messagex509: certificate relies on legacy Common Name field, use SANs instead

x509: certificate relies on legacy Common Name field, use SANs instead means the server certificate names its host only in the Subject Common Name (CN) and has no Subject Alternative Name (SAN) extension, which Go no longer accepts. The certificate must be reissued with SANs; there is no client-side switch left in current Go versions.

Quick fix

Reissuing the certificate with SANs is the only fix.

Confirm that the certificate has no SAN extension:

openssl s_client -connect registry.internal.example.com:443 -servername registry.internal.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -ext subjectAltName

If you see subject=CN = registry.internal.example.com and no X509v3 Subject Alternative Name block, reissue the certificate with SANs. For an internal CA:

# 1. Key and CSR with SANs
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr \
  -subj "/CN=registry.internal.example.com" \
  -addext "subjectAltName=DNS:registry.internal.example.com,IP:203.0.113.10"

# 2. Sign it, copying the SAN into the certificate
cat > san.ext <<'EOF'
subjectAltName = DNS:registry.internal.example.com, IP:203.0.113.10
extendedKeyUsage = serverAuth
EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 365 -sha256 -extfile san.ext -out server.crt

openssl x509 -req does not copy extensions from the CSR unless told to, which is how many SAN-less certificates were created; the -extfile above handles that.

Causes

1. Old internal or self-signed certificate

Certificates made years ago with openssl req -x509 -subj "/CN=host" and no extensions are the typical source.

2. Internal CA that drops SANs while signing

Some home-grown signing scripts and older CA software sign the CSR but ignore its extensions.

  • Confirm: the CSR has SANs (openssl req -in server.csr -noout -text) but the issued certificate does not.
  • Fix: configure the CA to copy or add subjectAltName (OpenSSL: -extfile or copy_extensions = copy in the CA config).

3. Appliances and embedded devices

Storage arrays, IPMI/BMC interfaces, older firewalls and printers often ship default certificates with only a CN. Replace the device certificate with one that has SANs, issued by your internal CA.

4. Upgraded client, unchanged server

The server did not change; your client was rebuilt with a newer Go version. That is why the error tends to appear after upgrading Docker, kubectl or Terraform.

Fixes by environment

Every environment needs the same fix: reissue the certificate with the hostname in the SAN extension.

Private Docker registry: reissue the registry certificate with SANs, then restart the registry. Clients keep their CA in /etc/docker/certs.d/<host>/ca.crt; see Docker x509 error.

Kubernetes admission webhooks: the API server calls the webhook Service by its DNS name, for example my-webhook.my-namespace.svc. Put that name in the certificate's SANs. With cert-manager, list it in spec.dnsNames of the Certificate.

etcd and kubeadm clusters: kubeadm-generated certificates already include SANs. Hand-made etcd certificates from old tutorials may not; regenerate them with the node names and IPs as SANs.

Java and other clients: Java still falls back to the CN for DNS names in some cases, so a certificate may work in Java and fail in Go. Fix the certificate once and both work; see also No subject alternative names present.

Don't do this

  • Pinning an old Go version or an old Docker/kubectl release to keep the fallback. You give up security fixes to keep a broken certificate.
  • InsecureSkipVerify: true or --insecure-skip-tls-verify. This removes all verification, not only the CN check.
  • Setting GODEBUG=x509ignoreCN=0 and assuming it worked. On Go 1.17 and later the variable is ignored, so you will waste time on a no-op.

FAQ

Can I set GODEBUG=x509ignoreCN=0 to make it work again?

No. That switch existed only as a temporary escape hatch in Go 1.15 and 1.16, and the Go 1.17 release notes state that it has been removed. Any binary built with Go 1.17 or later ignores it.

The certificate has a CN that matches. Why is that not enough?

Since 2000 (RFC 2818), clients have been told to use the SAN extension when present, and the CA/Browser Forum requires SANs for every public certificate. Go stopped supporting the old fallback. Public certificates already have SANs, so this error almost always comes from internal or self-signed certificates.

Which tools show this error?

Any Go program that verifies TLS: docker pull against a private registry, kubectl and kubelet, webhooks called by the Kubernetes API server, Terraform providers, Prometheus scrapes, etcd clients, Helm and many more.

How do I check whether my certificate has SANs?

Run openssl x509 -in server.crt -noout -ext subjectAltName. No output means no SAN extension. You can also paste the PEM into the certificate decoder.

Sources

  1. Go 1.15 Release Notes: X.509 CommonName deprecation
  2. Go 1.17 Release Notes
  3. Go package crypto/x509
  4. openssl-x509 (OpenSSL 3.0)