The Wayback Machine - https://web.archive.org/web/20201126135402/https://github.com/caddyserver/caddy/issues/3767
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"invalid header field value" when including http.request.tls.client.certificate_pem placeholder in a header #3767

Open
mc0239 opened this issue Sep 30, 2020 · 10 comments

Comments

@mc0239
Copy link

@mc0239 mc0239 commented Sep 30, 2020

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

I am trying to pass pem-encoded client certificate to proxied service via a X-SSL-Cert header, like so:

sub.example.com {
    reverse_proxy 127.0.0.1:8000 {
        header_up X-SSL-Cert {http.request.tls.client.certificate_pem}
    }

    tls {
        client_auth {
            mode require
        }
    }
}

I am trying to use recently added client.certificate_pem placeholder (497c3f7#diff-18e521e381018c19bda45164dcad2b9cR347 ) which, if I understand correctly, is synonymous to apache's SSL_CLIENT_CERT (i.e. in an apache config one would write RequestHeader set X-SSL-Cert "%{SSL_CLIENT_CERT}s").

But for the given Caddyfile I get a 502 Bad Gateway with the following error in the logs:

Sep 30 19:11:55 example.guest caddy[9838]:
{
	"level": "error",
	"ts": 1601493115.886311,
	"logger": "http.log.error",
	"msg": "net/http: invalid header field value \"-----BEGIN CERTIFICATE-----\\nMIIG... certificate content...-----END CERTIFICATE-----\\n\" for key X-Ssl-Cert",
	"duration": 0.000691328,
	"status": 502,
	"err_id": "gys3swkea",
	"err_trace": "reverseproxy.(*Handler).ServeHTTP (reverseproxy.go:440)"
}
@francislavoie
Copy link
Member

@francislavoie francislavoie commented Sep 30, 2020

Interesting. I think the problem is the PEM is not URL encoded, so I think it's complaining about certain characters contained there. I think likely + and /.

@gdhameeja I think we might need another placeholder, certificate_pem_urlencoded or something like that.

@gdhameeja
Copy link
Contributor

@gdhameeja gdhameeja commented Oct 1, 2020

Right. Will take this up and add the placeholder.

@mholt
Copy link
Member

@mholt mholt commented Oct 1, 2020

Why does it need to be URL encoded in an HTTP header?

@francislavoie
Copy link
Member

@francislavoie francislavoie commented Oct 1, 2020

Taking another quick look at why this happens, I think it's because of the newlines rather than +/. Seems like headers can't have newlines.

@mholt
Copy link
Member

@mholt mholt commented Oct 1, 2020

That might be more accurate. It is true that header values can't have raw new lines; I bet Apache is escaping them. Our header manipulation should probably do the same thing.

Edit: Wait, shouldn't Go (std lib) already be escaping it for us?

@francislavoie
Copy link
Member

@francislavoie francislavoie commented Oct 1, 2020

Looks like it hits this check: https://godoc.org/golang.org/x/net/http/httpguts#ValidHeaderFieldValue

in https://golang.org/src/net/http/transport.go

I'm not certain how to decipher that 🤔

Edit: Yeah isCTL() rejects \n as it's ASCII value is 10 (it rejects 0-31 and 127)

Edit2: Looks like HTTP headers use \r\n (CRLF) for newlines.

@francislavoie
Copy link
Member

@francislavoie francislavoie commented Oct 1, 2020

Looks like nginx solves this by providing two variables (one of which is deprecated):

http://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_escaped_cert

The deprecated one used \t to replace the newlines, and the new one URL encodes. I think we should provide a URL encoded option for use in headers, similarly to nginx.

Following their lead on the naming, I think certificate_pem_escaped would do.

@mholt
Copy link
Member

@mholt mholt commented Oct 1, 2020

It's weird to URL-encode a header value though.

Is there any other option that's more elegant? What if we just strip the header/footer lines and remove all the newlines? To clarify, this means that the header value is basically just the base64-encoded DER content of the certificate. That should suffice. If the upstream really wants PEM they can tack headers on the front and back and then add newlines if they want it to look pretty.

(I'll also add that communicating a certificate via a header is just plain weird.)

@francislavoie
Copy link
Member

@francislavoie francislavoie commented Oct 1, 2020

I don't see the harm in URL encoding it personally. It'll be more compatible with existing applications written to support the approach Apache and Nginx take with passing through certificates via headers.

If we do the DER approach then it should be called certificate_der, but I don't see the harm in adding certificate_pem_escaped for those that want it that way.

@mholt
Copy link
Member

@mholt mholt commented Oct 1, 2020

If we do the DER approach then it should be called certificate_der

Let's do that then. It will solve the problem and won't introduce any unnecessary weirdness into the code base. URL-encoding base64 just seems odd to me. We don't need to urlencode it: just use the base64.

I'll get around to implementing this sooner or later, unless someone would like to get to it first!

@francislavoie francislavoie mentioned this issue Oct 20, 2020
2 of 3 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.