Caddy on QNAP - set up reverse proxy

Thank you for your answer Whitestrake.

Let’s add some information and question, so that I can more easily - with your support - idea tidy the problem.

Additional info:

  1. In the caddy config file I am creating around 15 subdomains (sub.domain.com, sub2.domain.com, etc.). → would be this the reason for the problem?

  2. One subdomain already has one purchased certificate (which however I did not specified in the config). → shall I modify the config file to point to the actual valid certificate? How?

  3. Caddy (when I only use HTTP and use 443 to access directly QTS) currently uses port 8964 (HTTPS) and 8963 (HTTP). When trying to use HTTP, beside trying to free up port 443, I simply changed the router port forwarding of 443 and 80 to the two mentioned ports. → is that the correct way to do?
    → shall I understand from the error that the config is correct in terms of port forwarding/settings?

  4. I do not remember how to check, verify and/or change the two ports used by Caddy (8964 and 8963). → How can I see it?

  5. Beside waiting for a week to test again, is there a flag I can use to at least test that the config file is correct and that next week, once discovered what I am not doing well, the certificates can be issued?

Thanks!

Not likely - you’ve got up to 50 per week.

The main limit is Certificates per Registered Domain (50 per week).
Rate Limits - Let's Encrypt

Taking a closer look at the error you received (too many failed authorizations recently), I would wager that you’ve actually hit this one:

There is a Failed Validation limit of 5 failures per account, per hostname, per hour.
Rate Limits - Let's Encrypt

Have a look at the tls directive:

To use Caddy with your own certificate and key:

tls cert key

https://caddyserver.com/docs/tls#syntax

There’s multiple ways to go about it. This method should work, but forwarding 80/443 straight to 80/443 on the Caddy host is the simplest method. The error implies that something isn’t working, and doing port detouring might be the cause.

Those are not the default ports Caddy uses, so you must have changed them. If you didn’t do it in your Caddyfile, you probably did it by flag.

https://caddyserver.com/docs/cli#http-port

Remember, too, that Caddy outputs what sites it is serving, and on what scheme/port, when it starts up. You can double check that output to know for sure what Caddy’s listening on locally.

We recommend, when testing, troubleshooting, and configuring new or problematic HTTPS setups, that you use the LetsEncrypt ACME staging environment. They have relaxed rate limits there, and a rate limit on the production environment won’t stop you from testing on the staging environment. Use the -ca flag when running Caddy to specify which ACME endpoint to use.

1 Like

So, I tried again. I have completely shut down Qhttpd which was taking port 80 and ensured that ports 80 and 443 were free and forwarded by the router to the Nas.

Then I run Caddy with the following command line:

caddy -conf /share/Public/caddy/caddy.conf -agree -port 20015 -root /home/Qhttpd -log $QPKG_ROOT/var/logs/caddy.log -pidfile /var/run/caddy.pid &

The file is the following:

(gzipconf) {
        gzip {
                ext *
                level 7
                min_length 1
        }
}

(addheader) {
        header / {
                Strict-Transport-Security "max-age=31536000;"
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                X-Frame-Options "DENY"
                Referrer-Policy "strict-origin-when-cross-origin"
                -Server
        }
}

test.domain.com {
tls /share/Public/SSLcertificate.crt /share/Public/SSLprivatekey.key
        import gzipconf
        import addheader
        proxy / https://192.168.1.3:8080 {
                keepalive 32
                transparent
                websocket
        }
}

nas.domain.com {
tls sub@domain.com
        import gzipconf
        import addheader
        proxy / https://192.168.1.10:8080 {
                keepalive 32
                transparent
                websocket
        }
}

sub.domain.com {
tls sub@domain.com
        import gzipconf
        import addheader
        proxy / https://192.168.1.10:8080 {
                keepalive 32
                transparent
                websocket
        }
}

The errors I got are the following:

[/share/CACHEDEV1_DATA/.qpkg/Caddy] # Activating privacy features... 2019/10/01 14:58:38 [INFO] [nas.domain.com] acme: Obtaining bundled SAN certificate
2019/10/01 14:58:39 [INFO] [nas.domain.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851733xx
2019/10/01 14:58:39 [INFO] [nas.domain.com] acme: use tls-alpn-01 solver
2019/10/01 14:58:39 [INFO] [nas.domain.com] acme: Trying to solve TLS-ALPN-01
2019/10/01 14:58:46 [INFO] [nas.domain.com] The server validated our request
2019/10/01 14:58:46 [INFO] [nas.domain.com] acme: Validations succeeded; requesting certificates
2019/10/01 14:58:48 [INFO] [nas.domain.com] Server responded with a certificate.2019/10/01 14:58:48 [INFO] [sub.domain.com] acme: Obtaining bundled SAN certificate
2019/10/01 14:58:49 [INFO] [sub.domain.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851752xx
2019/10/01 14:58:49 [INFO] [sub.domain.com] acme: Could not find solver for: tls-alpn-01
2019/10/01 14:58:49 [INFO] [sub.domain.com] acme: use http-01 solver
2019/10/01 14:58:49 [INFO] [sub.domain.com] acme: Trying to solve HTTP-01
2019/10/01 14:58:57 [INFO] Unable to deactivated authorizations: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851752xx
2019/10/01 14:58:58 [INFO] [sub.domain.com] acme: Obtaining bundled SAN certificate
2019/10/01 14:58:59 [INFO] [sub.domain.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851772xx
2019/10/01 14:58:59 [INFO] [sub.domain.com] acme: Could not find solver for: tls-alpn-01
2019/10/01 14:58:59 [INFO] [sub.domain.com] acme: use http-01 solver
2019/10/01 14:58:59 [INFO] [sub.domain.com] acme: Trying to solve HTTP-01
2019/10/01 14:58:59 [INFO] Unable to deactivated authorizations: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851772xx
2019/10/01 14:59:00 [INFO] [sub.domain.com] acme: Obtaining bundled SAN certificate
2019/10/01 14:59:01 [INFO] [sub.domain.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851776xx
2019/10/01 14:59:01 [INFO] [sub.domain.com] acme: Could not find solver for: tls-alpn-01
2019/10/01 14:59:01 [INFO] [sub.domain.com] acme: use http-01 solver
2019/10/01 14:59:01 [INFO] [sub.domain.com] acme: Trying to solve HTTP-01
2019/10/01 14:59:01 [INFO] Unable to deactivated authorizations: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851776xx
2019/10/01 14:59:02 [INFO] [sub.domain.com] acme: Obtaining bundled SAN certificate
2019/10/01 14:59:03 [INFO] [sub.domain.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851780xx
2019/10/01 14:59:03 [INFO] [sub.domain.com] acme: use tls-alpn-01 solver
2019/10/01 14:59:03 [INFO] [sub.domain.com] acme: Trying to solve TLS-ALPN-01
2019/10/01 14:59:06 [INFO] Unable to deactivated authorizations: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851780xx
2019/10/01 14:59:07 [INFO] [sub.domain.com] acme: Obtaining bundled SAN certificate
[/share/CACHEDEV1_DATA/.qpkg/Caddy] # 2019/10/01 14:59:08 [INFO] [sub.domain.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851794xx
2019/10/01 14:59:08 [INFO] [sub.domain.com] acme: use tls-alpn-01 solver
2019/10/01 14:59:08 [INFO] [sub.domain.com] acme: Trying to solve TLS-ALPN-01
2019/10/01 14:59:09 [INFO] Unable to deactivated authorizations: https://acme-v02.api.letsencrypt.org/acme/authz-v3/5851794xx
2019/10/01 14:59:10 [INFO] [sub.domain.com] acme: Obtaining bundled SAN certificate
[/share/CACHEDEV1_DATA/.qpkg/Caddy] # 2019/10/01 14:59:11 failed to obtain certificate: acme: error: 429 :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-order :: urn:ietf:params:acme:error:rateLimited :: Error creating new order :: too many failed authorizations recently: see https://letsencrypt.org/docs/rate-limits/, url:

Where am I doing wrong?

Not sure. Is the subdomain pointed at the correct IP address in DNS?

There might be more info in the (redacted) AuthURLs, and I can’t do any diagnostics (because I assume you don’t own domain.com and don’t know your real domain).

What I can see is that you got rate limited again at the end there. Did you swap to the staging server while testing?

I did not wanted to publicly write the domain name. :slight_smile: I can send you the unredacted in private (if it is possible).

Ok, looking at the link, in one case I found that I did not point the DNS of one subdomain to the right IP (redacted output below):

detail": "DNS problem: NXDOMAIN looking up A for sub.domain.com",
        "status": 400

I have fixed it now.

For the other, I see that the certificate is created, the DNS points to the right address and to the right port (redacted output below):

{
      "type": "tls-alpn-01",
      "status": "valid",
      "url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/5851733xx/Lm2cbA",
      "token": "NKXBWbp1CCeEYwLokpv02gvRfQ_gh696W-Z7cx6Vu_t",
      "validationRecord": [
        {
          "hostname": "test.domain.com",
          "port": "443",
          "addressesResolved": [
            "187.92.110.9"
          ],
          "addressUsed": "187.92.110.9"
        }

I will make a test again to see if it works now.

I have two questions:

  1. Why Caddy, even if there are many other other reverse proxies in the config file, if it finds an error (like the above) in one, it stops and does not try to also make the other working? It could could be useful also to try different set-up in one run.

  2. Is the config and port settings correct in the configuration above or I am still misconfiguring something? Is it useful or is it a mistake to use the variable “-port 20015” to launch Caddy (see post above)? Does it make any difference for caddy to listen ports 80 and 443?

Thanks a lot for your help!

This is a conscious decision the developers made when designing Caddy’s core behaviour. When the user configures the server to grab certs for two different sites, but it can only grab one, the question must be raised:

  • Do we start up with behaviour that is, strictly speaking, not what the user asked for? (i.e. proceed serving one site)
  • Or, do we tell the user that we’re not able to do what they’ve configured us to do? (i.e. fail out, produce an error for the user to troubleshoot and fix)

The decision was made to have the server strictly only do what the user asks, and be loud and obvious with problems. It’s all too common for simple log entries to be ignored, or worse, never logged at all (we see this constantly). During initial startup and configuration is the best time to ensure a user is aware that the server cannot support the configuration they’ve asked for. This decision was supported by the fact that it is possible, if absolutely necessary, for the user to manually configure Caddy such as to provide a best-effort attempt to serve the sites (for example, via On-Demand TLS).

But, ultimately, if you say “Serve these two sites with managed HTTPS!”, and Caddy can’t serve those two sites with managed HTTPS, it’s going to tell you as much.

The configuration looks serviceable. -port only governs which port Caddy will try to use for non-HTTPS-managed sites, so for all the valid sites with Automatic HTTPS, this flag is irrelevant. (To change the main ports, you’d need -http-port and -https-port.)

You might consider the -email flag (e.g. -email sub@domain.com) to set the default email address instead of specifying it in all the individual sites.

1 Like

Thank you, this is helpful to understand the Caddy logic!

In relation to the ports, shall then I forward the port 80 of the router to port 20015? or should I change the lunch command as follows?

caddy -conf /share/Public/caddy/caddy.conf -agree -http-port 80 -https-port 443 -email sub@domain.com -root /home/Qhttpd -log $QPKG_ROOT/var/logs/caddy.log -pidfile /var/run/caddy.pid &

Again, a BIG thanks!

Neither.

Remove all port-related flags. You don’t need to change the default -port (its default is :2015, but I don’t even think you’ve got any sites unmanaged). You don’t need to set the -http-port to 80, because that’s its default too, as is -https-port defaulted to 443.

Ok, I tried again (using above settings) and all certificates are created correctly: “server responded with a certificate”.

At the end of the process, have around 15 reverse proxies, I have the following output:

Serving HTTPS on port 20015
https://sub.domain.com:20015
https://test.domain.com:20015

Serving HTTP on port 80
http://sub.domain.com
http://test.domain.com

I can confirm that ports 80 and 443 are correctly forwarded to the NAS. Moreover I can see that Caddy has taken port 80, but not port 443 (which not used).

If I type now sub.domain.com on the browser I am now redirected to https://sub.domain.com:20015.

Am I still missing something?

EDIT: It seems it is solved now taking out -port 20015 from the launching instruction, but I still have one problem for the (sole) domain for which I have a purchased certificate:

sub.domain.com {
tls /share/Public/SSLcertificate.crt /share/Public/SSLprivatekey.key
        import gzipconf
        import addheader
        proxy / http://192.168.1910:8080 {
                keepalive 32
                transparent
                websocket
        }
}

Indeed the output that I have from caddy is the following:

Serving HTTPS on port 2015
https://sub.domain.com:2015

I do not understand why it uses port 2015 even if there is no port set on caddy at launch…

Thanks (I am almost there!)

This is expected behaviour. Automatic HTTPS is what configures the ports, as well as the redirect listener.

Caddy automatically enables HTTPS for all your sites, given that some reasonable criteria are met:

  • Certificates and keys are not provided by you

Automatic HTTPS — Caddy Documentation

Given that you’ve provided your own certificate, you’ll need to configure this manually.

Like so:

https://sub.example.com {
  # [site config here]
}

http://sub.example.com {
  redir https://sub.example.com{uri}
}
1 Like

Ok, so if my understanding is correct, I have to “split” the instruction to ensure it works. I will test it!

By the way, and I am continuing here as it relates to Caddy and QNAP, I have discovered that using HTTPS will create a problem for many QNAP applications used such as Notification Center, Help desk, Malware Remover, Photo Station, Download Station, etc… which are actually standalone applications developed by QNAP but linked to QTS through a proxy (for which the url is for example user.myqnapcloud.com/…).

For those I receive the error “connection refused”.

Should I specify something in the config file of Caddy to tell that it should also take into consideration all other applications?

Thanks again

Which port are you trying to connect to them on? (i.e. is there a port in the browser URL? Is there a port in the reverse proxy config in the Caddyfile?)

No, basically I connect, via https (443) to port 8080 of my QNAP where I can access to QTS (the NAS interface, eg test.myqnapcloud.com). From there you can open different applications, most of them within the same window, which are basically separate applications which are proxied and embedded to QTS, just with a different path (e.g. test.myqnapcloud.com/malware_remover/).

I am sorry that I cannot be more specific. What I know is that with Caddy in http I had not this error.

Uhh, do you mean that you connect to Caddy, which proxies to port 8080?

Could some of these be iframes? When you get a connection refused error, can you tell us which request exactly generates that error?

Yes, I use Caddy to connect to [internal IP]:8080 (and other ports for other services) just having ports 80 and 443 opened on the router.

Re iframes, I do not know. How can I confirm you? Can caddy log tell me this?

Thanks a lot!

No, you’ll need to check your browser’s dev console.

Open it up and go to the Network tab, then reload the problem page. You can then inspect each request to determine what URL was used for the request, what port, etc. and what the response was.

Finally I managed to look at it. Here is what I am getting:

VM1557:1 Refused to display ‘Welcome dragndropbuilder.com - BlueHost.com’ in a frame because it set ‘X-Frame-Options’ to ‘deny’.

Refused to display ‘Welcome dragndropbuilder.com - BlueHost.com’ in a frame because it set ‘X-Frame-Options’ to ‘deny’.

Refused to display ‘https://test.domain.com/malware_remover/?windowId=q-app-MalwareRemover-1332&v=3.5.2&date=2019-08-01’ in a frame because it set ‘X-Frame-Options’ to ‘deny’.

Looking at here it seems that I have to change the addheader settings here:

(addheader) {
        header / {
                Strict-Transport-Security "max-age=31536000;"
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                X-Frame-Options "DENY"
                Referrer-Policy "strict-origin-when-cross-origin"
                -Server
        }
} 

Should I simply take out the X-Frame-Options "DENY" settings or should I do something else? Is there any downside to do so?

Thanks!

EDIT: doing so it resolves the issues. But I do not know if there is any downside.

Reading here it seems there is a more elegant and secure way to do, but I would need some support in case. :slight_smile:

You could try setting:

X-Frame-Options "allow-from https://example.com" (replacing example.com with the domain name you use to access the site loading the frames).

As I have at least 2 subdomain which will require so, can I put it in the specific part of the conf fail or should it be on the general section linked to addheader?

X-Frame-Options doesn’t allow for multiple subdomains to be set; the options are deny, sameorigin, and allow-from [uri].

You can use Content-Security-Policy: frame-ancestors [option...] instead, which can specify multiple origins the frame can be loaded to - Content-Security-Policy obsoletes X-Frame-Options.