Repository: caddyserver/caddy (language: go)

Issue:
Doesn't seem possible to disable ALPN
Hello again. Caddy's ALPN configuration is a part of TLS connection policy. I would like to be able to turn it off, which is to say, pass an empty uh... this thing to golang stdlib:
>	// NextProtos is a list of supported application level protocols, in
>	// order of preference. If both peers support ALPN, the selected
>	// protocol will be one from this list, and the connection will fail
>	// if there is no mutually supported protocol. If NextProtos is empty
>	// or the peer doesn't support ALPN, the connection will succeed and
>	// ConnectionState.NegotiatedProtocol will be empty.
>	NextProtos [][string](https://pkg.go.dev/builtin#string)

Unfortunately, I don't seem to be able to do this, because of this line (58):
https://github.com/caddyserver/caddy/blob/2b3046de36bad70bd7e48478c99a8a030fb35b98/modules/caddytls/connpolicy.go#L56-L59

Maybe allow passing null explicitly as value for `"alpn":` json key?

In any case it doesn't say in the documentation that empty array means default values:
https://caddyserver.com/docs/json/apps/http/servers/tls_connection_policies/alpn/


Discussion / hints:
:thinking: Never had a request like this before. What's your use case? Eliminating ALPN breaks upgrades to the application layer.
Well, having ALPN breaks TLS if I don't know what is my next proto. My Caddy use case has little to do with HTTP and nothing to do with HTTP/2 or /3.

Edit: it boils down to a universal TLS termination endpoint with automatic cert management.
I mean, I suppose we could change the condition to `== nil`, but before we do, I still want to be sure I understand the use case. How do you use a TLS connection without ALPN? Or if it's not used, why not just have it be ignored instead?
I don't understand the question. What do you mean "how"? I use a TLS connection without a care in the world what the next proto is.
I'm not really secretive about the work I do, I can explain in detail if you wish, but I tend to keep things small in issues, because the problems tend to be sidetracked miles away from the root cause. The root cause here in my opinion is that golang's TLS library allows to disable server-side ALPN, while Caddy doesn't support it. I don't ask to change any defaults, or any established behaviour, I want to specifically configure my Caddy in a way that stdlib allows, supports and doesn't frown upon, if I'm not mistaken.
> I use a TLS connection without a care in the world what the next proto is.

I guess that's my question then: if you ignore ALPN, then why does it matter if it's set?
> I guess that's my question then: if you ignore ALPN, then why does it matter if it's set?

Matt, the server needs a correct list of all possible values the client may send if ALPN is enabled, otherwise it won't accept the connection. As I don't know this list, I can't configure it in a meaningful way, I need to disable it in order to allow the client to connect, where am I wrong?
I still don't really understand your use case, but would you mind doing a favor and changing the `len()` logic to `== nil` to see if that does what you need?
I'll be happy to explain further. As I've said I use Caddy as a universal TLS termination endpoint with built-in certificate management. Suppose there's an existing client-server communication in use -- I don't know, Postgres replication. Both the client and the server know the next protocols they want (we assume ALPN is in use, otherwise the example doesn't work). Client lists its protocols, server checks if there's an intersection with its protocols. Crucially, if there isn't one, server closes the connection and doesn't allow the client in -- that's how ALPN works.

Now I need to proxy this connection through Caddy. I can't realistically modify the client to require it to not send ALPN anymore. I can't realistically go through every such connection, list every protocol they use, and then maintain this list in the future, it's just too much of a hassle. Now we're at an impasse: the client sends ALPN list, but the server -- Caddy -- doesn't know what to do with it. Caddy will not accept the client's connection, unless the client happens to request http/1.1 and/or h2. As a side note, Caddy will accept h2 even if it can't serve it later in the handlers -- it manifests as an empty answer, and I believe you have had issues with this already.

There's a really simple test case: `$ openssl s_client -alpn something -connect ...`. Try to serve it without explicitly configuring `"alpn": ["something"]`.

As for testing new logic, I'm afraid it would be difficult for me. I'm not at all a golang developer. I was able to hack around, and even made my own plugin, but it took me several days to build Caddy with it. I can of course edit the file, but I don't know what to do with this edited file, and golang proficiency is not among my life goals. That said, I'll try to do it on Monday.
> As for testing new logic, I'm afraid it would be difficult for me. I'm not at all a golang developer. I was able to hack around, and even made my own plugin, but it took me several days to build Caddy with it. I can of course edit the file, but I don't know what to do with this edited file, and golang proficiency is not among my life goals. That said, I'll try to do it on Monday.

The simplest way is to use `xcaddy` to replace the main Caddy package itself with your local checked-out copy. See the README for examples https://github.com/caddyserver/xcaddy#custom-builds

```
$ xcaddy build \
    --with github.com/caddyserver/caddy/v2=../../my-caddy-fork
    --with github.com/some-other/plugin
```
@stronny If I push a patch to a branch, would you be able to test a CI artifact to ensure it meets your needs?
Thanks, Francis, that gives me an idea indeed.

Matt, I think I'll be able to test local modifications after all.

By the way, I have remembered one more quibble I had with hardcoded behaviour: Caddy stamps `Server: Caddy` header quite early in its handlers chain. Do you want me to open a separate issue for that?
> By the way, I have remembered one more quibble I had with hardcoded behaviour: Caddy stamps `Server: Caddy` header quite early in its handlers chain. Do you want me to open a separate issue for that?

This is intended and not going to change. See other issues for discussion about it if you feel the need to dig into it.
It's a hard thing to search for. I was only able to find this one: #3455. As far as I understand the intention is to make Caddy more visible on the internet?

Fix patch:
diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go
index 46a8eda399f..a4dc4119d96 100644
--- a/modules/caddytls/connpolicy.go
+++ b/modules/caddytls/connpolicy.go
@@ -54,7 +54,7 @@ func (cp ConnectionPolicies) Provision(ctx caddy.Context) error {
 		}
 
 		// enable HTTP/2 by default
-		if len(pol.ALPN) == 0 {
+		if pol.ALPN == nil {
 			pol.ALPN = append(pol.ALPN, defaultALPN...)
 		}
 
@@ -283,7 +283,7 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
 			break
 		}
 	}
-	if !alpnFound {
+	if !alpnFound && (cfg.NextProtos == nil || len(cfg.NextProtos) > 0) {
 		cfg.NextProtos = append(cfg.NextProtos, acmez.ACMETLS1Protocol)
 	}
 


Test patch: