As the title suggest, I have an AWS API Gateway endpoint that I want to put behind HAProxy.
This is my current HAProxy configuration
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
listen http
bind 127.0.0.1:8080
maxconn 18000
acl api_gateway path_beg /api-gateway
use_backend api-gateway-backend if api-gateway
backend api-gateway-backend
http-request set-header Host xxxxx.execute-api.ap-southeast-2.amazonaws.com
server api-gateway xxxxx.execute-api.ap-southeast-2.amazonaws.com:443
When I hit the /api-gateway
endpoint on my HAProxy, I get 400 Bad Request
. See below:
I tried to change the backend to use this server api-gateway xxxxx.execute-api.ap-southeast-2.amazonaws.com:443 ssl verify none
but I got 503 Service Unavailable
instead.
I think this could be related to SSL SNI configuration that I need to enable on HAProxy, see this forum post https://forums.aws.amazon.com/thread.jspa?threadID=240197
Best Answer
If you don't use HTTPS then CloudFront will return the
400 Bad Request
¹ error because API Gateway doesn't support HTTP.Adding
ssl verify none
enables HTTPS to the back-end, CloudFront just closes the connection, causing HAProxy to log the session state at disconnection asSC--
and return the local503 Service Unavailable
error.The solution is indeed sending Server Name Identification (SNI). Otherwise, CloudFront's front-end doesn't know which SSL certificate to offer you -- the generic
*.cloudfront.net
wildcard cert, or the one for*.execute-api.ap-southeast-2.amazonaws.com
or one of probably hundreds of thousands of other possibilities. That's what SNI does in this case -- it tells the server what name you're connecting to.The solution -- this is a single line, shown as multiple lines for clarity:
You have to use the
str()
string sample fetch here, because thesni
server keyword expects a sample fetch expression (for cases where you wanted to use the incoming request's SNI, for example).The problem in the question on the forum was not actually related to SNI -- that part of the configuration was correct. The problem there was that they failed to do what you already did correctly --
http-request set-header host ...
so that CloudFront sees the correctHost:
header.Note also the advice on the forum that this is a "very bad idea" seems distinctly misplaced.
¹ 400 CloudFront -- in some cases -- returns "Bad Request" in the body but the actual HTTP status code is
403
which corresponds toForbidden
.