Securing NetBSD communication with TLS: generating keys and CSRs with OpenSSL, creating self-signed certificates, managing trust with ca-certificates, and automating Let's Encrypt certificates.

In episode 13 we secured the administration gate with key-based SSH. But many other services — web, email, API — speak different protocols and need a different security layer: TLS. In this episode we'll dissect TLS, OpenSSL, and certificates on NetBSD — generating keys and CSRs, creating self-signed certificates, managing trust with ca-certificates, and automating Let's Encrypt certificates.
| Term | Explanation |
|---|---|
| TLS | The protocol that encrypts communication (successor to SSL) |
| Certificate | A digital document containing identity + public key |
| Key | The secret private key owned by the server |
| CSR | Certificate signing request |
| CA | The authority that signs certificates |
| Chain of trust | The trust chain from a CA to the certificate |
The flow: the server generates a private key, creates a CSR with its identity, then the CSR is signed into a certificate — either by a public CA (Let's Encrypt) or by itself (self-signed).
OpenSSL is part of the NetBSD base system. Verify its version:
openssl versionOpenSSL 3.4.1 6 Jun 2026For a website web.example.com, generate an RSA 3072 key and CSR in one go:
openssl req -new -newkey rsa:3072 -nodes \
-keyout web.key -out web.csr \
-subj "/CN=web.example.com"Generating a RSA private key
...
writing new private key to 'web.key'Two files are produced: web.key (private, secret) and web.csr (request, public). Send the CSR to a CA for signing, or sign it yourself.
Warning
-nodes means the key is not encrypted with a passphrase — convenient for daemons that must start automatically, but it also means anyone who controls the web.key file controls that identity. Store it in a protected location, e.g. /etc/openssl/private with mode 600.
For internal testing, create a self-signed certificate valid for 365 days:
openssl req -x509 -newkey rsa:3072 -nodes \
-keyout web.key -out web.crt -days 365 \
-subj "/CN=web.example.com"Generating a RSA private key
...Check the certificate contents:
openssl x509 -in web.crt -text -nooutCertificate:
Data:
Version: 3 (0x2)
Serial Number: ...
Subject: CN=web.example.com
Not Before: Aug 3 12:00:00 2026 GMT
Not After : Aug 3 12:00:00 2027 GMT
Public Key Algorithm: rsaEncryption
...Note the validity period (Not Before/Not After) — the first thing checked when troubleshooting a "certificate expired" error.
Verify the certificate against the chain and key:
openssl verify -CAfile ca.crt web.crtweb.crt: OKVerify that the key matches the certificate (their moduli must be identical):
openssl pkey -in web.key -pubout -outform DER | sha256
openssl x509 -in web.crt -pubkey -noout -outform DER | sha256For applications to trust public CAs (e.g. when calling HTTPS APIs), a CA list must exist. NetBSD provides the ca-certificates package:
cd /usr/pkgsrc/security/mozilla-rootcerts
make installExtracting certificates from Mozilla's NSS
Creating /etc/openssl/certs/ca-certificates.crtThe collected CA certificates are placed in /etc/openssl/certs/ca-certificates.crt — the file applications use (via SSL_CERT_FILE) to verify outgoing connections.
For an internal CA (e.g. a corporate CA), add it to the trust store:
cp internal-ca.crt /etc/openssl/certs/
rehash /etc/openssl/certs/etc/openssl/certs/internal-ca.crt -> internal-ca.crtSelf-signed certificates aren't trusted by public browsers. For publicly accessible services, use Let's Encrypt — a free CA that issues 90-day certificates and can renew them automatically. NetBSD provides acme-client (the native BSD tool) and acme.sh.
Install from pkgsrc:
cd /usr/pkgsrc/security/acme-client
make install cleanThe configuration lives in /usr/pkg/etc/acme-client.conf:
authority letsencrypt {
api url "https://acme-v02.api.letsencrypt.org/directory"
account key "/etc/ssl/account.key"
}
domain web.example.com {
domain key "/etc/ssl/private/web.key"
domain certificate "/etc/ssl/web.crt"
domain full chain "/etc/ssl/web-fullchain.pem"
challenge "/var/www/htdocs/web"
}Issue the first certificate:
acme-client -v web.example.comacme-client: web.example.com: certificate signedLet's Encrypt certificates are valid for 90 days. Renew automatically via cron — for example every day at 03:00, plus a reload of the web service:
0 3 * * * acme-client web.example.com && service nginx reload| Rule | Reason |
|---|---|
| 3072-bit keys or higher | RSA 2048 is starting to be considered weak |
| Short self-signed validity | Reduce the risk of a leaked cert being used for long |
Strict protection of web.key (mode 600) | The private key is the server's identity |
| Let's Encrypt auto-renewal | Eliminate manual work and expiry risk |
Use ca-certificates for trust | Outbound verification works correctly |
In this episode 14, you've secured NetBSD communication with TLS: generating keys and CSRs with OpenSSL, creating and verifying self-signed certificates, managing trust with ca-certificates, and automating Let's Encrypt certificate issuance with acme-client.
Key takeaways:
openssl req -x509 for self-signed, openssl verify and modulus matching for verification.ca-certificates → /etc/openssl/certs/ca-certificates.crt.acme-client + cron renewal.In the next episode, episode 15, we'll reinforce the system from within: security features and PaX — enabling ASLR and W^X, managing per-binary flags with paxctl, and applying server security best practices with sysctl. See you in episode 15!