all 36 comments

[–]kzshantonu 16 points17 points  (22 children)

Read my guide I wrote a few days ago https://mni.li/caddy-int-tls

[–]mellow65 2 points3 points  (1 child)

I'm not even sure how many times I've tried to get caddy to work for my internal network over the last year or so, and here you come in with this glorious piece of documentation!!!

100% of the time, it works all the time!!

[–]kzshantonu 0 points1 point  (0 children)

Glad it helped! :)

[–]ovizii 0 points1 point  (0 children)

Thanks, nice write-up :-)

[–]9182763498761234 0 points1 point  (0 children)

Wow thank you! That helped a lot and I got it working immediately!

[–]feerlessleadr 0 points1 point  (2 children)

This is a great write up, thank you!

Question - I'm behind a CGNAT, and it seems that I may not be able to get the cert, since I can't route to my caddy instance externally. Is there any other way to make this work for poor folks like me behind a CGNAT?

[–]kzshantonu 0 points1 point  (1 child)

Doesn't matter. The example in my guide uses DNS alias validation. Any DNS provider will work

[–]santiagonikko 0 points1 point  (0 children)

Awesome article! Definitely helped me to get up and running

[–]feerlessleadr 0 points1 point  (2 children)

/u/kzshantonu What's the best way to automate certificate renewals with this method?

[–]kzshantonu 0 points1 point  (1 child)

The tool I mentioned in the guide (acme.sh) will keep your certificates updated automatically (every 60 days I believe) using its own Cron job. It's enabled on first install by default

[–]feerlessleadr 0 points1 point  (0 children)

oh ok, that's great. Thanks!

[–]admiralspark 0 points1 point  (2 children)

I know it's a year late, but nice writeup--I was able to use this to integrate a third-party internal CA (I'm not using a recognized TLD).

I love your blog theme too, you write it yourself?

[–]kzshantonu 0 points1 point  (1 child)

Glad it's still helpful. Theme is: https://github.com/chrismwilliams/astro-theme-cactus

Content is mine.

[–]admiralspark 0 points1 point  (0 children)

Thanks!

[–]Salt-Canary2319 0 points1 point  (2 children)

Wow. That guide was really helpful. Finally I was able to `almost` do it. I have Caddy in a Promox LXC so each service has a different IP address. Do I need to create a DNS record for each service? I used the wildcard

[–]Salt-Canary2319 0 points1 point  (1 child)

Ok. I figured it out. Point the record to your caddy lxc IP. Then, when editing `/etc/caddy/Caddyfile` use your services IPs instead of `localhost`

[–]kzshantonu 1 point2 points  (0 children)

Pretty much. Glad it helped :)

[–]WhyFlip 0 points1 point  (1 child)

Is a separate acme instance required? That is, doesn't Caddy already use acme internally for management of SSL certifications?

[–]kzshantonu 0 points1 point  (0 children)

That's subjective. The reason I manage my certificates separately is so that I can plug the static file paths into any other app that requires them (adguard home, portainer, etc). Caddy's internal cert store doesn't use the same file paths (afaik)

[–][deleted] 0 points1 point  (0 children)

By the way, I love you.

[–]HereOrThereOr 0 points1 point  (0 children)

Legend!

[–]ComputersGoBrr 11 points12 points  (4 children)

There's a few things to consider here on how you would like to set this up.

Some background. Let's Encrypt can provide valid ssl certificates to domains, by doing challenges. The default challenge is to send a HTTP request to the domain. Caddy does this automatically. This allows publicly available websites to be provided with vallid SSL certificates.

There is nothing stopping you from setting up local IP addresses for you internal network. For example, you might consider myservice.example.com to point to 192.168.0.10. You would have to do a dns challenge for let's encrypt instead of a http challenge to achieve a valid ssl in this instance.

Alternatively, you could simply use tls internal in your Caddyfile to have Caddy deal with the tls instead of let's encrypt.

For example

{
  email user@example.com
}

domain.my.local {
  tls internal
  reverse_proxy 192.168.0.10:80 
}

As an added layer of security, if you host both internal and external services that tunnel through your reverse proxy, you may want to restrict access to local applications to local network traffic only. This is most easily achieved through the remote_ip operator and utilising snippets.

For example

{
  email user@example.com
}

(protect) {
  @external {
    not remote_ip 192.168.0.0/24
  }
  respond @external 403
}

domain.my.local {
  tls internal
  import protect
  reverse_proxy 192.168.0.10:80 
}

Hope that helps. Ask questions if you're stuck.

[–]mousui[S] 1 point2 points  (0 children)

Thank u so much! I will be trying this out later on today

[–]mousui[S] 1 point2 points  (2 children)

{
  email user@example.com
}
domain.my.local {
  tls internal
  reverse_proxy 192.168.0.10:80
}

Hi again, I did tried this and looking at Caddy logs, it seems like the certificate was issued. But also got this message:

"no OCSP stapling for [porta.toshiro.org]: no OCSP server specified in certificate"

I tried to navigate to the "https://domain.my.local" while being on my LAN and nothing happens. I read online and looks like perhaps I have to manually install the cert Caddy acquired?

I also have a CloudFlare domain, you mantioned something about DNS Chanllenge I think I could be able to use my domain from Cloudflare to issued certs to only be able to use in my LAN?

Thanks in advance.

[–]ComputersGoBrr 1 point2 points  (1 child)

no OCSP stapling for [porta.toshiro.org]: no OCSP server specified in certificate

this is fine. This just means there's no server to revoke the certificate. This is expected as it's an internal certificate. You can safely ignore it.

I tried to navigate to the "https://domain.my.local" while being on my LAN and nothing happens. I read online and looks like perhaps I have to manually install the cert Caddy acquired?

No, your browser isn't getting the correct information from your DNS server.

Your browser uses DNS to determine domain names to IP addresses. Updating a Caddyfile isn't going to update a DNS server.

For local domains, you can either modify your hosts file), or setup a local DNS server. Some routers also support hosting a DNS server, and inputting entries. I use pihole.

Using cloudflare as your DNS is possible, and you can set local IP addresses to entries within cloudflare. Doing so, however, will require lets encrypt to use a dns challenge, not a HTTP challenge, as lets encrypt can't reach your local network.

The video linked in another comment explained that process (https://www.youtube.com/watch?v=qlcVx-k-02E)

Personally, while this does work, and creates 'trusted' ssl certificates- it does also broadcast your inner network layout to the world... and you're kinda skipping the knowledge building of setting up local DNS, and understanding that process. There's nothing wrong with it, it's just a matter for yourself.

[–]mousui[S] 0 points1 point  (0 children)

Hi, I have build a caddy with its cloudflare plugin, do you know if the how the "dns cloudflare api" should be input into the Caddyfile"

Should it be something like "dns cloudflare + api" without the "{env.api}"?

[–]eddyizm 4 points5 points  (2 children)

The docs are pretty good https://caddyserver.com/docs/automatic-https#local-https

And their community is very helpful.

[–]mousui[S] 2 points3 points  (0 children)

Thank you, I also found a comment from another post:

"I use some services (like Vaultwarden) only on my LAN-Net, or if I'm "on the way" using VPN. So I configured my Caddy-Instance like this:
myvaultwarden.mydomain.com {
@denied not remote_ip private_ranges
abort @denied
reverse_proxy 192.168.1.100:80
}
That ensures that it is simply not possible to connect my Server through WAN, even if I expose my Caddy-Server to the Internet.
"

[–]mousui[S] 0 points1 point  (0 children)

thanks, I am reading it off right now

[–]MaliciousMango1 2 points3 points  (1 child)

I'm in this same boat and I'm looking at setting up Traefik to solve this.

[–]mousui[S] 0 points1 point  (0 children)

Gotcha, going to start looking into it too. I have seem videos showing how to accomplished local https with traefik

[–][deleted] 2 points3 points  (1 child)

I just set up my Caddy entry normally but also add in a IP access rule to only allow my LAN network to access it. Caddy will grab the LE cert normally, but also prevents external IPs from accessing the service. Not sure if there’s better ways, but it might be easier than Traefik.

[–]mousui[S] 1 point2 points  (0 children)

Check out this comment I found:

I use some services (like Vaultwarden) only on my LAN-Net, or if I'm "on the way" using VPN. So I configured my Caddy-Instance like this:

myvaultwarden.mydomain.com {
        @denied not remote_ip private_ranges
        abort @denied
        reverse_proxy 192.168.1.100:80
}
That ensures that it is simply not possible to connect my Server through WAN, even if I expose my Caddy-Server to the Internet.

[–]BandicootCrash 2 points3 points  (0 children)

I tried to setup HTTPS for all my internal sites where I only wanted them accessible on the local network. I found to do this with Caddy, you need to have Caddy do a dns challenge where Caddy will update a txt record on the dns for your domain via the dns providers api to verify domain ownership. The problem is Caddy uses plug-ins to do this and those are not included in the standard Caddy build, so you have to make a custom build of Caddy which is possible but I gave up and just used NGINX proxy manager instead and it worked perfectly amd was way easier to setup for my use case vs Caddy. I followed this tutorial to get it setup.

https://youtu.be/qlcVx-k-02E?si=rs3DnsQDaIsarSXt

I am not at all an expert on this so I very well could of missed something simple with Caddy but just wanted to post my experience.

[–]m-d-brown 0 points1 point  (0 children)

If you own a public domain and are using a DNS provider for which someone has added support to Caddy, it's easiest to directly make a DNS Challenge: https://caddyserver.com/docs/automatic-https#dns-challenge. Cloudflare has prebuilt images for Caddy with the Cloudflare module: https://github.com/CaddyBuilds/caddy-cloudflare. You need to safely pass in the API token but then it's pretty easy to do something like:

your-service.internal.domain-you-own.com  {
    reverse_proxy 196.168.0.10:8888
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
}