Conditionals in snippets?

1. The problem I’m having:

I’m using snippets in the Caddyfile in order to reuse as much configuration content as possible and to avoid copying&pasting and producing errors due to gettings those copy&paste clones out of sync.

Now I am wondering whether I can do conditionals within snippets.

Consider a Caddyfile as follows:

(logging) {
    log {
        format transform "{common_log}" ### how can I make this conditional?
        output file /var/log/caddy/{args[0]}.log {
            roll_size 10mb
            roll_keep 5
        }
    }
}

(common) {
    header {
        # Enable headers for all sites here ...
    }
    import logging {args[0]}
}

(common_proxy_https) {
    reverse_proxy {args[1]} {
        transport http {
            tls_server_name {host}
        }
    }
    import common {args[0]}
}

# Global options
{   
    import logging caddy
    events {
        on cert_obtained exec ...
    }
}

# Reverse proxies
host1.example.tld {
    import common_proxy_https service1 https://service1.local
}

host2.example.tld {
    import common_proxy_https service2 https://service2.local
}

host3.example.tld {
    import common_proxy_https service3 https://service3.local
}

I would like to keep JSON log format for the main caddy.log (via global options import logging caddy) but for the actual reverse proxies I would like to have traditional Apache access log format in order for fail2ban to work.

Therefore my question: Is it possible to have a conditional on {args[0]} != "caddy" within snippet (logging) so that the format transform "{common_log}" is only active if it is not the main caddy log?

2. Error messages and/or full log output:

No error messages, I’d like to improve Caddyfile configuration.

3. Caddy version:

$ caddy version
v2.7.4 h1:J8nisjdOxnYHXlorUKXY75Gr6iBfudfoGhrJ8t7/flI=

4. How I installed and ran Caddy:

a. System environment:

x86_64 Debian GNU/Linux

b. Command:

I diverted custom caddy according to the documentation at Build from source — Caddy Documentation

c. Service/unit/compose file:

n/a

d. My complete Caddy config:

See above.

5. Links to relevant resources:

n/a

Just make two snippets with different names. Like logging_json and logging_commonlog or something.

Yes, basically

(logging_common) {
        #level DEBUG
        output file /var/log/caddy/{args[0]}.log {
            roll_size 10mb
            roll_keep 5
        }
}

(logging_json) {
    log {
        import logging_common {args[0]}
    }
}

(logging_apache) {
    log {
        # Next line requires module caddy.logging.encoders.transform:
        format transform "{common_log}"

        import logging_common {args[0]}
    }
}

is what I am using, I was just wondering whether this repetition can be expressed a bit more concise with some kind of conditional statement.

Yeah you can do that. Though personally I’d probably skip the logging_common snippet cause it’s barely saving you any lines and only adding complexity (making it harder to read the config cause you need to visually jump when you read import)

Thanks for your replies, but also to summarize: There is nothing possible like

(logging) {
    log {
        if ({args[0]} != "caddy") {
            format transform "{common_log}"
        }

        import logging_common {args[0]}
    }
}

Or via some other syntax? Is some conditional like this planned or is it a deliberate decision not to have it?

It’s a deliberate decision. if is not a good idea in config.

Not wanting to argue here, but for other parts of the config, a “kind of” conditional exists:

    @admin {
        path /admin*
        not client_ip 192.168.1.0/24
    }
    redir @admin /

Which is basically

    if (path == "/admin*" && client_ip != "192.168.1.0/24") {
        redir /
    }

and I think this is not a bad idea either. :wink:

But again, not warning to argue, just wanting to understand. I have a - not so concise - solution and can live with it.

Those are request matchers, it’s runtime tests against the current request. That’s not the same thing as conditional config.

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