Hi Willy,

I cant replicate your results here....

I cloned from git and built the package with the debian/ubuntu build scripts from https://launchpad.net/~vbernat/+archive/ubuntu/haproxy-1.7 ... updating the changelog to add a 1.8-dev2 version and calling ./debian/rules binary to build the package.

The git log shows:

   commit 2ab88675ecbf960a6f33ffe9c6a27f264150b201
   Author: Willy Tarreau <w...@1wt.eu>
   Date:   Wed Jul 5 18:23:03 2017 +0200

        MINOR: ssl: compare server certificate names to the SNI on
   outgoing connections


So I'm sure its in there unless a ./debian/rules binary build is breaking something.

this is my config.

haproxy-min-sni.cfg

global
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private

ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
    ssl-default-bind-options no-sslv3

defaults
    mode    http
    option    httplog
    option    dontlognull
    option    forwardfor
    option    http-server-close
    option  log-health-checks
    timeout connect 5000
    timeout client  50000
    timeout server  50000

frontend www-https
bind :::443 v4v6 ssl crt /etc/haproxy/certs/www.example.ca.pem crt /etc/haproxy/certs/
    reqadd X-Forwarded-Proto:\ https
    use_backend www-backend-https

backend www-backend-https
    http-response set-header X-Server %s
    balance roundrobin
server app2 10.10.0.5:443 ssl verify required sni ssl_fc_sni ca-file /etc/ssl/certs/ca-certificates.crt check check-ssl

--

/usr/sbin/haproxy -d -f haproxy-min-sni.cfg

--

Loading ssltest-broken.example.ca (that the backend server has no cert for and so serves from the default tls vhost (app2.example.ca in this case))... This shows a secure page in the browser, however the connection to the backend cannot be secure.

[WARNING] 205/165327 (16816) : Health check for server www-backend-https/app2 succeeded, reason: Layer6 check passed, check duration: 5ms, status: 3/3 UP. 00000000:www-https.accept(0004)=0007 from [::ffff:<redacted>:36565] ALPN=<none> 00000001:www-https.accept(0004)=0006 from [::ffff:<redacted>:45955] ALPN=<none> 00000002:www-https.accept(0004)=0005 from [::ffff:<redacted>:44474] ALPN=<none>
00000000:www-https.clireq[0007:ffffffff]: GET / HTTP/1.1
00000000:www-https.clihdr[0007:ffffffff]: Host: ssltest-broken.example.ca
00000000:www-https.clihdr[0007:ffffffff]: Connection: keep-alive
00000000:www-https.clihdr[0007:ffffffff]: Cache-Control: max-age=0
00000000:www-https.clihdr[0007:ffffffff]: Upgrade-Insecure-Requests: 1
00000000:www-https.clihdr[0007:ffffffff]: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 00000000:www-https.clihdr[0007:ffffffff]: Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
00000000:www-https.clihdr[0007:ffffffff]: Accept-Encoding: gzip, deflate, br
00000000:www-https.clihdr[0007:ffffffff]: Accept-Language: en-US,en;q=0.8
00000000:www-backend-https.srvrep[0007:0008]: HTTP/1.1 200 OK
00000000:www-backend-https.srvhdr[0007:0008]: Date: Tue, 25 Jul 2017 16:49:19 GMT
00000000:www-backend-https.srvhdr[0007:0008]: Server: Apache
00000000:www-backend-https.srvhdr[0007:0008]: Vary: Accept-Encoding
00000000:www-backend-https.srvhdr[0007:0008]: Content-Encoding: gzip
00000000:www-backend-https.srvhdr[0007:0008]: Content-Length: 515
00000000:www-backend-https.srvhdr[0007:0008]: Connection: close
00000000:www-backend-https.srvhdr[0007:0008]: Content-Type: text/html; charset=UTF-8
00000000:www-backend-https.srvcls[0007:0008]


Loading ssltest.example.ca
[WARNING] 205/165327 (16816) : Health check for server www-backend-https/app2 succeeded, reason: Layer6 check passed, check duration: 5ms, status: 3/3 UP. 00000000:www-https.accept(0004)=0005 from [::ffff:<redacted>:45095] ALPN=<none> 00000001:www-https.accept(0004)=0006 from [::ffff:<redacted>:41897] ALPN=<none> 00000002:www-https.accept(0004)=0007 from [::ffff:<redacted>:37526] ALPN=<none>
00000000:www-https.clireq[0005:ffffffff]: GET / HTTP/1.1
00000000:www-https.clihdr[0005:ffffffff]: Host: ssltest.example.ca
00000000:www-https.clihdr[0005:ffffffff]: Connection: keep-alive
00000000:www-https.clihdr[0005:ffffffff]: Upgrade-Insecure-Requests: 1
00000000:www-https.clihdr[0005:ffffffff]: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 00000000:www-https.clihdr[0005:ffffffff]: Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
00000000:www-https.clihdr[0005:ffffffff]: Accept-Encoding: gzip, deflate, br
00000000:www-https.clihdr[0005:ffffffff]: Accept-Language: en-US,en;q=0.8
00000000:www-backend-https.srvrep[0005:0008]: HTTP/1.1 200 OK
00000000:www-backend-https.srvhdr[0005:0008]: Date: Tue, 25 Jul 2017 16:53:33 GMT
00000000:www-backend-https.srvhdr[0005:0008]: Server: Apache
00000000:www-backend-https.srvhdr[0005:0008]: Vary: Accept-Encoding
00000000:www-backend-https.srvhdr[0005:0008]: Content-Encoding: gzip
00000000:www-backend-https.srvhdr[0005:0008]: Content-Length: 458
00000000:www-backend-https.srvhdr[0005:0008]: Connection: close
00000000:www-backend-https.srvhdr[0005:0008]: Content-Type: text/html; charset=UTF-8
00000000:www-backend-https.srvcls[0005:0008]

--

Via the haproxy box.

#curl -D - https://ssltest-broken.example.ca
curl: (51) SSL: certificate subject name (app2.example.ca) does not match target host name 'ssltest-broken.example.ca'

# curl -D - https://ssltest.example.ca
HTTP/1.1 200 OK
Date: Tue, 25 Jul 2017 16:54:39 GMT
Server: Apache
Vary: Accept-Encoding
Content-Length: 1044
Content-Type: text/html; charset=UTF-8


--


# printf "GET / HTTP/1.0\r\n\r\n" | openssl s_client -connect 10.10.0.5:443 -quiet -servername ssltest.example.ca
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = ssltest.example.ca
verify return:1


# printf "GET / HTTP/1.0\r\n\r\n" | openssl s_client -connect 10.10.0.5:443 -quiet -servername ssltest-broken.example.ca
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = app2.example.ca
verify return:1


So as you can see, ssltest-broken is hitting the app2 default vhost/cert. The backend server has no knowledge of the ssltest-broken certificate. The verifyhost is /not/ checked between the backend and the haproxy. Further, I think the health check should probably fail too because its trying to load via the ip-as-hostname and the cert im using doesnt have the IP in it. So that should fail hostname check too.

I'm confident that the verifyhost is not being done... I suspect your test case is failing because the dom4 is totally unknown to the haproxy, whereas in my case, the haproxy has a cert for ssltest-broken but the backend does not.

--

Kevin


On 2017-07-25 5:26 AM, Willy Tarreau wrote:
Hi again Kevin,

On Tue, Jul 25, 2017 at 07:26:07AM +0200, Willy Tarreau wrote:
frontend www-https
     bind :::443 v4v6 ssl crt /etc/haproxy/certs/default.example.ca.pem crt
/etc/haproxy/certs/
     use_backend www-backend-https

backend www-backend-https
     server app default.example.ca:443 ssl verify required sni ssl_fc_sni
ca-file /etc/ssl/certs/ca-certificates.crt check check-ssl

If you visit https://should-be-broken.example.ca you will get the page for
default.example.ca, but the browser/visitor will show the
should-be-broken.example.ca cert from the haproxy and the page will appear
secure, despite the backend apache instance having no access to
should-be-broken's virtual host or certificate and serving a certificate for
default.example.ca to the haproxy.
Thanks, I'll retry it. I'm surprized because what you describe here is
*exactly* what I did and it worked fine for me, I remember getting a 503
when connecting with the wrong name. But obviously there must be a
difference so I'll try to find it.
So I tried again to replicate it and cannot confirm your issues. Here's
what I've done :
   - I'm having haproxy serve as the origin because I don't have an apache
     instance running and don't know how to set it up so I'm not going to
     waste my time on it ;

   - this origin server responds on 3 different domain names and thus
     serves 3 different certificates (dom{1,2,3}.example.com).

   - a front gateway responds on a dummy cert, and connects to the server
     passing the front connection's SNI to the server.

   - the client connects to this front gateway with 4 different names, the
     3 supported ones and an unsupported one

What I'm seeing is that the first 3 domains work well and the 4th fails.

Here's the config :

   listen gateway
         mode http
         bind :4430 ssl crt rsa2048.pem
         server app 127.0.0.1:4431 ssl sni ssl_fc_sni verify required ca-file 
ca.pem check check-ssl

   frontend origin
         mode http
         bind :4431 ssl crt dom1.example.com.pem crt dom2.example.com.pem crt 
dom3.example.com.pem
         http-request redirect location /called-with-%[ssl_fc_sni]

Command to start this and output :
   $ ./haproxy -d -f sni-srv-bug.cfg

Test with dom1..dom3 :
   $ printf "GET / HTTP/1.0\r\n\r\n" | openssl s_client -connect 127.0.0.1:4430 
-quiet -servername dom1.example.com

Haproxy's output :
   00000004:origin.accept(0005)=0007 from [127.0.0.1:36664] ALPN=<none>
   00000004:origin.clicls[0007:ffffffff]
   00000004:origin.closed[0007:ffffffff]
   00000005:gateway.accept(0004)=0006 from [127.0.0.1:56942] ALPN=<none>
   00000005:gateway.clireq[0006:ffffffff]: GET / HTTP/1.0
   00000006:origin.accept(0005)=0008 from [127.0.0.1:36668] ALPN=<none>
   00000006:origin.clireq[0008:ffffffff]: GET / HTTP/1.0
   00000006:origin.clicls[0008:ffffffff]
   00000006:origin.closed[0008:ffffffff]
   00000005:gateway.srvrep[0006:0007]: HTTP/1.1 302 Found
   00000005:gateway.srvhdr[0006:0007]: Cache-Control: no-cache
   00000005:gateway.srvhdr[0006:0007]: Content-length: 0
   00000005:gateway.srvhdr[0006:0007]: Location: /called-with-dom1.example.com
   00000005:gateway.srvhdr[0006:0007]: Connection: close
   00000005:gateway.srvcls[0006:0007]
   00000005:gateway.clicls[0006:0007]
   00000005:gateway.closed[0006:0007]
   00000007:origin.accept(0005)=0007 from [127.0.0.1:36670] ALPN=<none>
   00000007:origin.clicls[0007:ffffffff]
   00000007:origin.closed[0007:ffffffff]

OpenSSL output :
   depth=0 C = FR, ST = Some-State, O = test, CN = localhost
   verify error:num=18:self signed certificate
   verify return:1
   depth=0 C = FR, ST = Some-State, O = test, CN = localhost
   verify return:1
   HTTP/1.1 302 Found
   Cache-Control: no-cache
   Content-length: 0
   Location: /called-with-dom1.example.com
   Connection: close
Test with dom4:
   $ printf "GET / HTTP/1.0\r\n\r\n" | openssl s_client -connect 127.0.0.1:4430 
-quiet -servername dom4.example.com

Haproxy's output :
   00000000:origin.accept(0005)=0007 from [127.0.0.1:36640] ALPN=<none>
   00000000:origin.clicls[0007:ffffffff]
   00000000:origin.closed[0007:ffffffff]
   00000001:gateway.accept(0004)=0006 from [127.0.0.1:56918] ALPN=<none>
   00000001:gateway.clireq[0006:ffffffff]: GET / HTTP/1.0
   fd[0007] OpenSSL error[0x14090086] ssl3_get_server_certificate: certificate 
verify failed
   fd[0008] OpenSSL error[0x14094438] ssl3_read_bytes: tlsv1 alert internal 
error
   00000002:origin.accept(0005)=0008 from [127.0.0.1:36646] ALPN=<none>
   00000002:origin.clicls[0008:ffffffff]
   00000002:origin.closed[0008:ffffffff]
   fd[0007] OpenSSL error[0x14090086] ssl3_get_server_certificate: certificate 
verify failed
   fd[0008] OpenSSL error[0x14094438] ssl3_read_bytes: tlsv1 alert internal 
error
   fd[0007] OpenSSL error[0x14090086] ssl3_get_server_certificate: certificate 
verify failed
   fd[0008] OpenSSL error[0x14094438] ssl3_read_bytes: tlsv1 alert internal 
error
   00000003:origin.accept(0005)=0008 from [127.0.0.1:36652] ALPN=<none>
   00000003:origin.clicls[0008:ffffffff]
   00000003:origin.closed[0008:ffffffff]
   fd[0007] OpenSSL error[0x14090086] ssl3_get_server_certificate: certificate 
verify failed
   fd[0008] OpenSSL error[0x14094438] ssl3_read_bytes: tlsv1 alert internal 
error
   00000001:gateway.clicls[0006:adfd]
   00000001:gateway.closed[0006:adfd]

OpenSSL output :
   depth=0 C = FR, ST = Some-State, O = test, CN = localhost
   verify error:num=18:self signed certificate
   verify return:1
   depth=0 C = FR, ST = Some-State, O = test, CN = localhost
   verify return:1
   HTTP/1.0 503 Service Unavailable
   Cache-Control: no-cache
   Connection: close
   Content-Type: text/html
<html><body><h1>503 Service Unavailable</h1>
   No server is available to handle this request.
   </body></html>

So as you can see it works as expected here. I hardly know what else
can be checked. What exact version are you using ? Are you certain it
contains the patch I mentionned ?

Regards,
Willy

Reply via email to