HAProxy redirects when it should not

Hi,
I have the following haproxy config where I don’t want to redirect from http to https for /.well-known/acme-challenge/. But for all other paths I want to redirect from http to https. But it seems that it is getting redirected for all paths including /.well-known/acme-challenge/ . Can you help me understand what I am doing wrong here?

haproxy.cfg:
----
# HAProxy configuration generated by https://github.com/appscode/voyager
# DO NOT EDIT!
global
  daemon
  stats socket /tmp/haproxy
  server-state-file global
  server-state-base /var/state/haproxy/
  # log using a syslog socket
  log /dev/log local0 info
  log /dev/log local0 notice
  tune.ssl.default-dh-param 2048
  ssl-default-bind-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:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
defaults
  log global
  # https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-option%20abortonclose
  # https://github.com/appscode/voyager/pull/403
  option dontlognull
  option http-server-close
  # Timeout values
  timeout client 50s
  timeout client-fin 50s
  timeout connect 50s
  timeout server 20m
  timeout tunnel 50s
  # Configure error files
  # default traffic mode is http
  # mode is overwritten in case of tcp services
  mode http
frontend http-0_0_0_0-80
  bind *:80
  mode http
  option httplog
  option forwardfor
  acl is_proxy_https hdr(X-Forwarded-Proto) https
  acl acl_:.well-known-acme-challenge path_beg /.well-known/acme-challenge/
  use_backend voyager-operator.voyager:56791 if  acl_:.well-known-acme-challenge
  acl acl_search-rpc-kube.example.com hdr(host) -i search-rpc-kube.example.com
  acl acl_search-rpc-kube.example.com hdr(host) -i search-rpc-kube.example.com:80
  use_backend production-rpc.production-rpc:80 if acl_search-rpc-kube.example.com
  acl acl_nextcloud.example.com hdr(host) -i nextcloud.example.com
  acl acl_nextcloud.example.com hdr(host) -i nextcloud.example.com:80
  use_backend nextcloud.nextcloud:80 if acl_nextcloud.example.com
  acl acl_internal-services.example.com hdr(host) -i internal-services.example.com
  acl acl_internal-services.example.com hdr(host) -i internal-services.example.com:80
  use_backend graphie-to-png.graphie-to-png:8765-ebc10c77500be47b8c6f25c6a9db26f6 if acl_internal-services.example.com
  acl acl_graphie-to-png.kasandbox.org hdr(host) -i graphie-to-png.kasandbox.org
  acl acl_graphie-to-png.kasandbox.org hdr(host) -i graphie-to-png.kasandbox.org:80
  use_backend graphie-to-png.graphie-to-png:8765-16e12e4030e576bb5d488b97792cecc9 if acl_graphie-to-png.kasandbox.org
  acl acl_error-monitor-db.example.com hdr(host) -i error-monitor-db.example.com
  acl acl_error-monitor-db.example.com hdr(host) -i error-monitor-db.example.com:80
  redirect scheme https code 308 if ! is_proxy_https acl_error-monitor-db.example.com
  acl acl_crowdin-gcs-sync.example.com hdr(host) -i crowdin-gcs-sync.example.com
  acl acl_crowdin-gcs-sync.example.com hdr(host) -i crowdin-gcs-sync.example.com:80
  redirect scheme https code 308 if ! is_proxy_https acl_crowdin-gcs-sync.example.com
  acl acl_buildmaster.example.com hdr(host) -i buildmaster.example.com
  acl acl_buildmaster.example.com hdr(host) -i buildmaster.example.com:80
  redirect scheme https code 308 if ! is_proxy_https acl_buildmaster.example.com
  acl acl_alerta.example.com hdr(host) -i alerta.example.com
  acl acl_alerta.example.com hdr(host) -i alerta.example.com:80
  redirect scheme https code 308 if ! is_proxy_https acl_alerta.example.com```
backend voyager-operator.voyager:56791
  server pod-voyager-operator-5d4cc8948f-qffjc 10.4.0.126:56791
backend production-rpc.production-rpc:80
  server pod-production-rpc-b584c5769-sldgc 10.128.0.8:80
backend nextcloud.nextcloud:80
  server pod-nextcloud-6c4f588755-czfxg 10.128.0.3:80
backend graphie-to-png.graphie-to-png:8765-ebc10c77500be47b8c6f25c6a9db26f6
  server pod-graphie-to-png-76dfbd5549-27htk 10.128.0.7:8765
backend graphie-to-png.graphie-to-png:8765-16e12e4030e576bb5d488b97792cecc9
  server pod-graphie-to-png-76dfbd5549-27htk 10.128.0.7:8765
frontend http-0_0_0_0-443
  bind *:443  ssl no-sslv3 no-tlsv10 no-tls-tickets crt /etc/ssl/private/haproxy/tls/  alpn http/1.1
  # Mark all cookies as secure
  rsprep ^Set-Cookie:\ (.*) Set-Cookie:\ \1;\ Secure
  # Add the HSTS header with a 6 month default max-age
  http-response set-header Strict-Transport-Security max-age=15768000
  mode http
  option httplog
  option forwardfor
  acl is_proxy_https hdr(X-Forwarded-Proto) https
  acl acl_error-monitor-db.example.com hdr(host) -i error-monitor-db.example.com
  acl acl_error-monitor-db.example.com hdr(host) -i error-monitor-db.example.com:443
  use_backend error-monitor-db.error-monitor-db:9340 if acl_error-monitor-db.example.com
  acl acl_crowdin-gcs-sync.example.com hdr(host) -i crowdin-gcs-sync.example.com
  acl acl_crowdin-gcs-sync.example.com hdr(host) -i crowdin-gcs-sync.example.com:443
  use_backend crowdin-gcs-sync.crowdin-gcs-sync:9999 if acl_crowdin-gcs-sync.example.com
  acl acl_buildmaster.example.com hdr(host) -i buildmaster.example.com
  acl acl_buildmaster.example.com hdr(host) -i buildmaster.example.com:443
  use_backend buildmaster.buildmaster:80 if acl_buildmaster.example.com
  acl acl_alerta.example.com hdr(host) -i alerta.example.com
  acl acl_alerta.example.com hdr(host) -i alerta.example.com:443
  use_backend alerta.alerta:8080 if acl_alerta.example.com
backend error-monitor-db.error-monitor-db:9340
  server pod-error-monitor-db-cb7bbcdb8-4sdrs 10.128.0.3:9340
backend crowdin-gcs-sync.crowdin-gcs-sync:9999
  server pod-crowdin-gcs-sync-5b684f687-xv87k 10.128.0.4:9999
backend buildmaster.buildmaster:80
  server pod-buildmaster-cb8cbc586-mf9hm 10.128.0.2:80
backend alerta.alerta:8080
  server pod-alerta-fbbdbbc9c-b5rh4 10.128.0.7:8080
frontend tcp-0_0_0_0-3322
  bind *:3322
  mode tcp
  default_backend scp.scp:3322
backend scp.scp:3322
  mode tcp
  server pod-scp-9d464fd77-pcbwj 10.128.0.7:3322

~ $ curl -vv http://alerta.example.com/.well-known/acme-challenge/4QN4jFM7lrp1AxdX7DOapUmleelvzeZJKA5fYCW_kA8
*   Trying 35.184.192.240...
* TCP_NODELAY set
* Connected to alerta.example.com (35.184.192.240) port 80 (#0)
> GET /.well-known/acme-challenge/4QN4jFM7lrp1AxdX7DOapUmleelvzeZJKA5fYCW_kA8 HTTP/1.1
> Host: alerta.example.com
> User-Agent: curl/7.58.0
> Accept: */*
> 
< HTTP/1.1 308 Permanent Redirect
< Content-length: 0
< Location: https://alerta.example.com/.well-known/acme-challenge/4QN4jFM7lrp1AxdX7DOapUmleelvzeZJKA5fYCW_kA8
< 
* Connection #0 to host alerta.example.com left intact

Hi Tamal,

First, I can see a lot of configuration inaccuracy, which may lead to a lot of warnings when parsing the configuration :slight_smile:
My first advice is to fix all of them and to use the most appropriate configuration settings.

To answer your question, you need to add an exception to your redirect rule:
First, let’s have a look at your current rule:
redirect scheme https code 308 if ! is_proxy_https acl_alerta.example.comThis rule should fail at configuration parsing, saying the ACL acl_alerta.example.com does not exist.
Well, this might be a discourse bug :slight_smile:
So let’s analyse this one:
redirect scheme https code 308 if ! is_proxy_https acl_alerta.example.com
it says redirect to https anything which is not yet ciphered and whose host header is diffent than alerta.example.com.

So the quick way to fix it is to append an other negative ACL:
redirect scheme https code 308 if ! is_proxy_https acl_alerta.example.com ! acl_:.well-known-acme-challenge

Note: this acl “acl_:.well-known-acme-challenge” is also poorly named :slight_smile:

In a general manner, I would use http-request ruleset, it’s much more predictible.

Hey Baptiste, nice to hear from you!

I cleaned up the config map, as I got over some slack. After cleanup I only see the following errors:

$ haproxy -c -f /home/tamal/Desktop/acme-bug.txt
[WARNING] 159/034349 (8088) : parsing [/home/tamal/Desktop/acme-bug.txt:51] : a 'redirect' rule placed after a 'use_backend' rule will still be processed before.
[WARNING] 159/034349 (8088) : parsing [/home/tamal/Desktop/acme-bug.txt:54] : a 'redirect' rule placed after a 'use_backend' rule will still be processed before.
[WARNING] 159/034349 (8088) : parsing [/home/tamal/Desktop/acme-bug.txt:57] : a 'redirect' rule placed after a 'use_backend' rule will still be processed before.
[WARNING] 159/034349 (8088) : parsing [/home/tamal/Desktop/acme-bug.txt:60] : a 'redirect' rule placed after a 'use_backend' rule will still be processed before.
[ALERT] 159/034349 (8088) : parsing [/home/tamal/Desktop/acme-bug.txt:72] : 'bind *:443' : unable to load SSL certificate file '/etc/ssl/private/haproxy/tls/' file does not exist.
[ALERT] 159/034349 (8088) : Error(s) found in configuration file : /home/tamal/Desktop/acme-bug.txt
[ALERT] 159/034349 (8088) : Fatal errors found in configuration.

Let’s take a redirect rule:

redirect scheme https code 308 if ! is_proxy_https acl_alerta.example.com

I am reading that this rules applies when (! is_proxy_https) AND acl_alerta.example.com . Am I right? But since redirect rules apply before use_backend rule of acl_:.well-known-acme-challenge, http -> https redirection is happening for /.well-known/acme-challenge/ . I understand that adding the negative !acl_:.well-known-acme-challenge will make this redir rule to fail resulting in desired behavior.

Does http-request redirect apply in order? If we switch to that, do I still have to use the negative rule?

Regarding names, these are auto generated from Kubernetes ingress so somewhat ugly. :frowning:

http-request rules are always applied before the use-backend.

You could do the following ruleset:

http-request allow if acl_:.well-known-acme-challenge # rules processing will stop here for URLs matching this ACL, then use-backend will be applied for them

http-request redirect scheme https code 308 if ! is_proxy_https acl_alerta.example.com

Check the dock for the “allow” action:

https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#4.2-http-request

Baptiste

1 Like

Thanks. http-request allow will be easier for us. This should do the trick.