[HN Gopher] Lcl.host: fast, easy HTTPS in your local dev environ...
       ___________________________________________________________________
        
       Lcl.host: fast, easy HTTPS in your local dev environment
        
       Author : todsacerdoti
       Score  : 161 points
       Date   : 2024-03-20 12:42 UTC (10 hours ago)
        
 (HTM) web link (anchor.dev)
 (TXT) w3m dump (anchor.dev)
        
       | peter_l_downs wrote:
       | I like the interactive setup. I think this is solid but if you
       | want something even faster and easier to use, try my project
       | localias [0]. The parent project, lcl.host, has some annoying
       | restrictions:
       | 
       | > This CA has some restrictions though: it can only issue
       | certificates for subdomains of lcl.host and localhost, but that's
       | all you need for local development.
       | 
       | Localias, on the other hand, lets you use any custom domain you'd
       | like. And if you use a domain ending in .local, it will broadcast
       | over mDNS so that you can easily connect to that server from any
       | other device on your wifi network (like your phone.)
       | 
       | Localias also allows you to share your configuration with your
       | entire development team by committing a .localias.yaml file to
       | the root of your git repo. This makes sharing links with each
       | other super convenient.
       | 
       | Always nice to see another competitor in the space; if you're
       | interested in this, please check out Localias as well!
       | 
       | [0] https://github.com/peterldowns/localias
        
         | mholt wrote:
         | Neat! I hadn't seen this before, and it uses Caddy :D
        
           | peter_l_downs wrote:
           | Yes! Thank you for making Caddy, it's wonderful software and
           | it was really easy to extend!
        
         | elzbardico wrote:
         | Oh man, I was up and running in my project in less than one
         | minute. Thanks!
        
           | peter_l_downs wrote:
           | I'm glad it worked for you and was easy to set up. If you run
           | into any trouble or have any feature requests, please file an
           | issue on github!
        
         | gvkhna wrote:
         | This looks fantastic! Does it support node extra ca cert etc?
         | I've had that issue with mkcert in the past and it's easy to
         | fix but another thing to keep track of in these already complex
         | dev setups if you're doing local https.
        
           | peter_l_downs wrote:
           | I don't understand what you mean, what "node extra ca cert
           | etc" and what is the issue with mkcert?
           | 
           | Localias wraps Caddy to handle all the cert provision; I
           | believe Caddy uses mkcert. I haven't seen any bug reports
           | about yet, but if you give it a try and run into an issue I
           | would be happy to help fix it.
        
         | globular-toast wrote:
         | This would be perfect if combined with Traefik's method of
         | config via docker tags.
        
       | dspillett wrote:
       | It surprises me how few people dev/test against HTTPS, given that
       | it isn't exactly hard to setup manually (with tools like this
       | making it even easier). Just point a wildcard DNS entry at
       | 127.0.0.1 or some other useful address if your dev copy is
       | actually not that local, and chuck a web server there acting as a
       | proxy to what-ever apps, with a LetsEncrypt wildcard cert. It
       | isn't zero work, but saves time in the long run as soon as you
       | hit unexpected issues caused by small differences between dev and
       | prod.
        
         | zikduruqe wrote:
         | You don't even need to mess with a wildcard from Lets Encrypt,
         | just use https://github.com/FiloSottile/mkcert
        
           | dspillett wrote:
           | I've always been a bit wary of a local trusted CA, especially
           | with the signing cert on the same dev box as the certificates
           | it signs which is how I've seen things done a lot. It feels
           | like opening up a trust issue that could allow an
           | uncooperative entity to play games with me... Maybe that is
           | just paranoia from the practical jokes played back in CompSci
           | at Uni!
           | 
           | Admittedly an external attacker getting close enough to sign
           | a cert using such a CA, in order to trick me into something,
           | means they probably have such high access already that they
           | don't really need the CA to do that or worse, so perhaps it
           | is unnecessary caution.
        
             | nucleardog wrote:
             | The way I handle this in the dev tooling I put together is
             | to run a totally separate browser profile that (1) trusts
             | the certificate and (2) can only connect to localhost. It
             | also launches with a totally different colour scheme.
             | 
             | With chrome, that's something like:
             | google-chrome             --user-data-
             | dir="${HOME}/.config/ourlocaldev/google-chrome"
             | --install-autogenerated-theme=85,63,9             --host-
             | rules="MAP * 127.0.0.1, EXCLUDE localhost, EXCLUDE
             | fonts.googleapis.com, EXCLUDE fonts.gstatic.com"
             | --ignore-certificate-errors-spki-
             | list="0oKw9nasIS7qRQD1CYXe5bmi22/mnHjZP++f6G+VM88="
             | "https://my.dev.thing"
             | 
             | This will launch a separate copy of chrome with a
             | fresh/separate profile. It will have a different colour
             | (RGB values set in there) so it's visually distinct when
             | it's running and I don't get the windows mixed up. Any
             | request made from the browser will be rewritten to connect
             | to 127.0.0.1 (except a couple google font domains).
             | 
             | The danger if someone got their hands on my local key/cert
             | is basically nil. They would only be able to MITM
             | connections from this one specific browser window to
             | localhost. And that browser is incapable of connecting to
             | anything besides localhost. I can never accidentally open
             | my banking site in there. Also fresh profile so no saved
             | passwords, credit cards, or anything else.
             | 
             | (As an added benefit, I don't really need to worry about
             | reconfiguring URLs for projects. If I open
             | "testing.mysite.com" in that browser, it will force the
             | connection to localhost, so I can just run my services at
             | our test URLs and steal configs as-is from the testing
             | environment. Taking it further, I then have a controller
             | set up in k3s/Rancher Desktop that rewrites the service on
             | all annotated ingresses to point to its own service, which
             | runs nginx, which the controller then configures to proxy
             | the requests on to the local service or the actual upstream
             | testing service depending on whether the local container is
             | running. It also configures CoreDNS to point the upstream
             | URLs at the same proxy. End result is that from the browser
             | or anything running in k3s you can hit our testing URLs and
             | it will hit your local container if it's running or fall
             | back to the testing environment if not.)
             | 
             | If you want to try the browser thing, you can generate the
             | fingerprint for a certificate with:                   echo
             | "" |         openssl s_client -connect 127.0.0.1:443
             | -prexit 2>/dev/null |         openssl x509 -pubkey -noout
             | -in /dev/stdin |         openssl pkey -pubin -outform der |
             | openssl dgst -sha256 -binary |         openssl enc -base64
        
               | djbusby wrote:
               | This is an awesome trick, thanks, I'm copying you.
        
         | afavour wrote:
         | > It surprises me how few people dev/test against HTTPS
         | 
         | For dev at least it's mostly because web browsers treat
         | localhost as a special domain that gets the HTTPS treatment
         | even when loaded over HTTP.
         | 
         | I have set up local HTTPS certs before now, can't remember
         | exactly what required it. But I still load most web projects on
         | localhost over HTTP just out of habit.
        
           | hk1337 wrote:
           | I've always thought the need for local HTTPS was unnecessary
           | for at least 90% of the projects and brought no real value
           | other than making the developer "feel" better.
           | 
           | localhost with HTTP should be sufficient for most things.
           | It's when you start doing stuff like "app.localhost" like
           | what was mentioned in the article which, despite it having
           | "localhost" is not the same as http://localhost.
           | 
           | Develop basic in local and do all the hostname + https on
           | actual server where you can easily do something like
           | letsencrypt route DNS to it.
        
             | chuckadams wrote:
             | There are a number of APIs, such as geolocation, that only
             | work over https. It's also very common to accidentally
             | generate non-https urls for assets, which is a bug most
             | people would rather catch in local dev and not production
             | when the browser refuses to fetch them.
             | 
             | I use traefik to serve my apps in dev, which generates
             | self-signed certs by default if you don't hook it up to
             | ACME. Slightly more cumbersome because it means clicking
             | through a warning screen and disabling verification in CLI
             | tools (actually those are where I do drop to plain http).
             | I'm sure this lcl.host product smooths this process
             | considerably, but when it comes to local dev tools, I have
             | an absolute requirement that they actually run locally and
             | don't tether me to some cloud service, so I'll be sticking
             | with traefik regardless for now.
        
               | afavour wrote:
               | > There are a number of APIs, such as geolocation, that
               | only work over https
               | 
               | And localhost
               | 
               | https://developer.mozilla.org/en-
               | US/docs/Web/Security/Secure...
        
               | ifwinterco wrote:
               | Might have changed, but last time I tried to use the
               | Clipboard API it didn't work on localhost without https
        
               | umvi wrote:
               | I would be very surprised if that were the case.
               | Localhost gets special privileges in most browsers
        
         | knallfrosch wrote:
         | It _is_ hard to set up. Creating a cert and trusting it is easy
         | - a Powershell script can do that in 3 lines.
         | 
         | But then, trouble begins. You have to configure every server to
         | use the certificate (or use a proxy) and every client to accept
         | the certificate. Sometimes the proxies eat headers, or they
         | have trouble with WebSockets and hot reloading or whatever.
         | 
         | We also use IPs instead of domains to find our locally running
         | prod servers so that our customers don't have to configure
         | anything DNS on their maybe-offline WiFi network. We also have
         | to test with external devices, such as iPads. How do you
         | automate getting your certificate onto them?
         | 
         | ..And then your IP changes and all is lost again.
         | 
         | Yeah, if you have a 50 line Svelte "app" that you serve as site
         | it's easy. But who does that?
        
         | holoduke wrote:
         | I never work locally. Always remote. Dev.xxx.com, stg.xxx.com
         | etc
        
       | BroomOfSYS wrote:
       | How is this different from something like Caddy which supports
       | https://localhost instead of something like https://lcl.host
        
         | its_stolt wrote:
         | localhost gives you a different security context in your
         | browser than using a full domain name. Typically to use a full
         | domain name locally you'd either need to mess with /etc/hosts
         | (which can bite you later) or mess with DNS. lcl.host just
         | makes things work out of the box.
        
           | its_stolt wrote:
           | Let me add a bit of clarification here, since you
           | specifically asked about https://localhost
           | 
           | Using http with localhost is a mostly security context, but
           | it has some quirks[0]. Using https://localhost will give you
           | the full security context but then you're typically stuck
           | either dealing with certs manually or using a proxy, like
           | caddy. lcl.host should simplify your setup.
           | 
           | We're big fans of caddy by the way and even sponsor the
           | project. Matt's doing amazing work over there.
           | 
           | [0] https://web.dev/articles/when-to-use-local-https
        
             | mholt wrote:
             | Thanks! That reminds me we need to get your logo on our new
             | homepage. I'll shoot you an email.
        
       | blopker wrote:
       | I don't see OrbStack mentioned here much, but it's completely
       | replaced Docker Desktop for me. Aside from having a better UI,
       | faster and uses less battery, it also gives local https with
       | custom domains for free [0].
       | 
       | [0] https://docs.orbstack.dev/features/https
        
         | avtar wrote:
         | If you don't need a GUI, the following combo works pretty well:
         | 
         | - https://github.com/abiosoft/colima
         | 
         | - https://github.com/peterldowns/localias
        
         | ensignavenger wrote:
         | It looks nice, but it is MacOS only, and it is really expensive
         | at $96 per year! Probably why it doesn't get mentioned often.
        
       | mholt wrote:
       | I want to point out that Anchor.dev sponsors the Caddy project
       | and we're very grateful for that! Anchor devs have also made code
       | contributions to Caddy and CertMagic.
       | 
       | Anchor has a neat product. They're making internal TLS more
       | accessible to more developers who don't necessarily need a
       | separate web server. They're doing local TLS just about as best
       | as possible from what I can see.
       | 
       | IMO this has more utility than mkcert -- which is a great tool by
       | Filippo and Caddy shares some of its underlying library for its
       | own auto-trusted internal CA -- because, like Caddy, Anchor fully
       | automates the certificates instead of just generating them and
       | needing a cron job. It's more hands-off and higher-level,
       | allowing you to get more done with less effort.
        
       | JimDabell wrote:
       | Warning: This checks if it's running the latest version and
       | refuses to run if it isn't up to date. They just released v0.0.16
       | 15 minutes ago and the update hasn't hit Homebrew yet, so it has
       | completely disabled itself and won't run. There doesn't seem to
       | be any option to skip the version check.
       | 
       | So don't use this unless you don't mind it breaking randomly
       | whenever there's an update.
        
         | mholt wrote:
         | When I first announced Caddy, our website downloaded everything
         | as .gz due to high traffic load -- a lesson I learned very
         | quickly and a mistake I never made again.
         | 
         | This probably falls in the same boat. :)
        
           | emi2k01 wrote:
           | Sorry, maybe it's too early in the day but I don't get what
           | the lesson was. Could you explain?
        
             | orliesaurus wrote:
             | (IMHO) the lesson is that sometimes you're excited to tell
             | the world about what you built, that you forget about some
             | "other stuff"
             | 
             | i.e. I don't think that the authors of this tool wanted to
             | have developers be forced to use the latest version of
             | their tool.
             | 
             | It's probably that they didn't think about it when they
             | wrote the code, but now they know and hopefully this gets
             | fixed in the next release?
        
               | mholt wrote:
               | Yeah, exactly. A lot goes into shipping, and sometimes
               | things like this can be overlooked, especially on a small
               | team that needs to deliver broad platform support right
               | out of the gate.
               | 
               | There are some bugs that are just hard to find until
               | they're out there.
        
               | hackmiester wrote:
               | I just don't understand what the thing about "downloading
               | everything as .gz" means. It's not like a gz is a rare
               | file format, it seems like a totally reasonable format to
               | download something in.
        
               | mholt wrote:
               | To clarify, every page load (.html file) was downloaded
               | as a .gz file instead of being served as an HTML file and
               | displayed as a web page in the browser.
        
               | wolfgang42 wrote:
               | Having once made a similar mistake myself, I assume what
               | they mean is that it was downloading _everything_ as a
               | .gz--that is, the browser was asking users "where would
               | you like to save index.html.gz?" instead of showing the
               | homepage. (This happens when you precompress a static
               | site for performance, but forget to tell the server that
               | gzip should be negotiated as a Content-Encoding instead
               | of a Content-Type.)
        
         | jollyllama wrote:
         | Gross. Thanks for the heads up!
        
           | apgwoz wrote:
           | Not affiliated with them, but it seems this was a mistake.
           | https://news.ycombinator.com/item?id=39768685 -- maybe give
           | it another shot?
        
           | pmarreck wrote:
           | They fixed this. It's now a warning.
        
         | benburkert wrote:
         | Sorry about that, we're working on switching this to a warning
         | and not an error, that slipped by us before release. After the
         | next update, it will only show a warning if you're not on the
         | latest release.
        
           | benburkert wrote:
           | We just released a fix for the version error, this will be
           | the last one you see, we promise!
        
         | hipadev23 wrote:
         | That's an absurd tactic I've not seen since last time I used
         | Firefox.
        
           | yoavm wrote:
           | you're completely free to run firefox from 5 years ago and it
           | will not refuse to run.
        
       | robertclaus wrote:
       | In my experience a lot of developers ignore the concept of
       | dev/prod parity like this because a different team (ex.
       | Deployment) ends up dealing with the integration issues it
       | causes. That being said, its also traditionally a balance between
       | effort and output - so I can see a "make it easier" tool like
       | this helping lower the barrier to actually get devs to use it.
        
       | k__ wrote:
       | Congratulations on shipping!
       | 
       | Just yesterday, I needed it for a hackathon, but had to switch to
       | a cloud IDE instead.
        
       | Wingy wrote:
       | Why modify the trust stores with the personal CA? Is there any
       | risk to just publishing a globally valid wildcard cert for
       | *.lcl.host, since it always resolves to 127.0.0.1 anyway?
        
         | benburkert wrote:
         | We install the CA certificates into the trust stores so that
         | the certificates are trusted by your browsers and clients,
         | otherwise they will (rightfully!) get connection errors. We
         | also set the CAA records for all lcl.host subdomains to
         | anchor.dev, so no public CA will issue certificates for
         | *.lcl.host. The only valid certs for lcl.host subdomains you
         | will encounter are for your account's CAs. If we gave everyone
         | a cert+key for *.lcl.host, besides the security concerns, we'd
         | have to keep redistributing them every ~45 days, but with
         | lcl.host you can setup ACME to automatically renew certs before
         | they expire.
        
         | 8organicbits wrote:
         | Some sites have tried this before, but I dont think they stay
         | online long. The certificates are "leaked" when they are
         | shared, so the CA will revoke them.
         | 
         | I think a better approach is to get a domain name and a Let's
         | Encrypt certificate. There's lots of tooling for this, and it
         | matches production. I built https://www.getlocalcert.net/ to
         | act as free, Let's Encrypt compatible subdomain service
         | specifically for these sorts of challenges.
        
           | extraduder_ire wrote:
           | Cool. I was hoping something like this existed, and glad to
           | see you got it into the public suffix list. I'd been
           | considering doing something like it for some time.
        
       | felixhummel wrote:
       | We built something similar for docker-compose based projects:
       | https://gitlab.com/hukudo/ingress
        
       | hartator wrote:
       | Were some bugs that show only in the https version of the app?
        
         | its_stolt wrote:
         | These tend to manifest themselves when you have mixed content
         | on the page or you're using CORS. For CORS the issues can
         | surface due to running different rules for dev vs prod/stg.
        
       | binwiederhier wrote:
       | I've used this service in the past, http://local-ip.co/ which
       | provides downloadable private key and certs and they run a dns
       | service that resolves to any IP address.
        
       | nickphx wrote:
       | "Try for free", no mention of pricing anywhere.. I'll stick to
       | what I'm already doing that's free.. :D
        
       | WesolyKubeczek wrote:
       | I've just got me a domain and am using letsencrypt. The hostnames
       | resolve to LAN addresses. Still unsure what I'm doing wrong.
        
       | zachrip wrote:
       | Off topic but I find this font for the body very hard to read.
        
       | redder23 wrote:
       | Can you not use Letsencrypt locally? I think I actually saw
       | something about that recently here on HN.
       | 
       | What is the difference between this and LE if true?
       | 
       | I would like to use a .test domain. I use that currently and just
       | click "proceed anyway" whenever the warning pops up and It's
       | pretty often lately.
        
       | jbverschoor wrote:
       | Just use orbstack, it gives you https and hostnames.
       | 
       | Shameless plug: https://github.com/jrz/container-shell in
       | combination with orbstack. Isolated dev environment, easy to use,
       | local tools, https on https://xxxxx.orb
        
       | d_meeze wrote:
       | I'm skimming the docs looking for how "in containers" is handled
       | and so far I can only see a one liner in the release notes. One
       | problem in local environments I keep having and building tricks
       | for is for services inside containers getting certs for other
       | containers. From within the container, resolving to 127.0.0.1
       | isn't helpful as that's the internal loopback not the host.
        
         | benburkert wrote:
         | we're going to say more about how lcl.host works between
         | containers in the future since it ends up pulling in Anchor's
         | package features, but I can give a quick rundown of what we've
         | done in the past with docker-compose: start a service in
         | container A and expose port 44300, and configure the service
         | with an ACME client to provision a `service-a.lcl.host`
         | certificate. The clients in that container won't trust the
         | cert, but that no problem, since your system/browser will trust
         | the cert if you've run `anchor lcl`. In container B, install an
         | anchor built package for the language of the server, and setup
         | the HTTPS/TLS client to use the set of CAs in that package. Now
         | app B can connect to `service-a.lcl.host:443300` over
         | HTTPS/TLS.
        
           | d_meeze wrote:
           | "Clients in that container won't trust the cert". Yeah,
           | there's the trick.
           | 
           | "service-a.lcl.host:443300" so when inside the container,
           | won't that resolve to 127.0.0.1 which is the container
           | internal loopback interface not the docker host's interface?
           | Hence trying to connect to itself not its sibling.
        
             | benburkert wrote:
             | right it's the loopback, but I believe docker-compose can
             | forward loopback ports to the host (and then back into the
             | other container) using links, but i'm fuzzy on the details
             | and may be misremembering.
        
       | jpambrun wrote:
       | I have another personal solution [0]. It's a DNS server that also
       | gets a wildcard certificate and make it available with a secret.
       | This is definitely in the convenience over security relm, but it
       | resolve any pattern prefix-123-123-123-123-suffix.example.com to
       | the enclosed ip (e.g. 123.123.123.123). It will resolve to
       | 127.0.0.1 or any other ip happily. Now you just need to use the
       | associated cert and enjoy https. Works great from with k8s
       | ingress, caddy, node.. You don't have to fiddle with your trusted
       | store and it works for everybody. I took inspiration from
       | https://nip.io/ for the dns part.
       | 
       | [0] https://github.com/jpambrun/dnsssl
        
       | himhckr wrote:
       | I'm on Windows 11 and not sure if it is just me but your font is
       | too hard to read. I thought it was just the blog but no, it's
       | everywhere even on the landing page.
        
       | jensenbox wrote:
       | How do I get this working if we are 100% docker?
        
       | austin-cheney wrote:
       | Some things I learned about trusted localhost HTTPS:
       | 
       | * Windows is the easiest... by far. There is only one trust store
       | and its extremely easy to access at different levels of trust.
       | Firefox has its own trust store so you can either add your certs
       | to both the Windows store AND the Firefox trust store or flip a
       | config in Firefox to tell it to use the Windows trust store like
       | everyone else.
       | 
       | * Linux is a challenge because you have to add your certificates
       | to the OS trust store and then each browser has their own trust
       | stores.
       | 
       | * MacOS is pretty close to impossible, at least fully automated.
       | If the cert is not registered with a third party of the OS's
       | choosing the cert will not be trusted in the browser. The way
       | around this is to manually add your localhost cert chain to the
       | MacOS keychain.
       | 
       | If anybody wants an example here is something I wrote a ways back
       | in JS (but please be warned its specific to my application:
       | 
       | * Build the certificate chain -
       | https://github.com/prettydiff/share-file-systems/blob/master...
       | 
       | * Install the cert by OS type -
       | https://github.com/prettydiff/share-file-systems/blob/master...
       | 
       | That second sample also installs pcap so that I can serve on
       | localhost over ports 80/443.
        
       | jokull wrote:
       | I just use cloudflare tunnels (cloudflared) - don't have to
       | install any certificates, it's all handled by cloudflare. Yes, it
       | exposes globally, but that's often convenient to share a link to
       | my dev with colleagues. And it has been fast enough. Downside is
       | that you need internet connectivity.
        
       | mderazon wrote:
       | Any reason why these tools usually don't support windows ? Is it
       | harder on Windows ?
        
       ___________________________________________________________________
       (page generated 2024-03-20 23:01 UTC)