Guide for Windows Admin Center Proxy from Linux VM to Server Core

1. My Caddy version (caddy version):

#caddy version
v2.0.0-beta.15 h1:Td1esMk7bebftnoBuT3gOqUGxew5HqdIKw3s36S8tNw=

I have been reading about Proxying Windows Admin Center which lead me to Caddy and I am so far really impressed with how quick I was able to switch from an already setup NginX reverse proxied site to Caddy, but WAC is a bit more complicated.

I have read through Doesn't work when reverse proxy Windows Admin Center which seems to imply it’s working, but I am requesting a bit more guidance as to setup. Another user/thread has asked the same here → Windows Admin Center + Portainer as subdirectory - #8 by comp500

I currently have Server Core 2019 w/ WAC1910 running which I can connect to remotely just fine but am looking to setup a proxy server on a Ubuntu 18 Desktop VM. Note: I setup this using a “self generated SSL Cert” & serving over port 443. I do have my own cert I could use, but I’m fine with just having Caddy trust this self-generated cert for now/testing as well.

So far all I have done is downloaded/installed Caddy binary and then ran the following but it doesn’t work and I am at a loss (also thinking this is not correct at all!):

cat << 'EOF' >/var/www/html/wac/CaddyFile
{
    "apps": {
        "http": {
            "servers": {
                "srv0": {
                    "listen": [
                        ":8888"
                    ],
                    "routes": [{
                        "match": [{
                            "host": [
                                "subdomain.domain.com"
                            ]
                        }],
                        "handle": [{
                            "handler": "subroute",
                            "routes": [{
                                "handle": [{
                                    "handler": "reverse_proxy",
                                    "transport": {
                                        "protocol": "http_ntlm",
                                        "tls": {
                                            "insecure_skip_verify": true
                                        }
                                    },
                                    "upstreams": [{
                                        "dial": "https://192.168.1.42:443"
                                    }]
                                }]
                            }]
                        }]
                    }]
                }
            }
        }
    }
}
EOF
cd /var/www/html/wac/
Caddy Run

Taggin @matt as he seemed to be the master in the last thread!

1 Like

That’s too vague, unfortunately.

Could elaborate on what isn’t working? Do you have logs to share? What requests are you trying? Can you use curl -v to make test requests and show how they look?

1 Like

@francislavoie I’ll try to give more verbose information, but as I’m requesting a “guide” not troubleshooting I was purposefully vague. I don’t think I’m necessarily even close to being setup correctly.

"upstreams": [{
    "dial": "https://192.168.1.42:443"
}]

This looks wrong – should probably be 192.168.1.42:443 (dial addresses aren’t URLs).

Would be a good place to start. Otherwise, I second everything @francislavoie said – we’ll need logs, errors, and more specifics to be helpful to you!

As i said before, I feel I am way off the mark here, as I can only visit http://localhost:2019 and get a 404, but no other URL even gets a response. Here is my terminal output:

root@skynet:/var/www/html/wac# caddy run
2020/03/10 20:22:35.097 INFO    admin   admin endpoint started  {"address": "localhost:2019", "enforce_origin": false, "origins": ["localhost:2019"]}
2020/03/10 20:22:35.097 INFO    serving initial configuration
2020/03/10 20:22:50.602 INFO    admin.api       received request        {"method": "GET", "uri": "/", "remote_addr": "127.0.0.1:53286", "headers": {"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"],"Accept-Encoding":["gzip, deflate"],"Accept-Language":["en-US,en;q=0.5"],"Cache-Control":["max-age=0"],"Connection":["keep-alive"],"Upgrade-Insecure-Requests":["1"],"User-Agent":["Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0"]}}

@matt I am trying to start with the most simple Caddyfile possible so I have stripped down stuff but now I get a bunch of errors (even some with your name!). Does this make sense, can you point out my issue/syntax or is this a code issue?

What I tried:

cat << 'EOF' >/var/www/html/wac/Caddyfile
cadtest.freesoftwareservers.com
reverse_proxy 192.168.1.42:443 {
transport http_ntlm {
tls_insecure_skip_verify
}
}
EOF
cd /var/www/html/wac/
caddy run

Result:

root@skynet:/var/www/html/wac# caddy run
2020/03/10 22:40:44.538 INFO    using adjacent Caddyfile
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xb38666]

goroutine 1 [running]:
github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy.(*HTTPTransport).UnmarshalCaddyfile(0x0, 0xc000206c90, 0x0, 0x0)
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/modules/caddyhttp/reverseproxy/caddyfile.go:616 +0xf06
github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy.(*Handler).UnmarshalCaddyfile(0xc0002d45a0, 0xc000206c30, 0x2, 0xc000376c40)
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/modules/caddyhttp/reverseproxy/caddyfile.go:496 +0xde1
github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy.parseCaddyfile(0xc000206c30, 0xc000206ab0, 0xc00041dca0, 0xc000206c00, 0xc000061d90, 0x1, 0x1, 0xc00041dc80, 0x1, 0x1, ...)
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/modules/caddyhttp/reverseproxy/caddyfile.go:41 +0x4e
github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile.RegisterHandlerDirective.func1(0xc000206c30, 0xc000206ab0, 0xc00041dca0, 0xc000206c00, 0xc000061d90, 0x1, 0x1, 0xc00041dc80, 0x1, 0x1, ...)
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/caddyconfig/httpcaddyfile/directives.go:104 +0x22b
github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile.ServerType.Setup(0xc000206ba0, 0x1, 0x1, 0xc000206ab0, 0xc00041dc80, 0x1, 0x1, 0x0, 0x0, 0x0)
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/caddyconfig/httpcaddyfile/httptype.go:136 +0x3bf0
github.com/caddyserver/caddy/v2/caddyconfig/caddyfile.Adapter.Adapt(0x1234b80, 0x1a33d08, 0xc0002a2780, 0x75, 0x275, 0xc000206ab0, 0xc00045c000, 0x4f000000000001d9, 0xc0006df828, 0x4fb07953b566c748, ...)
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/caddyconfig/caddyfile/adapter.go:49 +0x139
github.com/caddyserver/caddy/v2/cmd.loadConfig(0x1045f77, 0x9, 0x0, 0x0, 0xc0001908c8, 0x80, 0x57, 0xc0006dfc40, 0x40c5d4, 0xc0004dd5c0)
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/cmd/main.go:160 +0x27f
github.com/caddyserver/caddy/v2/cmd.cmdRun(0xc0001806c0, 0x0, 0x0, 0x0)
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/cmd/commandfuncs.go:174 +0x8f1
github.com/caddyserver/caddy/v2/cmd.Main()
        /Users/matt/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.0.0-beta.15/cmd/main.go:72 +0x23e
main.main()
        /Users/matt/Downloads/Downloads/caddytest/main.go:37 +0x20
root@skynet:/var/www/html/wac#

More progress, seems my inital code used “CaddyFile” which won’t work, it has to be “Caddyfile” no capital F. I also had pasted Caddy Run but I really was using caddy run, that was just a mistake of not copy and pasting.

I now get the following error when trying to use the original config:

root@skynet:/var/www/html/wac# nano Caddyfile && caddy run
2020/03/10 22:52:39.970 INFO    using adjacent Caddyfile
run: adapting config using caddyfile: unrecognized parameter name: apps

Just to be unambiguous, Caddy works with any filename for configuration.

However, if it’s not named exactly Caddyfile and located in the same directory you run the caddy command in, Caddy won’t automatically pick it up.

You can use --config /path/to/your/config.file --adapter caddyfile to tell Caddy to use an arbitrary file as its config and that the config syntax is that of a Caddyfile.


If you’re using your configuration from the original post, that’s not a Caddyfile, that’s JSON.

A Caddyfile is syntax for 99% of simpler deployments that don’t need advanced configuration in JSON.


Going back to that panic, I believe that’s this bug which has a fix in the source code, just not yet released as a new version: v2: Http_ntlm option resulting in runtime error · Issue #3119 · caddyserver/caddy · GitHub

Building Caddy from master should resolve that issue for you. (Thanks to @Mohammed90 for pointing this one out to me)

3 Likes

You can also grab the executables containing the fix as published from the CI

https://dev.azure.com/mholt-dev/Caddy/_build/results?buildId=949&view=artifacts&type=publishedArtifacts

3 Likes

@Whitestrake Wonderful news, I got it to work, but as I am about to publish a guide on my blog, I wanted to test somethings and possibly found another code bug (unless I’m doing something wrong!).

With the new executable, this works :

cat << 'EOF' >/var/www/html/wac/Caddyfile
https://cadtest.domain.com {
tls /etc/ssl/certs/domain.crt /etc/ssl/private/domain.key
 reverse_proxy 192.168.1.42:443 {
  transport http_ntlm 
 }
}
EOF
cd /var/www/html/wac/
caddy run

But first I tried to just use a “self_signed” cert for testing and was unable using the following:

cat << 'EOF' >/var/www/html/wac/Caddyfile
https://cadtest.domain.com {
tls self_signed
 reverse_proxy 192.168.1.42:443 {
  transport http_ntlm 
 }
}
EOF
cd /var/www/html/wac/
caddy run

Error:

2020/03/11 18:07:42.620 INFO    using adjacent Caddyfile
run: adapting config using caddyfile: parsing caddyfile tokens for 'tls': Caddyfile:2 - Error during parsing: single argument must be an email address

Unfortunately, tls self_signed isn’t a supported option in Caddy v2. You’ve probably seen that in a v1 config. The error is specifically telling you that it’s expecting an email address if only passing a single argument. See the docs here: tls (Caddyfile directive) — Caddy Documentation

The self-signed certs feature is currently a work-in-progress, and is getting a complete rewrite in functionality to widen the use-case (running an internal CA for longer lived trust). You can follow the discussion and progress here: Caddy+Smallstep integration (trusted certs for localhost and internal sites) · Issue #3021 · caddyserver/caddy · GitHub

Once implemented, it will probably look like tls internal instead.

2 Likes

@francislavoie Thanks for clearing that up, at least I know I wasn’t doing it wrong! I realize V2 is still beta and I’m happy to hear the service is being expanded, I do think it’s valuable for testing/LAN env.

I had one more problem, but it’s a little off topic. I’m trying to proxy the site through Nginx on top of caddy. I have Caddy listening on port 4343 and am able to connect fine (ssl verified), but when I proxy that URL in Nginx I get 502 bad gateway and the caddy log shows http: TLS handshake error from 192.168.1.42:32992: no certificate available for ''

Any idea what that is saying?

A client has to signal which domain name it wants at an early stage of the request so that Caddy knows which of its potentially many certificates it has to choose to establish HTTPS.

no certificate available for '' means that the client (in this case presumably nginx) did not signal this (i.e. it effectively requested '', an empty string), and because Caddy has no valid certificate for this “domain name”, it can’t complete the handshake process.

A quick search indicates that the nginx setting proxy_ssl_server_name is what you need to set to properly configure an nginx reverse proxy to a HTTPS server like Caddy. I’m quite surprised that this functionality is, by default, disabled.

http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_server_name


I’m curious, though. What purpose does nginx → Caddy → upstream serve?

2 Likes

@Whitestrake I must say, your support is top notch, thank you. That directive did make things progress, I now get the Auth prompt, but it just re-prompts over and over again, no output on the caddy cli. I realize this is becoming an NGinX support issue, and perhaps I should seek help elsewhere, but it’s hard to not try to take advantage of such great support when your answers have been spot on.

The reason I want to use NGinX is I recently combined all my VM’s into one “master” VM to save resources and I already have NGinX listening on port 443 and 80. I have apache listen on other ports for LAMP based apps, and now I have caddy serving on 4343. Ideally I’d like to access via https://winadmincenter.domain.com without having to attach ports on the end, but it may not be worth the effort (especially if it causes a performance drop due to all the extra layers).

Request on Port 443 → Nginx → caddy → wac = “clean URL”

My NginX config looks like so, if you can think of a directive I’m missing, let me know. Maybe it needs the NTLM module, I thought maybe since caddy was using it, nginx might be able to proxy through caddy, but I’m not willing to pay for nginx to get that module.

root@skynet:/etc/caddy/wac# cat /etc/nginx/websocket-proxy.conf
proxy_ssl_server_name on;
proxy_http_version      1.1;
proxy_set_header        Upgrade            $http_upgrade;
proxy_set_header        Connection         "Upgrade";
proxy_set_header        Authorization      "";
proxy_read_timeout      86400;
proxy_set_header        Host               $host;
proxy_set_header        X-Real-IP          $remote_addr;
proxy_set_header        X-Forwarded-Server $host;
proxy_set_header        X-Forwarded-For    $proxy_add_x_forwarded_for;
proxy_redirect          off;

Sounds like NTLM is broken, yep. Something ain’t working from client to WAC.

Why not replace nginx with Caddy entirely on that master VM?

Or eve (loathe as I am to suggest it :smiley:) just take Caddy out of the picture? You don’t need all these web servers. One webserver can do all this work unless none of the web server hosts have access to all of the backends you need. I really don’t see any performance issues occuring even with three web servers, because it’s still fundamentally not a huge resource-hogging setup, but it is three times more complex than it needs to be.

All the web servers need feature parity too, when it comes to NTLM, which must be passed all the way through from the client to WAC. If the free nginx can’t handle NTLM, I fully expect it to break WAC if you put it in the middle.

Arguably the biggest, headlining benefit of Caddy is its certificate management. You can still have that without it being on the edge (via DNS validation), but the configuration of that is at least as complex as moving the webserver to the edge on the default ports.

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.