Difference between ssl_c_verify and ssl_c_used

I have a very generic simple configuration like this:

use_backend static unless { ssl_c_verify 0 }  
use_backend dotwebha-http-10600 if { ssl_c_used }
# fall-through to holding page
default_backend static

The ssl_c_verify doesn’t seem to do anything. If I comment it out it has no effect whether or not you supply a cert.

vru-ws-webtest-b2buat:/# /usr/rbin/haproxy -vv
HA-Proxy version 1.5.18 2016/05/10
Copyright 2000-2016 Willy Tarreau <willy@haproxy.org>

Build options :
  TARGET  = solaris
  CPU     = generic
  CC      = gcc
  CFLAGS  = -m32 -O2 -g -fno-strict-aliasing -fomit-frame-pointer -DFD_SETSIZE=65536 -D_REENTRANT
  OPTIONS = USE_ZLIB=1 USE_OPENSSL=1

Default settings :
  maxconn = 2000, bufsize = 16384, maxrewrite = 8192, maxpollevents = 200

Encrypted password support via crypt(3): yes
Built with zlib version : 1.2.3
Compression algorithms supported : identity, deflate, gzip
Built with OpenSSL version : OpenSSL 1.0.2j  26 Sep 2016
Running on OpenSSL version : OpenSSL 1.0.2j  26 Sep 2016
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports prefer-server-ciphers : yes
Built without PCRE support (using libc's regex instead)

Available polling systems :
       poll : pref=200,  test result OK
     select : pref=150,  test result OK
Total: 2 (2 usable), will use poll.

vru-ws-webtest-b2buat:/# uname -a
SunOS vru-ws-webtest-b2buat 5.10 Generic_Virtual sun4v sparc sun4v
vru-ws-webtest-b2buat:/#

If you are using 2.0, you may be hitting a bug:

You need to combine it with ssl_c_used.

Show the entire configuration and the expected behavior, and I can suggest how the configuration should look like.

Here’s the entire text, except I removed all but one of the binds.

global
  # Run in the background
    daemon

  # Log locally - syslog logs to /var/haproxy/logs/haproxy.log
    log 127.0.0.1 local0

  # Sets the maximum per-process number of concurrent connections to 2400
    maxconn 2400

  # Where to find ssl certs
    crt-base /etc/haproxy
    ca-base /etc/haproxy

  # local stats socket for hatop command  - use "/usr/bin/hatop -s /var/haproxy/haproxy.sock"
    stats socket /var/haproxy/haproxy.sock mode 0600 level admin

defaults
  # Defaults section is inherited by each frontend/backend if is isn't specified
  # Log extended info in standard NSCA format
    option httplog 

  # Log null connections - 
  #  option dontlognull

  # Add x-forward-for in header to retain source
    option forwardfor

  # Log info on health checks by haproxy
    option log-health-checks

  # Enable most uptodate stats
    option  contstats

  # Times a connection attempt should be retried on a server when a connection either is refused or times out
    retries 3

  # Break cookie persistence and redistribute them to a working server should a server fail
    option redispatch

  # Max connections per service
    maxconn 200

  # Use the same config as in "global" section of cfg
    log global

  # default listening mode
    mode http

  # TCP timeout to connect to server
    timeout connect 5s

  # Timeout for client response
    timeout client 50s

  # Timeout defines how long to wait for a new HTTP request to start coming after a response was sent.
    timeout http-keep-alive 1s

  # Backend server timeout
    timeout server 50s

  # DDoS/Slowloris protection
  # Maximum accepted time to receive a complete HTTP request
    timeout http-request 15s
  # Time to wait to process requests when maxconn has been reached (before dropping request)
    timeout queue 30s
  # tarpit hold time
    timeout tarpit 1m
  # hint to number of TCP syns that the system can handle
    backlog 10000

    monitor-uri /haproxy_test

  # Set log format for analysis - this is analysed by awstats.  
  # It's vital that the capture header lines are in for each frontend so that the log format stays consistent
    log-format [%T]\ %ci\ %cp\ %ft\ %b\ %s\ %Tq\ %Tw\ %Tc\ %Tr\ %Tt\ %ST\ %B\ %{+Q}CC\ %{+Q}CS\ %tsc\ %ac\ %fc\ %bc\ %sc\ %rc\ %sq\ %bq\ %{+Q}sslv\ %{+Q}sslc\ %{+Q}hrl\ %{+Q}hsl\ %[ssl_c_verify]\ %{+Q}[ssl_c_s_dn]\ %{+Q}[ssl_c_i_dn]\ %[ssl_c_notafter]\ %{+Q}r

peers mypeers
  # Share sticky connection informaton
    peer vru-ws-webtest-b2buat 10.208.50.29:1024

frontend b2buat-https-in-ws-9600
  # SSL listener
    mode http
  # Test frontend requiring client certs
  # webservices-dot.test is set to the local IP address in /etc/hosts
  # Client cert is optional, so that we can redirect to a web page if there is a problem (eg using http!)
    bind webservices-dot.test:9600 ssl crt webservices-dot.test_300916.pem ca-file CA_2015.cer verify optional crt-ignore-err all ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
  # if there is an error with the certificate, then route the user to a holding page farm
    use_backend b2b-bad-cert-static unless { ssl_c_verify 0 }  
  # check if the certificate has been provided and give access to the application
    use_backend dotwebha-http-10600 if { ssl_c_used }
  # fallthrough to holding page again
    default_backend b2b-no-cert-static
  # Capture jboss cookie
    capture cookie JSESSIONID len 40
  # Max sessions
    rate-limit sessions 100
  # Compress responses
    compression algo gzip
    compression type text/html text/plain text/css
  # Log User agent
    capture request header User-agent len 320
  # log the beginning of the referrer
    capture request header Referer len 20
  # log the amount of data uploaded during a POST
    capture request header Content-Length len 10
  # logging the content-length is useful with "option logasap"
    capture response header Content-Length len 10
  # log the expected cache behaviour on the response
    capture response header Cache-Control len 8
  # log the URL location during a redirection
    capture response header Location len 20

backend b2b-bad-cert-static
  # This is a dropthough backend to only show local static (error) pages
    mode http
  # Detect an apacheKiller-like Attack
    acl weirdrangehdr hdr_cnt(Range) gt 10
  # Clean up the request
    reqidel ^Range if weirdrangehdr

  # Close connections to the backend while keeping frontend connection open
    option http-server-close
  # ACL url paths
    acl url_expired path /certexpired.html
    acl url_revoked path /certrevoked.html
    acl url_othererrors path /maintenance.html
  # ACL SSL cert return codes
    acl cert_expired ssl_c_verify 10
    acl cert_revoked ssl_c_verify 23
  # Add header
    reqadd X-Ssl-Error:\ 10 if cert_expired
    reqadd X-Ssl-Error:\ 23 if cert_revoked
    reqadd X-Ssl-Error:\ other if ! cert_expired ! cert_revoked
  # Redirect against ACLs above
    redirect location /certexpired.html if cert_expired ! url_expired
    redirect location /certrevoked.html if cert_revoked ! url_revoked
    redirect location /maintenance.html if ! cert_expired ! cert_revoked ! url_othererrors
  # Local apache server
    server localhost 127.0.0.1:4444 check

backend b2b-no-cert-static
  # This is a dropthough backend used to inform b2b peers that they
  # have not sent a client SSL certificate
  # All we have to do here is send a message to inform the peer
  # so no servers are configured, and the proxy will send a 503
  # Service temporarily unavailable - send a client cert next time!
    mode http
    errorfile 503 /etc/haproxy/errorfiles/503_nocert.http

backend dotwebha-http-10600
timeout server 900s
  # Insert cookie so that we can track the haproxy session
    cookie SERVERID insert indirect nocache
  # Track the session using the JSESSIONID cookie
    appsession JSESSIONID len 52 timeout 3h request-learn prefix 
  # Proxy transaction to the backend apache servers
  # option httpchk GET /heartbeat/thump HTTP/1.0
  # Balancing algorithm
    balance roundrobin
  # Sticky table
    stick-table type ip size 20k peers mypeers
  # Backend servers, 32 connections each, tracked by cookie and checked using httpchk above
    server vru-ws-webtest-b2buat 10.208.50.29:10600 maxconn 400 check cookie A id 1
  # Local backup server if above both fail
    server localhost 127.0.0.1:4444 maxconn 500 backup
  # All else fails - 503
    errorfile 503 /etc/haproxy/errorfiles/503.http  

listen admin
  # Admin server showing stats
    bind *:8080
    stats enable
    stats uri /

Ok, so what you would like to achieve is:

  • allow access if the client certificate is provided and verified without errors
  • redirect to b2b-bad-cert-static if a client certificate has been provided, but has failed validation
  • redirect to b2b-no-cert-static if no client certificate has been provided

I believe ssl_c_verify can be 0, if no cert was provided (as there is no error), so that’s why I’m saying you need to combine the two. Also, I’d turn the logic around, and make it fail-safe: only forward to the production backend if a certificate has been provided and is valid, as opposed to match some error conditions and fallback to production if it did not match.

# allow access if client certificate is provided + validated without errors
use_backend dotwebha-http-10600 if { ssl_c_used } { ssl_c_verify 0 }

# use b2b-no-cert-static if no client certificate has been provided
use_backend b2b-no-cert-static if ! { ssl_c_used  }

# use b2b-bad-cert-static if there is something wrong wwith the ssl verification
use_backend b2b-bad-cert-static if { ssl_c_used  } ! { ssl_c_verify 0 }

# no fallback; if bug/misconfiguration happened, make haproxy fail
1 Like

That makes more sense. I’ll try that out.

Thanks