Basicauth directive with "not" matcher not working

1. Caddy version (caddy version): 2.1.1

2. How I run Caddy:

Caddy is running inside docker with PHP 7.4 FPM side by side.

a. System environment:

  • Docker (Base image PHP 7.4 FPM alpine. Installation of Caddy is based on the original Docker Caddy image)
  • Supervisor.d

b. Command:

Supervisor.d command to start the caddy process:

caddy run --config /etc/caddy/Caddyfile --adapter caddyfile

c. Service/unit/compose file:

supervisord.conf

[supervisord]
nodaemon=true

[program:caddy]
command=caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:php-fpm]
command=php-fpm
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

d. My complete Caddyfile or JSON config:

example.myhiddendomain.com {
    root * /site/public
    try_files {path} /index.php?{query}
    
    encode zstd gzip
    php_fastcgi unix//run/php-fpm.sock
    file_server

    @exceptstripe not path /stripe/webhook /stripe/payment/*

    basicauth @exceptstripe {
        # hash = password
        user JDJhJDEwJFgzR3EyVXRyMFRENmRCQmFTYUl2Yk9IQmtzVDI5VjZuMm1FLmVhck5MRlA1NWFObzJtYzll
    }
    
    header {
        Strict-Transport-Security max-age=30758400;
        Referrer-Policy same-origin
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        X-XSS-Protection "1; mode=block"
    }
    
    # Access logging in the combined format
    log {
        output file /var/log/access.log {
            roll_size 50MiB
            roll_keep 14
            roll_keep_for 336h # 14 days
        }
        format json
    }
    
    log {
        output stdout
        format single_field common_log
    }
}

3. The problem I’m having:

The except directive @exceptstripe is not being applied to routes like https://example.myhiddendomain.com/stripe/webhook and https://example.myhiddendomain.com/stripe/payment/foo.
All routes are secured via basic auth, so I’m not able to make an exception for the above uri’s.

The routes may not have not be secured with the Basicauth directive as the Stripe server needs to talk to the server.

4. Error messages and/or full log output:

There are no error messages. The error is that the Basicauth dialog is prompted.

5. What I already tried:

I already tried the longer syntax which didn’t work as well.

Like:

...
    @exceptstripe {
        not {
            path /stripe/webhook /stripe/payment/*
       }
    }
...

I did’t try the expression syntax because I suppose there is some problem with my understanding of how the not directive is working.

I would gladly appreciate any feedback or hints.

Thanks!

Ah, the issue you ran into is a pretty easy one to miss.

Specifically, it has to do with the default directive order Caddy uses. See the docs below. Specifically, basicauth comes after try_files, so the path is being rewritten before the basicauth directive’s matchers are looked at, therefore the path looks like index.php once the basicauth matcher is executed.

You have a few options, but I think the best one is actually to remove the try_files directive altogether, because the php_fastcgi directive comes with its own try_files rewrite, so I don’t think you need to do that yourself.

1 Like

@francislavoie Thanks for your quick answer.

Unfortunately removing the try_files directive doesn’t fix the problem with the matcher of the basicauth directive not being applied.

Is it possible that the internal try_files directive of php_fastcgi is also handled before the basicauth instruction?

Here is the full json config output of caddy adapt --config /etc/caddy/Caddyfile --pretty if it helps:

{
  "logging": {
    "logs": {
      "default": {
        "level": "DEBUG"
      },
      "log0": {
        "writer": {
          "filename": "/var/log/access.log",
          "output": "file",
          "roll_keep": 14,
          "roll_keep_days": 14,
          "roll_size_mb": 50
        },
        "encoder": {
          "format": "json"
        },
        "level": "DEBUG",
        "include": [
          "http.log.access.log0"
        ]
      },
      "log1": {
        "writer": {
          "output": "stdout"
        },
        "encoder": {
          "field": "common_log",
          "format": "single_field"
        },
        "level": "DEBUG",
        "include": [
          "http.log.access.log1"
        ]
      }
    }
  },
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [
            ":443"
          ],
          "routes": [
            {
              "match": [
                {
                  "host": [
                    "domain.local"
                  ]
                }
              ],
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "vars",
                          "root": "/site/public"
                        },
                        {
                          "handler": "headers",
                          "response": {
                            "set": {
                              "Referrer-Policy": [
                                "same-origin"
                              ],
                              "Strict-Transport-Security": [
                                "max-age=30758400;"
                              ],
                              "X-Content-Type-Options": [
                                "nosniff"
                              ],
                              "X-Frame-Options": [
                                "DENY"
                              ],
                              "X-Xss-Protection": [
                                "1; mode=block"
                              ]
                            }
                          }
                        }
                      ]
                    },
                    {
                      "group": "group0",
                      "handle": [
                        {
                          "handler": "rewrite",
                          "uri": "{http.matchers.file.relative}"
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "try_files": [
                              "{http.request.uri.path}"
                            ]
                          }
                        }
                      ]
                    },
                    {
                      "group": "group0",
                      "handle": [
                        {
                          "handler": "rewrite",
                          "uri": "{http.matchers.file.relative}?{http.request.uri.query}"
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "try_files": [
                              "/index.php"
                            ]
                          }
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "authentication",
                          "providers": {
                            "http_basic": {
                              "accounts": [
                                {
                                  "password": "JDJhJDEwJFgzR3EyVXRyMFRENmRCQmFTYUl2Yk9IQmtzVDI5VjZuMm1FLmVhck5MRlA1NWFObzJtYzll",
                                  "username": "user"
                                }
                              ],
                              "hash": {
                                "algorithm": "bcrypt"
                              },
                              "hash_cache": {}
                            }
                          }
                        }
                      ],
                      "match": [
                        {
                          "not": [
                            {
                              "path": [
                                "/stripe/webhook",
                                "/stripe/payment/*"
                              ]
                            }
                          ]
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "encodings": {
                            "gzip": {},
                            "zstd": {}
                          },
                          "handler": "encode"
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "static_response",
                          "headers": {
                            "Location": [
                              "{http.request.uri.path}/"
                            ]
                          },
                          "status_code": 308
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "try_files": [
                              "{http.request.uri.path}/index.php"
                            ]
                          },
                          "not": [
                            {
                              "path": [
                                "*/"
                              ]
                            }
                          ]
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "rewrite",
                          "uri": "{http.matchers.file.relative}"
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "split_path": [
                              ".php"
                            ],
                            "try_files": [
                              "{http.request.uri.path}",
                              "{http.request.uri.path}/index.php",
                              "index.php"
                            ]
                          }
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "reverse_proxy",
                          "transport": {
                            "protocol": "fastcgi",
                            "split_path": [
                              ".php"
                            ]
                          },
                          "upstreams": [
                            {
                              "dial": "unix//run/php-fpm.sock"
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "path": [
                            "*.php"
                          ]
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "file_server",
                          "hide": [
                            "/etc/caddy/Caddyfile",
                            "/etc/caddy/Caddyfile.base"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ],
              "terminal": true
            }
          ],
          "logs": {
            "logger_names": {
              "domain.local": "log1"
            }
          }
        }
      }
    }
  }
}

If you comment out or remove the try_files directive and adapt again, you’ll notice that the only bit that gets removed is the handler that comes immediately before the basicauth handler in the JSON config. The try_files from php_fastcgi is added to the JSON together with the rest of the bits of php_fastcgi, all in the same place as determined by the directive order.

In the JSON, handlers are executed in the order the appear, the ordering part I explained earlier is purely Caddyfile logic, meant to prevent users from shooting themselves in the foot. It generally does the right thing, but in your case it’s not perfect because it just happens that it’s not in the order that does the ideal thing for you.

After trying without the try_files, maybe try a more permissive basicauth matcher to see if it gets you further.

1 Like

@francislavoie Thanks for your help. Seems like there is everything correct now. And the configuration is working.

1 Like

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