Skip to content

fix: trust the certificates named by SSL_CERT_FILE on Windows and macOS - #141

Open
pjcdawkins wants to merge 2 commits into
mainfrom
fix/go-ssl-cert-file
Open

fix: trust the certificates named by SSL_CERT_FILE on Windows and macOS#141
pjcdawkins wants to merge 2 commits into
mainfrom
fix/go-ssl-cert-file

Conversation

@pjcdawkins

Copy link
Copy Markdown
Contributor

Related to #110. Independent of #139 and #140, which deal with the Windows certificate store.

The problem

The legacy CLI trusts the certificates named by SSL_CERT_FILE on every platform, because Composer\CaBundle checks that variable first when it resolves a bundle. The Go part of the CLI only trusts it on Unix: Go reads SSL_CERT_FILE in crypto/x509/root_unix.go, and on Windows and macOS it verifies through the operating system instead.

So for someone whose organization gives them a CA bundle as a file, upsun environment:list works and upsun init does not, on the same machine, for the same reason — with no hint that the two halves of the CLI resolve certificates differently.

The change

certs.UseEnvCertFile() points the default HTTP transport at the file, on the platforms where Go does not read the variable itself. Every request the Go part makes goes through http.DefaultTransport — the API client, the update check, the config fetch, and init — so one call at startup covers all of them.

The file replaces the system certificates rather than adding to them. That matches Go's own behavior on Unix and Composer\CaBundle's everywhere, so the CLI resolves certificates the same way whichever part serves the command.

An unusable file is a warning on stderr rather than a fatal error, and the system certificates are used, which is what both Go and Composer\CaBundle do with one.

Tests

internal/certs serves HTTPS with a certificate the system does not trust, and checks it is rejected with no file set and accepted with the file set. The error cases cover an unreadable path and a file holding no certificates.

Written by Claude Code.

Go reads that variable only on Unix; elsewhere it verifies through the
operating system. The legacy CLI reads it on every platform, through
Composer\CaBundle, so the two parts of the CLI disagreed about which
certificates to trust, and a command implemented in Go failed where the
same command implemented in PHP worked.

An unusable file is reported as a warning rather than being fatal, and
the system certificates are used, as before.

Written by Claude Code.
Copilot AI review requested due to automatic review settings July 30, 2026 00:45
@upsun-dispatch

upsun-dispatch Bot commented Jul 30, 2026

Copy link
Copy Markdown

📋 PR Summary

This PR adds an internal/certs package whose UseEnvCertFile() points the default HTTP transport at the certificate bundle named by SSL_CERT_FILE on Windows and macOS, where Go's own verification does not read that variable (Unix already does). It is invoked once at startup in cmd/platform/main.go, so the API client, update check, config fetch, and init — all of which use http.DefaultTransport/http.DefaultClient — resolve certificates the same way the legacy PHP CLI does. An unusable file downgrades to a stderr warning rather than a fatal error.

Changes
Layer / File(s) Summary
startup wiring
cmd/platform/main.go Calls certs.UseEnvCertFile() early in main() and prints a non-fatal warning to stderr if it fails.
cert bundle
internal/certs/certs.go New package that, on Windows/macOS, mutates http.DefaultTransport's TLSClientConfig.RootCAs to the CA bundle named by SSL_CERT_FILE, replacing system roots; empty path and platform gating are handled.
internal/certs/certs_test.go Tests trust/rejection against an httptest TLS server, plus unreadable-path and no-certificate error cases.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR aligns TLS certificate trust behavior between the Go and legacy PHP halves of the Upsun CLI by honoring SSL_CERT_FILE on Windows and macOS (where Go does not read it automatically), so commands implemented in Go (e.g., init) behave consistently with legacy commands.

Changes:

  • Add internal/certs with UseEnvCertFile() to configure the default HTTP transport to trust the PEM bundle referenced by SSL_CERT_FILE on Windows/macOS.
  • Invoke certs.UseEnvCertFile() early in cmd/platform/main.go, warning (non-fatal) if the file is unreadable or invalid.
  • Add tests in internal/certs covering trust/no-trust behavior and error cases for invalid/missing PEM input.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
internal/certs/certs.go Introduces transport configuration to use SSL_CERT_FILE roots on platforms where Go doesn’t read it.
internal/certs/certs_test.go Adds unit tests validating trust behavior and error handling for the cert-file logic.
cmd/platform/main.go Wires the cert-file logic into CLI startup so Go HTTP requests follow the same trust model as legacy PHP.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/certs/certs.go
Comment on lines +57 to +59
if transport.TLSClientConfig == nil {
transport.TLSClientConfig = &tls.Config{MinVersion: tls.VersionTLS12}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants