How correctly use the admin API endpoint from other host

Hi folks :wave: I looking into use the caddy server to a personal project and I planing to use the API to manage a lot of sites. I using the official docker image and I run the API on port 2019. but how I can protected the API, in the docs only say this :thinking: :

If you are running untrusted code on your server (yikes :grimacing:), make sure you protect your admin endpoint by isolating processes, patching vulnerable programs, and configuring the endpoint to bind to a permissioned unix socket instead.

Currently I trying to look if there is any doc explaining how secure the admin API.
I wan to connect via HTTP from a client and mange the server from other host with users and permissions. Somebody can point me on the right direction ?

1 Like

Good question, I need to expand the docs about this.

This is on Linux I guess?

The basic idea is that itā€™s a network socket, so you donā€™t want untrusted code to have access to it. Generally this is not a problem in the first place because if your system is running malicious code, itā€™s game over anyways. So we assume that your system is secure to begin with.

By default, Caddyā€™s admin port is bound to localhost:2019 (the significance of that port number is the year this feature was developed). That covers the majority of problems already. This means that remote code canā€™t access the port. To further increase security, you can restrict requests to come only from designated hosts / origins, with origins and enforce_origin in your admin config:

This is primarily useful against browser-based attacks, as it causes the admin API to emit CORS headers which browsers honor. (But since most people donā€™t deploy servers on the same machines that use web browsers, this is seldom a concern anyway and itā€™s why itā€™s not enforced by default.) As a nice bonus, enabling this also mitigates DNS rebinding attacks.

Another option on Linux is to use unix sockets. Simply permission the socket file so that only the Caddy process and any clients can access it. Then you donā€™t need to have Caddy enforce permissions itself, the OS does it for you.

So thatā€™s basic local access protections in a nutshell.

You mentioned that you want to have permissions for users, etc. You can enable the remote admin endpoint, which is a different socket because it uses TLS (mandatory):

Youā€™ll use the access_control property to specify permissions for specific public keys. Thatā€™s how you can control what each user can do.

In order for the admin endpoint to function, however, the server needs an identity certificate for itself. As the docs mention, you can use the identity property of the config to tell Caddy what its identity is so it can manage a certificate for itself. If your server isnā€™t being accessed with a public DNS name, then you should configure the issuers to be an internal issuer or your own ACME CA if youā€™re rolling your own PKI.

I really intend to write a docs page about this now, but for a while I was thinking it should be reserved for sponsors because itā€™s a very powerful and advanced feature.

Does that help? :slight_smile:

1 Like

hi @matt thk for this answer, yeah this help a lot, but I have two questions. I summarize to see if I understand.

Is this because the address use in the go code to bin to the port is using the explicit localhost:2019 that is a loopback address. so go donā€™t go to listen calls from other that localhost. So the main worry is to not get code that make calls from the local host.

If is set the origins prop on the settings means that caddy will listen from remote connections but only from these origins, but, can not the origin be change for an attacker ?

Here I need to configure listen to enable the endpoint, and I need to use ssh to use the connection, right?

Correct. No remote host can even connect to that socket (unless you change its listen address). Remember this is different from the remote admin socket, which is NOT enabled by default.

Right (if I understand you correctly).

Keep in mind a different container or outside the container counts as ā€œnot localhostā€.

No, you only need to add:

"remote": {}

to your admin config to enable it; there is a default listen address it will use. Only set listen if you want to customize it.

No, you use TLS.

Hi @matt, so I was trying to think how manage the sites, I wan to expose a simple interface like add site, remove site and simple stuffs, I come with this;

  1. create a new Dockerfile use the caddy as base, copy and build my management app to the image.
  2. the application with listen in a different port or host configured on the caddy.
  3. use my app to act as proxy between the internet and the caddy admin.

I donā€™t know if Iā€™m over thinking this. :hot_face:

Caddy already has the remote, so maybe is easier to use it directly, but use a proxy will allow me to only expose some operations. :hugs:

Docker seems unnecessarily complex here, as Caddy is already a static binary.

Not sure I understand the second point. As for third, you probably donā€™t need that either as Caddy can manage who can do what things with its access_control config. (linked above)

1 Like

@matt I created a simple application and manage to access caddy admin successfully inside of a container, I had to:

  • Configure the remote and the identity to a internal issuer.

  • Create a local self signed cert to the client, copy the content inside of the caddyfile.json that is copy during the container image build.

  • Then in my client load the system trust certs with go (I previously trust the caddy self cert) and configure a net/http client to use the tls config.

ready. I was able to make the structure that I wanted. I learn a lot about caddy in the process.

1 Like

I suppose that works :slight_smile:

Might be easier with the use of Caddyā€™s acme_server handler so then you can have it manage the identities of your instances automatically. But either way, sounds like you got it working! And even better, you got more familiar with Caddy and learned a lot :100:

Yeah, it works. Iā€™m experimenting with the configuration and different requests. Iā€™m also reviewing the source code of Caddy to understand the structure that you are using to parse the JSON configuration. Iā€™m using it in my code to manage the requests. Yeah, Iā€™m learning a lot about Caddy.

1 Like

Very cool. Iā€™m updating our website right now with some more thorough documentation in these areas so hopefully that will be helpful.

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