Use Admin API from Firebase Cloud Function

1. Caddy version (caddy version):

v2.2.0 h1:sMUFqTbVIRlmA8NkFnNt9l7s0e+0gw+7GPIrhty905A=

2. How I run Caddy:

  • On a Digital Ocean droplet
  • Caddy is built with cloudflare_dns and certmagic using xcaddy
  • I use the Admin API for config and not a manual Caddyfile

a. System environment:

Ubuntu 18.04.4

b. Command:

caddy start

c. Service/unit/compose file:

paste full file contents here

d. My complete Caddyfile or JSON config:


{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [
            ":443"
          ],
          "routes": [
            {
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "static_response",
                          "headers": {
                            "Location": [
                              "https://velosity.site{http.request.uri}"
                            ]
                          },
                          "status_code": 302
                        }
                      ]
                    }
                  ]
                }
              ],
              "match": [
                {
                  "host": []
                }
              ],
              "terminal": true
            },
            {
              "match": [
                {
                  "host": [
                    "root",
                    "*"
                  ]
                },
                {
                  "path": [
                    "/var/www/html"
                  ]
                }
              ],
              "terminal": true
            },
            {
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "vars",
                          "root": "/var/www/html"
                        }
                      ]
                    },
                    {
                      "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/php7.2-fpm.sock"
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "path": [
                            "*.php"
                          ]
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "file_server",
                          "hide": [
                            "/etc/caddy/Caddyfile"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ],
              "match": [
                {
                  "host": [
                    "rasmuslian.website",
                    "velosity.site",
                    "*.velosity.site"
                  ]
                }
              ],
              "terminal": true
            }
          ],
          "tls_connection_policies": [
            {}
          ]
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [
          {
            "issuer": {
              "challenges": {
                "dns": {
                  "provider": {
                    "api_token": <token>
                    "name": "cloudflare"
                  }
                }
              },
              "module": "acme"
            },
            "subjects": [
              "velosity.site",
              "*.velosity.site"
            ]
          },
          {
            "issuer": {
              "module": "internal"
            },
            "subjects": [
              "*"
            ]
          }
        ]
      },
      "certificates": {
        "automate": [
          "root",
          "*"
        ]
      }
    }
  }
}

3. The problem I’m having:

Hi, I recently started using Caddy for my server because I’m trying to create a web app that can (among many things) have the ability to add hosts to my config file. In other words, I want the users of my web app to be able to add their domain to my host config.

I have got the Admin API to work in the CLI for my server, but now I want to make it work remotely. My idea was to call the Admin API from a Firebase Cloud Function. The problem is I have a really hard time figuring out how I can do this, and also making the Admin API endpoint protected somehow.

In short my goal is to:

  • Make Admin API available remotely
  • Protect Admin API from unauthorized access (token, user&pass, etc)

4. Error messages and/or full log output:

No errors, haven’t gotten a working solution

5. What I already tried:

I was about to try this solution ( Access Caddy server API from remote http ), but was very unsure of how I could “have some kind of authentication or firewall limiting access to only you” and still let users via my web app add their domain to my host in Caddy.

6. Links to relevant resources:

The way to do this would be to enable enforce_origin and set one of the allowed origins to a strong secret, like a 32 byte hash for example, i.e. something not easily guessable, kinda like an API token.

1 Like

Yes, this really looks like a solution! :smiley:
However, when I tried this I think I accidentally locked myself out of the API. Now I get the error “host not allowed: localhost:2019”. I probably need to add something more to the origins aswell (the server IP?).

But how can I get acces to the Admin API again? I can’t seem to find the json file that I edit through the API…

Your last JSON config is autosaved to the config storage directory (unless you set persist to false in the admin config):

1 Like

Hm… I havent manually set persist to false. I have no caddy file in that directory. Only a go folder :face_with_raised_eyebrow:

Which user have you run Caddy with? It’ll be in the home of that user. If you ran it as a systemd service, then the home will be in /var/lib/caddy, i.e. the home of the caddy user.

Since you’re running Caddy with the API rather than with a Caddyfile, you might want to use the caddy-api.service that ships with the .deb package rather (which the DO one-click app uses, if that’s what you used https://marketplace.digitalocean.com/apps/caddy) by disabling the caddy service then enabling the caddy-api service.

Ah, now I found it in /var/lib/caddy . But if I change that file nothing happens. Yes, I think I would like to disable caddy service then enabling caddy-api service. But I haven’t found any doc on how to do this?

Yeah, that’s just an autosave, it doesn’t start with that config unless you use the --resume option, which is what the caddy-api service file does.

sudo systemctl disable caddy I think, then sudo systemctl enable caddy-api (or the last 2 params are flipped, I never remember which is which between systemctl and service commands, which have them reversed), then you can use sudo systemctl status caddy-api to see if it’s running, and sudo systemctl start caddy-api to start it.

There’s some docs here about using the systemd service (the first few setup steps are done for you already if you installed Caddy with a package):

1 Like

I see. I tried too use caddy run --resume but I got this error: "no autosave file exists {"autosave_file": "/root/.config/caddy/autosave.json"}".

The only “real benefit” of using the caddy-api service is in ExecStart and ExecReload?

Yeah that would be because you’re running as root instead of as the caddy user, so it’s looking in the wrong place for the autosave file.

That’s the only main difference between the caddy service and the caddy-api service, but there’s other advantages to running as a service, like having it use the caddy user instead of root (so it doesn’t have more permissions than it needs), having it automatically start up if the machine restarts, having the stdout output managed (which you can access via journalctl -u caddy or journalctl -u caddy-api), etc.

Hm. Yeah, I see that, but I thought I was running as caddy user because when I do which caddy I get this: /usr/bin/caddy. Do I need to stop caddy first, and then run it as caddy user?

Big thank you for this clarification! :grinning:

That’s just the location of the binary, not the user as which it runs. You can see the current user of your shell session by running whoami.

Yes, you’ll need to stop Caddy if you have it running as another user, because you’ll get a bind conflict (you can’t have two programs trying to bind to the same ports on your machine).

Ah, yeah my bad then. Sorry for being a newbie in this area, but how can I log in with the caddy user? It already exists (sudo useradd caddy | user already exists), but I don’t know the password or similar.

You can use sudo su caddy to switch to that user.

But you don’t really need to do that at all, just let the service run Caddy as that user.

You mean switch to caddy user, start caddy and then just let it run as that user?

What I’m saying is I recommend that you not do that, but instead use the caddy-api service to run Caddy.

Ah yes. I’ll try all this tomorrow. Massive thanks for your support and all others working on the awesome Caddy project. :grin: Will let you know if I mess it up again (let’s hope not) :stuck_out_tongue_closed_eyes:

1 Like

Good morning!
I got a bit better grasp on this I think now, Caddy is now run through caddy-api service I believe. Although I still can’t make requests to the Admin API from for example Postman. Here’s my admin json config:

"admin": {
    "enforce_origin": true,
    "listen": "0.0.0.0:2019",
    "origins": [
      "165.227.129.48", // my servers ip
      "<secret token>"
    ]
  },

And this is the error I get in my Postman. I tried to send a GET request to both http://165.227.129.48:2019/config/ and 165.227.129.48:2019/config/ . It works when using the CLI though.
image

When you say CLI, you mean your local machine with curl? Or do you mean on the machine running Caddy? Did you forget to open port 2019?

I’m using Putty CLI with SSH logged in as the root user. There I can successfully use curl.

I thought the port was open. How do I check or if not open, open it to use remotely?