we-promise/sure · warning

[SSL] Could not find system CA bundle - using custom CA only

Error message

[SSL] Could not find system CA bundle - using custom CA only

What it means

Log warning from SslInitializerHelper#create_combined_ca_bundle (config/initializers/00_ssl.rb:160) when find_system_ca_bundle locates no system CA bundle path (none of the known distro locations like /etc/ssl/certs/ca-certificates.crt exists or is readable). The helper then returns nil and the caller falls back to trusting only the custom CA, meaning TLS connections to public services (whose chains root in system CAs) may fail verification.

Source

Thrown at config/initializers/00_ssl.rb:160

        bundle_path = File.join(openssl_ca_dir, bundle_name)
        return bundle_path if File.exist?(bundle_path) && File.readable?(bundle_path)
      end
    end

    nil
  end

  # Creates a combined CA bundle with system CAs and custom CA.
  # Writes to a predictable path (tmp/ssl_ca_bundle.pem) for easy debugging
  # and to avoid Tempfile GC lifecycle issues.
  #
  # @param custom_ca_path [String] Path to the custom CA certificate
  # @param output_path [String] Where to write the combined bundle
  # @return [String, nil] Path to the combined bundle, or nil on failure
  def create_combined_ca_bundle(custom_ca_path, output_path: COMBINED_CA_BUNDLE_PATH)
    system_ca = find_system_ca_bundle
    unless system_ca
      Rails.logger.warn("[SSL] Could not find system CA bundle - using custom CA only")
      return nil
    end

    begin
      system_content = File.read(system_ca)
      custom_content = File.read(custom_ca_path)

      # Ensure the parent directory exists
      FileUtils.mkdir_p(File.dirname(output_path))

      File.write(output_path, system_content + "\n# Custom CA Certificate\n" + custom_content)

      Rails.logger.info("[SSL] Created combined CA bundle: #{output_path}")
      Rails.logger.info("[SSL]   - System CA source: #{system_ca}")
      Rails.logger.info("[SSL]   - Custom CA source: #{custom_ca_path}")

      output_path.to_s
    rescue StandardError => e

View on GitHub (pinned to e69894adb9)

Solutions

  1. Install system CA certificates in the image/host: apt-get install -y ca-certificates (Debian/Ubuntu) or apk add ca-certificates (Alpine), then restart.
  2. Point the config at an explicit bundle path if your distro keeps it elsewhere, so find_system_ca_bundle succeeds.
  3. Verify afterwards: the log should show the combined bundle at tmp/ssl_ca_bundle.pem and SSL_CERT_FILE set to it.
  4. As a stopgap, understand the fallback: only endpoints whose chain terminates in your custom CA will verify.

Example fix

// before
# Dockerfile (no system CAs) -> warning + public TLS failures

// after
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates
Defensive patterns

Strategy: fallback

Validate before calling

SYSTEM_CA_PATHS = %w[/etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt]
SYSTEM_CA_PATHS.any? { |p| File.readable?(p) }

Prevention

When it happens

Trigger: Booting the app in a minimal container (alpine/distroless/slim) that lacks the ca-certificates package, or on a host with a non-standard CA path, while a custom CA (SSL_CA_FILE-style config) is set.

Common situations: Docker images built without ca-certificates; hardened servers where /etc/ssl was trimmed; local dev boxes using OpenSSL in a Homebrew/Macports prefix the helper does not probe.

Understand the failure class

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/b8d4a0f5cf27a715. Report an issue: GitHub.