Introduction

Self-signed SSL certificates are commonly used in development, testing, and internal environments. By default, curl and most HTTP clients reject self-signed certificates because they are not signed by a trusted Certificate Authority. While --insecure (-k) bypasses this check, it disables all certificate validation, creating a security risk. The proper approach is to add the self-signed certificate (or its CA) to the trusted certificate store.

Symptoms

  • curl https://internal-server fails with SSL certificate problem: self-signed certificate
  • curl: (60) SSL certificate problem: self signed certificate
  • Python requests fails with SSLError: certificate verify failed: self-signed certificate
  • Java fails with PKIX path building failed: unable to find valid certification path
  • Using -k flag works but is flagged as insecure in code reviews

Common Causes

  • Internal server using self-signed certificate for HTTPS
  • Development environment without a private CA infrastructure
  • Certificate generated with openssl req -x509 for testing
  • Container or VM spun up with auto-generated self-signed cert
  • Internal API service not provisioned with a proper CA-signed certificate

Step-by-Step Fix

  1. 1.Download the self-signed certificate from the server:
  2. 2.```bash
  3. 3.echo | openssl s_client -connect internal-server:443 -servername internal-server 2>/dev/null | \
  4. 4.openssl x509 -out internal-server.pem
  5. 5.# Verify it is self-signed
  6. 6.openssl x509 -in internal-server.pem -noout -issuer -subject
  7. 7.# Issuer and Subject should be identical for self-signed
  8. 8.`
  9. 9.Add the certificate to the system CA bundle:
  10. 10.```bash
  11. 11.# Debian/Ubuntu
  12. 12.sudo cp internal-server.pem /usr/local/share/ca-certificates/internal-server.crt
  13. 13.sudo update-ca-certificates

# RHEL/CentOS sudo cp internal-server.pem /etc/pki/ca-trust/source/anchors/ sudo update-ca-trust ```

  1. 1.Verify curl now trusts the certificate:
  2. 2.```bash
  3. 3.curl -vI https://internal-server 2>&1 | grep -E "SSL|HTTP"
  4. 4.# Should show successful SSL handshake without -k flag
  5. 5.`
  6. 6.For a single curl command, use --cacert instead of -k:
  7. 7.```bash
  8. 8.curl --cacert internal-server.pem https://internal-server
  9. 9.# This validates only against this specific CA, not all CAs
  10. 10.`
  11. 11.For Python requests, specify the CA bundle:
  12. 12.```python
  13. 13.import requests
  14. 14.response = requests.get('https://internal-server', verify='/path/to/internal-server.pem')
  15. 15.`
  16. 16.For Java, import into the truststore:
  17. 17.```bash
  18. 18.sudo keytool -import -trustcacerts \
  19. 19.-alias internal-server \
  20. 20.-file internal-server.pem \
  21. 21.-keystore $JAVA_HOME/lib/security/cacerts \
  22. 22.-storepass changeit -noprompt
  23. 23.`

Prevention

  • Set up an internal Certificate Authority for all internal services
  • Use Let's Encrypt or internal PKI instead of self-signed certificates for production
  • Automate CA bundle updates through configuration management
  • Document the certificate trust setup for new team member onboarding
  • For development environments, use mkcert to generate locally-trusted certificates