I've created a self-signed certificate for foo.localhost using a Let's Encrypt recommendation using this Makefile:
include ../.env
configuration = csr.cnf
certificate = self-signed.crt
key = self-signed.key
.PHONY: all
all: $(certificate)
$(certificate): $(configuration)
openssl req -x509 -out $@ -keyout $(key) -newkey rsa:2048 -nodes -sha256 -subj '/CN=$(HOSTNAME)' -extensions EXT -config $(configuration)
$(configuration):
printf "[dn]\nCN=$(HOSTNAME)\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:$(HOSTNAME)\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth" > $@
.PHONY: clean
clean:
$(RM) $(configuration)
I've then assigned that to a web server. I've verified that the server returns the relevant certificate:
$ openssl s_client -showcerts -connect foo.localhost:8443 < /dev/null
CONNECTED(00000003)
depth=0 CN = foo.localhost
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = foo.localhost
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/CN=foo.localhost
i:/CN=foo.localhost
-----BEGIN CERTIFICATE-----
[…]
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=foo.localhost
issuer=/CN=foo.localhost
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1330 bytes and written 269 bytes
Verification error: unable to verify the first certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-GCM-SHA256
Session-ID: […]
Session-ID-ctx:
Master-Key: […]
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket:
[…]
Start Time: 1529622990
Timeout : 7200 (sec)
Verify return code: 21 (unable to verify the first certificate)
Extended master secret: no
---
DONE
How do I make cURL trust it without modifying anything in /etc? --cacert
does not work, presumably because there is no CA:
$ curl --cacert tls/foo.localhost.crt 'https://foo.localhost:8443/'
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
The goal is to enable HTTPS during development:
- I can't have a completely production-like certificate without a lot of work to enable DNS verification in all development environments. Therefore I have to use a self-signed certificate.
- I still obviously want to make my development environment as similar as possible to production, so I can't simply ignore any and all certificate issues.
curl -k
is likecatch (Exception e) {}
in this case - nothing at all like a browser talking to a web server.
In other words, when running curl [something] https://project.local/api/foo
I want to be confident that
- if TLS is configured properly except for having a self-signed certificate the command will succeed and
- if I have any issues with my TLS configuration except for having a self-signed certificate the command will fail.
Using HTTP or --insecure
fails the second criterion.
openssl x509 <file
to make sure it's in the right format andopenssl s_client ... -CAfile file
to see if that validates. (BTW-showcerts
only applies to chain certs from the server and is meaningless when there are no chain certs.) Also,curl
doesn't always use OpenSSL and if not it doesn't always accept exactly the same formats; checkcurl -V
(uppercase V).openssl s_client -showcerts
answer, butcurl -v --cacert cacert.pem URL
won't add the self-signed CA as an explicit whitelisting of trust withCERT_TRUST_REVOCATION_STATUS_UNKNOWN
.