lucaslorentz / caddy-docker-proxy

Caddy as a reverse proxy for Docker
MIT License
3.05k stars 174 forks source link

[Feature Request] Automatic local domain management #652

Closed sinopsysHK closed 2 months ago

sinopsysHK commented 3 months ago

Hello,

I really like this caddy-docker-proxy as it really helped me achieving what I wanted to do:

I have a bunch of web apps hosted on one server deployed via docker in my private lan. Each of these web apps are therefore mapped to a different exposed ports to prevent conflicts.

So instead of requesting these web apps using http[s]://docker.mydomain:XYZ I'm happy to use caddy docker proxy to reach them through https://webappname.mydomain/ (I use a local acme server to dynamically generate certificates + my local dns to route *.mydomain to caddy.mydomain)

But I still have a little pebble in my shoe: if ever I'm too lazy to type the full FQDN URL and I just want to query https://webappname/ caddy is not able to find related certificate and I land on my browser with an ERR_SSL_PROTOCOL_ERROR error.

The workaround is easy: just have to set both short and long addresses in the label:

--label caddy="webappname, webappname.mydomain"

But with 25+ apps it becomes a bit cumbersome

So my request would it be possible to create an activable "local mode" via config to tell caddy docker proxy:

And then allow a special pattern in the docker label to tell caddy docker proxy that this container is to be reversed-proxied within a local domain such as: --label caddy="webappname."

And then when activated caddy docker proxy would automatically register the 2 reverse proxies:

polarathene commented 2 months ago

I just want to query https://webappname/ caddy is not able to find related certificate and I land on my browser with an ERR_SSL_PROTOCOL_ERROR error.

That is a hostname, not a proper FQDN? πŸ€”

You can just append .localhost for all FQDN to be routed to loopback (localhost / 127.0.0.1) and have Caddy listen on that to respond to the address.

For supporting other clients on a network, you'd prefer .home.arpa or the recently approved .internal, unless you have registered a public domain, then use that if you like.


If the only intention is for a slightly shorter address for a service, then you should ask if typing is really the problem.

sinopsysHK commented 2 months ago

Hello,

You're right https://webapp/ is alike hostname and not an FQDN which in my case would be https://webapp.compute.mydomain.here/

Thank you for suggesting alternate solution but my usage is as describded: I want to be able to reach my webapps in my browsers by typing the shortest possible address ("webapp/").

we could debate endlessly if this is good/bad usage/practice as there are almost as many ways of using computers as people over the world so I will not try to convince you to adopt it but on the other hand what would be the issue of having an extra feature that may have only one user ?

Happy on the other hand to discuss on most suitable naming and syntax to make it consistent and sustainable.

(I like the auto-generated bookmark page but it doesn't fulfil my need. This solution is also allowing users not to recall a service port number still CDP is there...)

polarathene commented 2 months ago

Thank you for suggesting alternate solution but my usage is as describded: I want to be able to reach my webapps in my browsers by typing the shortest possible address ("webapp/").

Can you explain the use-case for that?

Try this dashy demo, where it shows what it would look like with many services configured. You can start typing the service name (no need to click anywhere) and it will filter. You can navigate by keyboard or mouse. On actual deployment it will redirect to the proper service URL for you.

we could debate endlessly if this is good/bad usage/practice as there are almost as many ways of using computers as people over the world so I will not try to convince you to adopt it but on the other hand what would be the issue of having an extra feature that may have only one user ?

There are other easy ways to get this sort of functionality with shorter domain in caddy directly if it's really important for you to save typing a few letters.

1 user is rarely a good justification for a niche feature. You get to benefit but offload the feature to others to keep around and maintain. Sometimes that is a problem because future development may run into a situation where it can add friction "can we do this, or will it potentially break this feature? Is anyone still using this feature?" or similar questions like that.

In my experience as a maintainer on other projects that can sometimes adds extra overhead to keep the support and slows down getting some improvements/features pushed forward as there are additional cases to consider or handle. So on those projects we try to delegate to docs / forks where possible and only consider supporting a feature if there is enough user interest and no practical alternatives.

In your case there are alternatives, there doesn't not seem to be a good reason for dismissing them other than being stubborn. Homepage automates the service config via container labels like you would with CDP. It might be a little bit more verbose in config for you than just your CDP feature, but that seems a fair tradeoff for a niche feature requirement.

Most users tend to use a service like Homepage/Dashy/Homarr for this exact concern of navigating to their many services without having to type out each one.


(I like the auto-generated bookmark page but it doesn't fulfil my need. This solution is also allowing users not to recall a service port number still CDP is there...)

??? You don't need to think about port number.

The Homepage service uses a label to refer to the site address, it's very simple page that display the links to each service, you click the service and it takes you to the same link you'd manually type. No port required.


Happy on the other hand to discuss on most suitable naming and syntax to make it consistent and sustainable.

Why not just have .x as TLD with your local DNS, then redirect to the domain you want it to be with certificate:

*.x {
  redir https://{labels.1}.example.com{uri}
}

This solution you can add without making any modification to CDP, just include into the base Caddyfile. Whatever the subdomain is, it'll take that and redirect it to the full one. Very simple and you only have to additionally type .x.


From your PR (so I don't add extra noise bouncing between the issue and PR discussing same issue): https://github.com/lucaslorentz/caddy-docker-proxy/pull/655#issuecomment-2319639353

If my motivation is a XY problem, your proposal is a workaround.

Incorrect. It is what most people self-hosting services do. What you propose is not a feature that users are requesting because it doesn't make sense to implement it in CDP.

XY problem means you want to accomplish a goal, but you go about it the wrong way. That's definitely what is happening here.

polarathene commented 2 months ago

Here is a small example with three placeholder services. It is very simple to use just like Caddy Docker Proxy:

Click to view (main compose.yaml) ```yaml # For other `compose.yaml` to connect through CDP: network: default: name: proxy-net services: reverse-proxy: image: lucaslorentz/caddy-docker-proxy:2.9 volumes: - /var/run/docker.sock:/var/run/docker.sock ports: - "80:80" - "443:443" homepage: image: ghcr.io/gethomepage/homepage:v0.9.6 volumes: - /var/run/docker.sock:/var/run/docker.sock configs: - source: homepage-settings target: /app/config/settings.yaml - source: homepage-docker target: /app/config/docker.yaml # Workaround: Skips copying unwanted demo configs: - source: empty target: /app/config/bookmarks.yaml - source: empty target: /app/config/services.yaml - source: empty target: /app/config/widgets.yaml labels: caddy: localhost caddy.reverse_proxy: "{{upstreams 3000}}" # Embedding small config files into `compose.yaml` instead of mounting via `volumes`: # NOTE: This requires a Docker Compose release from 2024+ configs: homepage-settings: content: | headerStyle: boxedWidgets layout: example: style: row columns: 4 homepage-docker: content: | my-docker: socket: /var/run/docker.sock empty: content: | # Empty ```

I have split these services to a separate compose.yaml instead of a single one to minimize noise.

Click to view (your services compose.yaml) ```yaml network: default: name: proxy-net external: true services: # Several service examples with labels for adding `homepage` metadata: example-a: image: caddy:2.8 environment: SOME_ENV_HERE: world configs: - source: hello-env target: /etc/caddy/Caddyfile labels: caddy: hello-world.localhost caddy.reverse_proxy: "{{upstreams 80}}" homepage.href: https://hello-world.localhost homepage.name: Hello World homepage.description: Basic example with Caddy homepage.icon: caddy homepage.group: Example example-b: image: caddy:2.8 environment: SOME_ENV_HERE: DMS configs: - source: hello-env target: /etc/caddy/Caddyfile labels: caddy: hello-dms.localhost caddy.reverse_proxy: "{{upstreams 80}}" homepage.href: https://hello-dms.localhost homepage.name: Hello DMS homepage.description: Basic example with Caddy homepage.icon: docker-mailserver homepage.group: Example example-c: image: traefik/whoami labels: caddy: whoami.localhost caddy.reverse_proxy: "{{upstreams 80}}" homepage.href: https://whoami.localhost homepage.name: Whoami? homepage.description: Diagnostics check homepage.icon: traefik homepage.group: Example configs: hello-env: content: | :80 { respond "Hello {env.SOME_ENV_HERE}!" } ```

Now you docker compose up and go to https://localhost, you will get all 3 services shown and can click to navigate to their FQDNs configured as href:

image

If you start typing, it will filter the service for quick navigation:

image

So if typing is problem for you, just make this your start page and type your service, should be even quicker UX.

sinopsysHK commented 2 months ago

hi,

Thank you very much for spending time to elaborate alternate solutions.

" In my experience as a maintainer on other projects that can sometimes adds extra overhead to keep the support and slows down getting some improvements/features pushed forward as there are additional cases to consider or handle. So on those projects we try to delegate to docs / forks where possible and only consider supporting a feature if there is enough user interest and no practical alternatives. " Fair point, then let close this PR.

" _Why not just have .x as TLD with your local DNS, then redirect to the domain you want it to be with certificate:

*.x { redir https://{labels.1}.example.com{uri} }_ " I lately found indeed this solution even without ".x" which almost fully address my needs (only facing a little issue with some brothers that default to https if typing only "webapp1/" as I failed to set a working wildcard block for https).

" (I like the auto-generated bookmark page but it doesn't fulfil my need. This solution is also allowing users not to recall a service port number still CDP is there...) " What I meant here with a pinch of ironical words was that a solution like homepage by wrapping an underlying URL into an hyperlink do achieve in a different way what caddy is offering: provide access to a service in a humain friendly way allowing to forget the actual exposed port.

Thanks again for maintaining caddy and CDP for the community.

Cheers...

polarathene commented 2 months ago

I failed to set a working wildcard block for https

Oh right, yeah you can't have a valid wildcard for an entire TLD IIRC.


Actually... Instead of separate subdomains with wildcard, you could use the subpath to provide the value. That'll work too:

# Use whatever short site-address you want:
r {
  # Capture the first subpath component into a var:
  vars service-name {path.0}

  # The `uri` placeholder (subpath + query params) now removes the first subpath component:
  handle_path /{vars.service-name}* {
    # Redirect to full FQDN and if any additional URI present append that:
    redir https://{vars.service-name}.example.com{uri}
  }
}

Or with Caddyfile base for CDP embedded into the compose.yaml:

services:
  reverse-proxy:
    image: lucaslorentz/caddy-docker-proxy:2.9
    environment:
      CADDY_DOCKER_CADDYFILE_PATH: /etc/caddy/Caddyfile
      # Name this variable whatever you like, so long as it matches the reference in Caddyfile:
      MY_FQDN_SUFFIX_HERE: services.example.com
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    configs:
      - source: cdp-caddyfile-base
        target: /etc/caddy/Caddyfile
    ports:
      - "80:80"
      - "443:443"

configs:
  cdp-caddyfile-base:
    content: |
      r {
        tls internal

        vars service-name {path.0}
        handle_path /{vars.service-name}* {
          redir https://{vars.service-name}.{env.MY_FQDN_SUFFIX_HERE}{uri}
        }
      }

Now you can go to https://r/webapp and it will redirect to https://webapp.services.example.com πŸ‘

Now no dashboard service needed, just different approach for how you type the short URL you wanted. So PR feature is not needed 😎


Thanks again for maintaining caddy and CDP for the community.

I am not involved in this πŸ˜… Just a user like yourself :)

lucaslorentz commented 2 months ago

Apparently, this was solved without changes to CDP codebase. Please re-open in case I misunderstood the thread.