Add caddy to manage container with existing https

1. Output of caddy version:

I am about to use caddy docker, with docker tag: caddy:2.6.2-alpine

2. How I run Caddy:

I will use docker-compose, see below command and docker-compose.yml content.

a. System environment:

b. Command:

docker-compose up -d

c. Service/unit/compose file:

docker-compose.yml content:

version: '2'

services:
  proxy:
    image: caddy:2.6.2-alpine
    container_name: proxy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./caddy-data:/data

3. The problem I’m having:

My Organization has an administrator that already provide https to my domain name (https://app1.example.com) that pointed to my app1. Public users now already can access https://app1.example.com.

Since I can only manage MyServer, I would like to add caddy to manage my existing docker container.
But I don’t want to interrupt the existing https that my administrator has already provide.

Below image is:
A. Existing topology, currently each docker container exposed directly to public IP.
B. What it will looks like if I add caddy as my own reverse proxy:

The question is: Will my Caddyfile works?

I planned to use this Caddyfile (but I’m not sure if this will preserve existing https):

http://app1.example.com {
        reverse_proxy app1:8080
}

or maybe this config (will it have a conflict with existing https)?

app1.example.com {
        reverse_proxy app1:8080
}

or maybe another suggestion?

Thank you in advance

Your Caddyfile could work, yes.
But I would highly recommend speaking with your administrator or whatever.

There are almost infinite ways how they might have set up that https proxy for your existing app1.example.com and how it might conflict with your plans :worried:

1 Like

Thank you for taking time to answer my question.

So there is no caddy config to skip and use existing https?
Perhaps just by forwarding any request on app1.example.com (without requesting new https) to any define reverse proxy on caddy?

I see an auto_https in global options. Can this be used? does it mean I don’t request https for app1.example.com but use the existing one?
For example like this full Caddyfile:

{
        auto_https off
}

app1.example.com {
        reverse_proxy app1:8080
}
1 Like

You can use Caddy in an http:// only mode - sure.

But if your administrator’s proxy only forwards app1.example.com to you, then you won’t be able to add app2.example.com just by adding it to your Caddyfile.
Instead, your administrator might have to add that to their proxy as well (and maybe create another dns record).

If you only care about app1.example.com for now, then sure.
Your B. Topology from that image you shared with

will suffice.
auto_https off on the other hand, not.

3 Likes

Yes, for now I need only to maintain this sub domain, and manage anything with caddy, including load balancing to any container behind caddy, etc, etc…

Thanks

Caddy is an HTTP server and proxy.

What you’re asking for is a TCP proxy, which means not decrypting the connection before passing it on upstream.

Vanilla Caddy doesn’t do that, but you could do it with GitHub - mholt/caddy-l4: Layer 4 (TCP/UDP) app for Caddy which is a plugin for Caddy that can proxy TCP/UDP. But this is a lot more complicated to use because it only supports JSON config at the moment, no Caddyfile support (see the GitHub issues for details on that).

Why must you keep the TLS connection unbroken? You could have Caddy terminate TLS using its own certificate issued from an ACME issuer, then proxy upstream over either HTTP (if your upstream has an HTTP port exposed) or HTTPS (i.e. another TLS connection to upstream, if necessary).

1 Like

Hmm I didn’t expect to use plugin, it’s much more complicated then… :sweat_smile:

Yes, I actually easily can issue certificate using caddy. But as I told in image A existing topology, Organization Admin already issue the certificate, and I don’t want to broke that.

I’d like to try as Emily suggest, if it doesn’t work, well I have to figure out something else :grin:

Thanks for jumping in to the topic.

By using above caddy config, I can successfully browse to https://app1.example.com/v2022-dev and load my login page of PHP app,

But it work only some parts, other parts shows Mixed Content issue which request http, mostly plugins or javascripts.

I have inspect using chrome devtools, and here is one error sample (other issue have more or less similar error) shows on the console:

Mixed Content: The page at 'https://app1.example.com/v2022-dev/?f=1' was loaded over HTTPS, but requested an insecure stylesheet 'http://app1.example.com/v2022-dev/asset/plugins/datatables-bootstrap/dataTables.bootstrap.custom.css'. This request has been blocked; the content must be served over HTTPS

Is it can be handled by caddy or there’s should be some changes in my code?

I also try to issue certificate using caddy. The certificate issued successfully, no error on caddy logs. But the browser end up with too many redirects error. I am using this caddyfile:

{
        email myemail@gmail.com
}

app1.example.com {
        handle /v1* {
                reverse_proxy app2:80
        }

        handle {
                reverse_proxy app1:80
        }
}

fyi, my administrator issue an asterisk certificate (*.example.com)

Currently, I add header Content-Security-Policy "upgrade-insecure-requests" to caddyfile. So far it resolved the problems, here are my final caddyfile:

http://app1.example.com {
        handle /v1* {
                reverse_proxy app2:80
        }

        handle {
                reverse_proxy app1:80
                header {
                        Content-Security-Policy "upgrade-insecure-requests"
                }
        }
}

Your upstream app needs to be updated to produce URLs with the same scheme as the incoming requests (or just use //app1.example.com, i.e. no scheme, which will let browsers use the appropriate scheme, or use absolute URLs with no hostname in it which would be even better).

1 Like

Thanks for the suggestion.

After a quick check, might be there’s a mistake on how to automatically detect https/http on our base_url variable.
We use this snippets to detect https/http inside our config PHP app:

$isSecure = false;
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
    $isSecure = true;
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') {
    $isSecure = true;
}
$REQUEST_PROTOCOL = $isSecure ? 'https' : 'http';

if ( ! isset( $_SERVER['HTTP_HOST'] ) ) {
     $_SERVER['HTTP_HOST'] = '';
}
$config['base_url'] = $REQUEST_PROTOCOL.'://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME']);

This base_url used to produce the full URL. Might be it’s not bullet proof :sweat_smile:

Why set the protocol at all? Let the browser/client decide.

1 Like

Ah great, thanks to both of you @matt and @francislavoie , setting base_url this way is working:

$config['base_url'] = '//'.$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME']);
1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.