Configuring docker image with .json

1. Caddy version (caddy version):

The docker image “caddy:2.1.1-alpine”

2. How I run Caddy:

In a docker managed by DC/OS

a. System environment:

Docker 19.03.8
Whatever version of alpine linux the caddy:2.1.1 docker image uses.

b. Command:

The docker command is being constructed for me by DC/OS but it would look something like this:

docker run -v /distributed_file_system/automated_testing/caddy.json:/etc/caddy/Caddyfile -v /distributed_file_system/automated_testing/certs:/root/.caddy -p 8443:8443 caddy:2.1.1-alpine

c. Service/unit/compose file:

I’m not sure what you mean by this.

d. My complete Caddyfile or JSON config:

{
  "apps": {
     "http": {
        "servers": {
           "example": {
              "listen": [":8443"],
                "routes": [
                   {
                     "handle": [{
                        "handler": "static_response",
                        "body": "It worked this time"
                      }]
                   }
                ]
             }
          }
       }
   }
}

3. The problem I’m having:

There are two questions I couldn’t figure out reading the documentation.

The first is whether or not it is possible when using the docker image to mount in a .json file rather than a Caddyfile. The first error message described below are what happen when I tried to do so. If so, how? Generally speaking, if the Caddyfile is going to be converted into json and the json is more fully featured, I’d prefer to use json to begin with. As a stop gap, I could hit the /load endpoint with the json but I’d really like for the json to be automatically be loaded each time dcos runs the service as opposed to relying on some external script to load it in.

The second question is whether or not it’s possible to disable client authentication. The only thing I found when looking around for answers earlier suggested that I ought to switch TLS to on_demand and a link was given to how one would do this in the json configuration (but not in the Caddyfile). Ultimately, what I would like is for unauthenticated clients to hit my proxy and have their request be passed onwards with the certificates I provide for them added (which are mounted into the docker image via the command above).

4. Error messages and/or full log output:

This is an output of attempting to run the docker while mounting in the caddy.json shown above.

run: adapting config using caddyfile: /etc/caddy/Caddyfile:2: unrecognized global option: apps
I0817 12:16:37.703112  8080 executor.cpp:736] Container exited with status 1

This is an output of when I switched to a Caddyfile and attempted to hit the server with an unauthenticated client. (on the client end I get ‘curl: (35) Peer reports it experienced an internal error’)

[automated-testsautomated-test-proxy.marathon.l4lb.thisdcos.directory] acme: error: 400 :: urn:ietf:params:acme:error:dns :: DNS problem: NXDOMAIN looking up A for automated-testsautomated-test-proxy.marathon.l4lb.thisdcos.directory - check that a DNS record exists for this domain, url: 
 - retrying in 2m0s (1m33.017635556s/720h0m0s elapsed)...

5. What I already tried:

I tried reading the documentation, the docker image README, and doing web searches. I have no reason to believe that anything is behaving in a fashion other than how it’s intended I’m just having troubles finding information on how to do as I wish.

6. Links to relevant resources:

You will need to override the default run command of the container if you want to use JSON config instead of the Caddyfile. It defaults to using the caddyfile adapter with the config at /etc/Caddy/Caddyfile. By convention, Caddy assumed a file that begins with the name Caddyfile to be a Caddyfile, so mount your config to a different path to make it less confusing.

The Caddyfile is much easier to write and maintain, and covers the 90% case. You should be fine to use a Caddyfile first, until you hit a wall (which isn’t too likely). Then if/when you do, you can use the caddy adapt command to get the JSON representation of the Caddyfile and make whatever changes you need to keep going. Note that it’s a one-way adapter, there’s no tooling to convert a JSON config back to a Caddyfile.

When running in Docker, please don’t forget to persist the /data volume in your container, else you will lose data and possibly hit Let’s Encrypt rate limits.

See below for the tls directive config options, you’ll notice there’s 4 different modes of operation for client certificate verification. One of these is likely what you need.

Thanks for responding.

I prefer working in json because I’m familiar with that syntax and find it easy to correct and parse. However since it appears we have to send a command to the server in order to switch and since we have no intent to persist the data for the server it looks like we’ll need to use the Caddyfile.

In that case, I’d like to ask a different question related to the syntax of the Caddyfile. Attempting to follow the syntax in that very definition I create this file:

automated-testsautomated-test-proxy.marathon.l4lb.thisdcos.directory {
        respond "It works now"
}

tls internal {
   client_auth {
      mode=request
   }
}

And when I try to load it into the image, I get told

run: adapting config using caddyfile: /etc/caddy/Caddyfile:6: unrecognized directive: client_auth
I0817 14:04:42.599907 29209 executor.cpp:736] Container exited with status 1

Obviously, the client_auth directive does exist. So in what way have I confused the program? The same thing happens if I try to set the on_demand flag. These are the kinds of issues I was running into and the reason I was hoping to be able to just use a json.

That’s not the case, what gave you that impression? You just need to run Caddy like this:

caddy run --config /path/to/caddy.json

Just override the default docker command with that.

FYI, you can use this plugin to generate a JSON schema that can help you with writing your JSON config if that’s the route you want to go:

Okay - just letting you know that the /data directory may be used for other purposes than certificates in the future, so we strongly recommend to persist it.

Since it looks like you’re intending to use tls internal, I would strongly recommend persisting it because Caddy sets up its own CA to issue TLS certificates, and if you don’t persist /data then you’re throwing away the CA every time you stop Caddy.

If you persist the CA, then you can add the root CA certificate to your client’s trust stores. This is kinda the entire point of the feature! :stuck_out_tongue:

That’s not correct. Please read this documentation page, which explains the Caddyfile syntax:

Specifically, you must have directives within your site blocks. Caddy is reading tls and internal as separate site labels and trying to manage those as domains.

Also the Caddyfile does not use = for key-value pairs, it uses whitespace for separating tokens.

Something like this might be what you want:

automated-testsautomated-test-proxy.marathon.l4lb.thisdcos.directory {
	respond "It works now"
	tls internal {
		client_auth {
			mode request
		}
	}
}
1 Like

Thanks for your help.

I am now at least able to get the server up and running using my configuration. I still seem to be running into issues either getting it to listen to unathenticated clients or pushing the communication on but at least I can work with it.

1 Like

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