Generate a Self-Signed Certificate with make-ssl-cert
A new service needs TLS today, and make-ssl-cert can hand you a working self-signed PEM pair in under a minute. This guide covers generating the distribution snakeoil certificate, making a separate test certificate, checking what came out, and knowing when a self-signed cert is genuinely the right tool. Examples use ssl-cert 1.1.2ubuntu1 and OpenSSL 3.6.1. Allow about fifteen minutes.
The route
Jump straight to the step you need, or tick off Done means at the end.
This is fine for local testing, development, and any service that explicitly expects snakeoil. It is not a route to a publicly trusted certificate: any browser or client that does not already trust your CA will warn about the result.
1. Check the installed command
Start with read-only checks. Nothing here needs elevated privileges:
$ command -v make-ssl-cert
/usr/sbin/make-ssl-cert
$ dpkg-query -W -f='${Package} ${Version}\n' ssl-cert
ssl-cert 1.1.2ubuntu1
$ make-ssl-cert --help
Usage: make-ssl-cert [options] <template> <output>
make-ssl-cert [options] generate-default-snakeoil
- Manual mode takes a template and an output path.
- generate-default-snakeoil uses
/usr/share/ssl-cert/ssleay.cnfand writes the conventional files under/etc/ssl.
Checkpoint
Confirm the template exists before trying manual mode:
$ test -r /usr/share/ssl-cert/ssleay.cnf && echo 'template is readable'
template is readable
2. Generate the distribution snakeoil certificate
On a normal Debian or Ubuntu box, this is the direct route:
$ sudo make-ssl-cert generate-default-snakeoil
This is an elevated, state-changing command. It can create or replace /etc/ssl/certs/ssl-cert-snakeoil.pem and /etc/ssl/private/ssl-cert-snakeoil.key, and it adds a hash link beside the certificate. Do not run it on a production host purely to quiet a client warning.
It normally refuses to replace a usable existing certificate: if both standard files exist, it checks the signature and RSA key length first to decide whether regeneration is actually needed. --no-overwrite makes an existing pair a hard stop; --force-overwrite forces a new pair regardless.
Warning
--force-overwrite invalidates the old private key for everything already using it. Stop the dependent service first if its reload behaviour is unclear, and keep a tested config backup. Generation itself does not configure or reload a web server.
Checkpoint
Inspect the result without printing the private key:
$ sudo stat -c '%A %U:%G %n' \
/etc/ssl/certs/ssl-cert-snakeoil.pem \
/etc/ssl/private/ssl-cert-snakeoil.key
-rw-r--r-- root:root /etc/ssl/certs/ssl-cert-snakeoil.pem
-rw-r----- root:ssl-cert /etc/ssl/private/ssl-cert-snakeoil.key
$ sudo openssl x509 -in /etc/ssl/certs/ssl-cert-snakeoil.pem \
-noout -subject -issuer -dates -ext subjectAltName
subject=CN = your-hostname
issuer=CN = your-hostname
notBefore=...
notAfter=...
X509v3 Subject Alternative Name:
DNS:your-hostname
Subject and dates are host-specific. What matters is that the private key is not world-readable; the exact owner can vary with packaging, but the restrictive mode on the key is the real boundary.
3. Create a separate test certificate
Manual mode suits an application that needs a certificate at a temporary or app-specific path. It asks debconf for a hostname and optional alternative name, then writes certificate and key together to your chosen output:
$ install -d -m 700 ./tls-test
$ make-ssl-cert /usr/share/ssl-cert/ssleay.cnf ./tls-test/test.pem
The output path must not already exist, unless you deliberately pass --force-overwrite. The wrapper sets a restrictive umask and applies mode 600 to a manually generated output. Treat test.pem as a private-key file even though it also holds the certificate.
For a non-interactive run, make sure debconf already has the intended hostname and alternative name set. Do not assume the machine's own hostname is what clients actually use: a wrong name is a certificate-identity problem, not something a client retry will fix.
Checkpoint
Inspect only the public certificate through OpenSSL:
$ openssl x509 -in ./tls-test/test.pem -noout \
-subject -issuer -dates -ext subjectAltName
subject=CN = your-test-name
issuer=CN = your-test-name
notBefore=...
notAfter=...
X509v3 Subject Alternative Name:
DNS:your-test-name
If you need the hash link the wrapper normally creates, look in the output directory for a symlink whose name starts with the certificate's OpenSSL subject hash:
$ openssl x509 -hash -noout -in ./tls-test/test.pem
...hash...
$ find ./tls-test -maxdepth 1 -type l -printf '%f -> %l\n'
4. Do not trust --expiration-days blindly
The manual page documents --expiration-days N with a default of 3650 days. On the installed 1.1.2ubuntu1 wrapper, though, a local test with --expiration-days 30 produced a certificate that expired three days later, consistent with the wrapper passing the option parser's position instead of the requested value.
Warning
Never put an unverified lifetime into an automated deployment. Generate into a disposable path and check the resulting dates first:
$ umask 077
$ make-ssl-cert --expiration-days 30 \
/usr/share/ssl-cert/ssleay.cnf ./tls-test/expiry-test.pem
$ openssl x509 -in ./tls-test/expiry-test.pem -noout -dates
notBefore=...
notAfter=...
Compare notAfter with the lifetime you requested before installing the file. If it is wrong, do not use that certificate. Remove only the disposable test file with rm -- ./tls-test/expiry-test.pem, an irreversible step, then report or patch the package through your normal maintenance process.
5. Handle common failures
- Output file already exists: choose a new path or inspect the existing certificate first. Do not reach for
--force-overwriteon a production command without knowing which service owns the key. - debconf cannot ask its questions: run from a suitable terminal, or preseed the hostname and alternative name through your normal Debian configuration process.
- A client rejects the certificate: check the subject alternative name, validity dates and trust store separately. A self-signed certificate is expected to fail trust validation; installing it as a trusted CA is a separate security decision, not something to bury inside a generation step.
Avoid copying a certificate generated for one hostname onto another service: it can stay syntactically valid while still failing hostname verification.
Undo the manual example
Stop anything using the test file, then remove the disposable directory:
$ rm -rf -- ./tls-test
Recovery
Never run that removal against /etc/ssl. For the default snakeoil pair, restore the previous files from backup and reload the consuming service per its own documentation. Regenerating the pair is not an undo, it creates a new identity.
Done means
- You know which mode you used, manual or default snakeoil.
- Subject alternative name and dates match what you actually intended.
- The private key is not readable by anyone untrusted.
- You have not mistaken self-signed for publicly trusted.
- You verified --expiration-days behaviour yourself before relying on it.
- A backup or recovery path exists before replacing a certificate a service uses.