
Show HN: Mkcert – Valid HTTPS certificates for localhost - FiloSottile
https://blog.filippo.io/mkcert-valid-https-certificates-for-localhost/
======
regecks
I don't understand why you made the CA private key user-readable.

On my system, modifying the trust anchors requires root privileges.

mkcert undoes this boundary by opening a "shortcut" where any code on the
machine can mint trusted certificates for any domain (like internet banking).

Why not make the CA private key only readable as root, and the issuance of new
leaf certificates a privileged operation?

I would also expect that if/when you add ACME, that the CA key material is
locked away in a different user to the one in the DE.

Maybe not every platform you support actually has this boundary, but I would
expect that mkcert doesn't remove the boundary on platforms where it does
exist.

Fan of your work since your Heartbleed scanner!

~~~
FiloSottile
That is a fair argument, but it is already possible to get the behavior you
want: the root key is generated with permissions 0400, so if your first run of
mkcert is with root, all following invocations will also require root.

My reason for not making it a first class feature (with its complexity cost)
is that in the vast majority of cases, an attacker with the ability to read
local files can also directly attack the browser. (At least in mkcert’s use
case, which is development not deployment.)

Also, I don’t really see how to maintain that boundary once ACME gives
certificates to anything that can connect to a local port (without sending
headers suggesting it’s a tricked browser).

~~~
Natanael_L
How about instead adding an alert when not running it as root?

------
speleding
Do you really want a valid certificate for localhost?

I can get a valid certificate for development by simply getting a valid
certificate for localhost.example.com on my server through let's encrypt and
then making localhost.example.com resolve to 127.0.0.1 in my /etc/hosts file.

Some code can behave slightly differently on localhost than on
localhost.example.com, for example in deciding whether to keep cookies on a
third level domain, so tests are more reliable that way.

~~~
nickjj
If you don't care about HTTPS on localhost but still want a fully qualified
domain name you can simplify this approach even further.

Let's say you have an app running on localhost:8000 on your dev box.

If you goto lvh.me:8000, it will work without having to install or modify
anything on your dev box or modify your DNS records for another domain. If
your app had an "example" subdomain you could even goto example.lvh.me:8000.

I wrote about lvh and other useful free services that help developing apps
without needing to install a DNS server or mess with SSL certificates at
[https://nickjanetakis.com/blog/ngrok-lvhme-nipio-a-
trilogy-f...](https://nickjanetakis.com/blog/ngrok-lvhme-nipio-a-trilogy-for-
local-development-and-testing).

~~~
darrenf
Regarding lvh, see also:

    
    
        localho.st

------
CaliforniaKarl
I have one slight concern, and one snark.

The slight concern is, I'm concerned that most of the pre-built binaries (the
"pre-built binaries link" goes to
[https://github.com/FiloSottile/mkcert/releases](https://github.com/FiloSottile/mkcert/releases))
are not signed. There are only a few releases that are marked Verified. Since
this messes with the local trust store, it would be good to have things
signed.

The snark is directed at this sentence:

> If you are doing TLS certificates right in production, you are using Let's
> Encrypt via the ACME protocol.

I agree that Let's Encrypt is great, but I dislike the implication that, if
you're not using Let's Encrypt, you're not doing TLS right. Or maybe the text
was supposed to be read as…

> If you are doing Let's Encrypt TLS certificates right in production, you are
> using the ACME protocol.

~~~
FiloSottile
Current desktop OSes don’t offer fine grained permissions, so there is no
reason to hold software with specific features to a higher standard of
integrity. Still, I recommend you install anything you can, mkcert included,
from your package manager, as shown in the README.

As for the snark, I stand by my provocation: I believe it’s best practice to
automate TLS certificate renewal, and for 95% of deployments that means ACME,
which for 95% of cases currently (but not necessarily) means Let’s Encrypt.

~~~
nodesocket
AWS ACM is great, and they handle auto renewal for you as well. LetsEncrypt is
fantastic, but there are other options.

~~~
FiloSottile
Anything with automated renewal is great.

~~~
forty
With AWS being the biggest cloud vendor, it means that most people could
probably use ACM and still do things right (it's probably even better than LE
as it's harder to mess it up). So your 95% above is probably extremely
inaccurate :)

------
Sidnicious
It's worth noting that UAs can treat localhost, 127.0.0.1, etc. as secure even
when they're visited over plain HTTP:

[https://w3c.github.io/webappsec-secure-
contexts/#potentially...](https://w3c.github.io/webappsec-secure-
contexts/#potentially-trustworthy-origin)

Chrome (in the post's screenshots) follows this; I'm not sure about other UAs.

~~~
sthottingal
It is broken in Firefox since version 62
[https://bugzilla.mozilla.org/show_bug.cgi?id=903966](https://bugzilla.mozilla.org/show_bug.cgi?id=903966)

~~~
jwatt
That's an old, long closed bug. I think the bug you want is
[https://bugzilla.mozilla.org/show_bug.cgi?id=1488740](https://bugzilla.mozilla.org/show_bug.cgi?id=1488740)

------
anderspitman
Well this is timely. I spent several hours today trying to get local https
working. I needed it because iOS Safari will only let you make getUserMedia
(WebRTC) requests for the webcam over secure connections. I needed access to
the webcam because Safari WebRTC won't give you access to other browsers on
your LAN for data channels (which have nothing to do with your webcam) unless
the user gives permission to use the webcam[0]. I needed access to LAN data
channels because I'm working on a multiplayer game that I'd like to work on
the major browsers. I had given up and finally decided to just use my VPS with
Let's Encrypt for testing iOS, but maybe I'll give this a try. Worst case it
doesn't work. Now I just need to figure out how to convince my users that
taking a selfie is a reasonable requirement to play my game.

[0]
[https://github.com/webrtc/samples/issues/1123](https://github.com/webrtc/samples/issues/1123)

~~~
anandpdoshi
I have used localtunnel over https for this:
[https://localtunnel.github.io/www/](https://localtunnel.github.io/www/)

~~~
anderspitman
Nice, is this similar to ngrok?

~~~
masklinn
Same principle, though less well maintained and somewhat iffier especially the
server bits, you may want to / have to set up your own localtunnel server[0]
though an advantage is that you can do that.

FWIW for my use case (testing github API calls & webhooks feedback), ngrok
with a free account was more reliable, and interacted better with little
snitch.

[0]
[https://github.com/localtunnel/localtunnel/issues/261#issuec...](https://github.com/localtunnel/localtunnel/issues/261#issuecomment-407550521)

------
zamadatix
Private CA is a good route if you plan on setting up a long standing internal
only test server and devices. OTOH if you just want to access
[https://localhost](https://localhost) on your PC things like
"chrome://flags/#allow-insecure-localhost" exist and are easier to manage.

~~~
gruez
>OTOH if you just want to access [https://localhost](https://localhost) on
your PC things like "chrome://flags/#allow-insecure-localhost" exist and are
easier to manage.

is there anything similar for firefox?

------
buybackoff
For local web dev/testing most tools generate test temp certificates
automatically or it's easy to get them, so the only issue is using it in
production e.g. for communicating from web apps to local services.

The article links to Let's Encrypt post
([https://letsencrypt.org/docs/certificates-for-
localhost/](https://letsencrypt.org/docs/certificates-for-localhost/)) where
they mention that "modern browsers consider
“[http://127.0.0.1:8000/"](http://127.0.0.1:8000/") to be a “potentially
trustworthy”". Before that several years ago it was just impossible to
communicate from https to local http. Now most browsers (excluding Safari)
support communicating with local http from https pages. And there is a hostile
opposition from a Safari developer to support this, even though it is now a
standard.
([https://bugs.webkit.org/show_bug.cgi?id=171934](https://bugs.webkit.org/show_bug.cgi?id=171934))

Distributing an unconstrained CA root certificate and asking users to install
it is a terrible idea. Even generating one locally is dangerous if private key
is easily exportable. If one still needs to support local https, it's better
to limit the scope as much as possible. E.g. to issue a certificate only to
sub-sub domain and apply name constraints as @tedunangst mentioned. It's easy
to do this with XCA tool:

* XCA: [https://github.com/chris2511/xca/](https://github.com/chris2511/xca/)

* Name Constraint Wiki: [https://wiki.mozilla.org/CA:NameConstraints](https://wiki.mozilla.org/CA:NameConstraints)

* How to create CA with NC using XCA: [https://security.stackexchange.com/questions/31376/can-i-res...](https://security.stackexchange.com/questions/31376/can-i-restrict-a-certification-authority-to-signing-certain-domains-only/130674#130674)

* An example how valid/invalid certificate look on Windows [https://github.com/DataSpreads/CA](https://github.com/DataSpreads/CA)

(Name constraints are also not supported on MacOS currently)

------
aorth
Cool. I wish there was an option to specify which trust stores to install to,
for example _only_ to Firefox's. The current default is to install in system
(requires sudo), Chrome (which I don't use), and Firefox trust stores.

------
mode7
In my experience, you don't want HTTPS for localhost, you want HTTPS for a
server in the local network so that you can also test on other devices than
the development machine.

A convenient solution in case you can control DNS at the local network level
is to just get an ordinary certificate for a real domain (for example
dev.mydomain.com) and have it resolve to that local IP.

Using your own CA is possible too, of course, but getting it installed on all
the devices is a nuisance.

~~~
713233eb
Using the DNS challenge with LE, you can create a wildcard cert that is valid
for *.domain.tld. Now you need a simple local DNS server like PiHole to
resolve any local domains to your local reverse proxy serving local sites with
the wildcard cert and you're done. You only need internet connection on the
user's browser to get the little green lock and when you generate the wildcard
cert itself.

~~~
Gorgor
If you do it this way, you have to copy your actual server’s private key to
your local machine (or even _machines_ if you’re using several), though. That
possibly increases the chances of it getting compromised.

I’d prefer getting a separate certificate for local.domain.tld instead.

Please correct me if I have a misunderstanding.

------
hopeless
I've been using [https://www.tinycert.org/](https://www.tinycert.org/) for
years which basically lets you create your own certificate authority and issue
certs (which obviously aren't trusted by everyone but can be trusted by
you/your team). It's ideal for generating SSL certs for ephemeral apps e.g.
review apps on Heroku since it can all be done using an API

------
ricardobeat
Am I wrong to think this opens a gaping security hole?

Adding a new root CA means anyone who gets their hands on the keys (which are
openly available to the user of mkcert) gets to completely obliterate your
‘trust’ store and MITM any of your secure connections.

~~~
cpach
AFAIK attackers with local access already have lots of ways to exploit your
system.

However, for added security you could run mkcert on another computer that is
not connected to a network. Then you just copy the root cert and the leaf cert
to your dev machine, but leave the root cert’s private key offline.

------
j1elo
I've always worked around low-level C++ so never got the need to understand
how HTTPS certificates work... until I needed to test some code for WebRTC
applications.

This tool was really helpful, when you just want to test your app and don't
care at all about all those security restrictions imposed by the browser,
Mkcert helps so thank you a lot for this.

At the beginning it was a bit confusing because _some_ knowledge is assumed
about how all this stuff works. For complete strangers like me I ended up
asking for help, this includes some exact steps that one can follow to
generate and install a certificate, so it might be helpful for someone reading
this:

[https://github.com/FiloSottile/mkcert/issues/60](https://github.com/FiloSottile/mkcert/issues/60)

------
jypepin
This is great, will definitely check this out. I just struggled for hours
following multiple blog posts and howtos to setup https locally, because I was
trying to test Facebook login, which only allows HTTPS and can't be tested
otherwise.

Definitely a great product if it works as advertised!

~~~
new_guy
While the tool been showcased here is great, there's really no substitute for
doing it yourself and getting your hands dirty. Too many people (not saying
you) use these kinds of tools as a crutch instead of learning the basics
themselves.

This is a great guide on how to set up SSL on localhost
[https://deliciousbrains.com/ssl-certificate-authority-for-
lo...](https://deliciousbrains.com/ssl-certificate-authority-for-local-https-
development/)

~~~
jypepin
indeed it's good to learn how those things work (I did while doing a similar
process than your article) but a tool like this is definitely good to save
time.

One could make a similar comment about using cURL vs building your own HTTP
request, to learn the basics themselves :)

------
xPaw
Last time I setup a certificate for localhost, I was unable to get *.localhost
to work because wildcard "tld" certificates are rejected by the browser.

I wonder if browsers will allow this to work for localhost?

Here's the script I used to do this last time, just a couple of openssl
commands:
[https://gist.github.com/xPaw/840f378f3fed64806b46211c2287b52...](https://gist.github.com/xPaw/840f378f3fed64806b46211c2287b52c)

~~~
unixhero
It's not honoured by chrome, yes.

------
kureikain
I love this tool a lot. It's super helpful. I usually can get away with this
but some tool really require it.

For example, I implement google oauth flow recently and they have a flow of
redirect URL you have to pre-defined. To simplify the code and config, I want
to make HTTPS by default and the only option instead of checking for the
env(dev/prod to enable https or not). This simplify the code and still allow
me to test https with it.

------
iheartpotatoes
I needed this three years ago!

My solution was to use AWS and route 443 to 80 in a load balancer. That way
there IS NO MIXED content: it is all HTTPS. (And certificates are free on
AWS!)

If you don't like AWS, can't you just nginx as your load balancer and do the
same thing?

Why should a website care about what port traffic is coming in on, shouldn't
the TLS happen IN FRONT of your website so the website can be agnostic?

~~~
kabes
mkcert is just making it easier to generate & install self-signed
certificates... Nothing that couldn't be done before and using nginx as you
describe is pretty standard practice. I don't really see why you went for the
AWS solution.

~~~
iheartpotatoes
Ah, I crammed a bunch of threads in my terse answer. We use AWS because
elastic beanstalk works better for our environment than heroku. Plus with S3
integration (and Route53), we found it doesn't make sense to maintain the OS
and to keep everything on one cheap platform (cheap because it can be scaled
up/down very quickly). Using the LB to send everything to :80 on a VPC means
our devs don't need to dink with TLS on their remote laptops.

------
jimmychangas
So the tool can generate a CA cert and automate it's installation. In my
experience it can be painful to teach other developers to generate and install
their own, especially if the Java keystore is involved, so this could be very
useful for large teams.

------
TimTheTinker
I solved this problem by requesting a TLS certificate from my company's
Windows domain CA for my machine's hostnames on the internal network
(machinename and machinename.company.com - both added to the same cert via
Subject Alternative Name). Then I mapped those domains to 127.0.0.1 in
/etc/hosts so it works even when I'm not on the internal network.

From there it's simple to host the front-end app I work on using a node.js
script.

------
sonaltr
I'm almost 99% certain that everyone who's this concerned - already has a
domain. (or they can get a free domain from something like .tk - as it really
does not matter since this is for pure local development).

So I feel like the following workflow is simpler no?

1\. Use something like local.mydomain.com as your local dev domain. (set the
DNS in Cloudflare / Netlify etc. to 127.0.0.1)

2\. Use Let's encrypt to generate certs for that domain.

Am I going about this the wrong way? (or is there something super insecure
that I've missed?)

~~~
stephenr
For the cert part, LetsEncrypt specifically recommend against that:
[https://letsencrypt.org/docs/certificates-for-
localhost/](https://letsencrypt.org/docs/certificates-for-localhost/)

For the dns part, I honestly think a hosts file entry is more flexible, as you
can support environments using vms/containers etc with a guest that has a dhcp
address.

~~~
sonaltr
The security issue comes in when you ship the private key - if you are
following best practices - won't the private key be different for each domain
/ managed in a better way?

~~~
stephenr
So, now you're going to give each member of your team a way to authorise valid
certificates for your domain? Great, I don't want to imagine what your
HR/security vetting process will be after the first abuse of that power.

~~~
sonaltr
I had not thought about that...great points!

This is way simpler in that case!

------
gvx
That's neat! I'm writing a web framework for Python that has some built-in
features to make testing on localhost easier: you can pass it the file name to
an existing certificate or it will generate one itself. The disadvantage to
the no-setup required autogeneration is that you need to add a security
exception on first use and every time the root expires. `mkcert` could
definitely help with not training web developers to perform unsafe actions.

------
tedunangst
It would be nice if the root could be generated with name constraints so
losing it isn't quite so catastrophic.

------
kmarc
Cool stuff, thanks.

I guess I am not the only one having around a `genssl.sh` that grew big during
the years, using openssl, generating CA, certreq, then signing and setting
properly `subjectAltName` etc :-D

I like mkcert since it also injects the root CA into the trust store - was
always too lazy to do this programmatically.

~~~
sametmax
If you have a commented version of this script (actually even an uncommented
one), I would love to be able to read it.

~~~
kmarc
Quick&Dirty (just replaced company details):

[https://gist.github.com/kmARC/458a2afbd55635eef3b5f911359ce5...](https://gist.github.com/kmARC/458a2afbd55635eef3b5f911359ce5e0)

Also note that this doesn't have intermediate CA, root CA signs everything.
And of course: _NEVER use this even close to production._ (as you can see,
even I only used in my sandbox env)

~~~
sametmax
Thanks !

------
mic159
What's wrong with Lets Encrypt and DNS challenge? Then you get a globally
recognised cert.

~~~
MehdiHK
You cannot use them for localhost

~~~
jimdigriz
Seems a lot easier to register your own domain (useful for so many other
things), point localhost.mydomain.com to 127.0.0.1 and run:

    
    
        sudo apt-get -yy install --no-install-recommends certbot
        mkdir -p le
        certbot --work-dir ./le --logs-dir le --config-dir le -d localhost.mydomain.com --manual --preferred-challenges dns certonly
    

Follow the instructions adding the TXT record, you have a valid cert in
minutes.

------
kzahel
Does anyone know if there's a publicly available domain that does RFC1918
resolutions? Such as

    
    
      192-168-86-30.localresolve.com has A record 192.168.86.30
      10-10-10-1.localresolve.com has A record 10.10.10.1
    

and so on.

~~~
lioeters
Might be close:

[http://lvh.me](http://lvh.me) is a free service that resolves itself along
with all subdomains to localhost.

Edit: [http://nip.io/](http://nip.io/) looks like it does exactly what you
asked.

------
groovypuppy
YES! Thank you. As an indie PWA developer this is exactly what I've been
looking for.

------
alexandernst
Will it work inside a Docker container and my browser in my bare metal?

~~~
FiloSottile
As long as you run "mkcert -install" where the client/browser runs, you can
use the certificates it generates in whatever deployment you like. If instead
you want to run it 100% inside Docker, but your browser is outside, you'll
have to manually install the root from "mkcert -CAROOT".

------
CoryG89
I've wondered if there was a way to do this forever. Was just trying to come
up with a good solution for trusting my self-signed certs on my machines.

------
jpkeisala
Can this be used for host hacked domains? I tend to use host hack where www is
replaced with dev on my localhost like "dev.somedomain.xx"

------
exabrial
If you need to keep a database of certs, checkout XCA.

------
simonsaidit
i'm doing something like this in bash for hosts i don't own the certificate
for the domain and using own created root CA and adding to java keystore.

[https://pastebin.com/ChNiHbYL](https://pastebin.com/ChNiHbYL)

------
joshwcomeau
Awesome! This looks like it'll solve a real pain point. Thanks for sharing :)

------
hartator
Awesome. Thanks a lot.

------
psonama
what is the benefit of having a certificate in localhost? I don't really see
the point if you are on your local machine

