Skip to main content
Glama
haproxy.cfg22.8 kB
global stats socket /var/run/haproxy/haproxy.sock mode 660 level admin expose-fd listeners maxconn "$LUNAR_MAXCONN" lua-prepend-path /etc/haproxy/lua/json.lua lua-prepend-path /etc/haproxy/lua/http.lua lua-load /etc/haproxy/lua/lunar.lua pidfile /var/run/haproxy/haproxy.pid tune.bufsize "$GATEWAY_BUFFER_SIZE" defaults log global log 127.0.0.1:5141 len 3072 local0 info retries 0 timeout connect "$LUNAR_CONNECT_TIMEOUT_SEC"s timeout client "$LUNAR_CLIENT_TIMEOUT_SEC"s timeout server "$LUNAR_SERVER_TIMEOUT_SEC"s log-format '{ "internal": true, "timestamp":%Ts%ms, "duration":%Tr, "total_duration":%Ta, "method":"%HM", "url":"%[dst]%HU", "request_headers":"%hr", "response_headers":"%hs", "status_code":%ST }' listen stats # Define a listen section called "stats" bind :9000 # Listen on localhost:9000 mode http stats enable # Enable stats page stats hide-version # Hide HAProxy version stats realm Haproxy\ Statistics # Title text for popup window stats uri /metrics # Stats URI listen healthcheck bind *:${LUNAR_HEALTHCHECK_PORT} mode http log-format '{ "internal": true, "timestamp":%Ts%ms, "duration":%Tr, "total_duration":%Ta, "method":"%HM", "uri":"%HU", "url":"%[dst]%HU", "request_headers":"%hr", "response_headers":"%hs", "path":"%HP", "status_code":%ST }' acl path_healthcheck path /healthcheck http-request deny status 404 unless path_healthcheck http-request return status 200 content-type text/plain lf-string "proxy is up" frontend http-async-in mode http bind *:${ASYNC_SERVICE_BIND_PORT} # _ # Lunar_Async_HTTPS_Binder # _ # option h1-case-adjust-bogus-client log-format '{ "internal": true, "timestamp":%Ts%ms, "duration":%Tr, "total_duration":%Ta, "method":"%HM", "uri":"%HU", "url":"%[dst]%HU", "request_headers":"%hr", "response_headers":"%hs", "path":"%HP", "status_code":%ST }' acl skip_all var(proc.skip_all) -m found acl proxy_shutting_down var(proc.disable_requests) -m found acl path_is_retrieve path /retrieve acl has_query_params urlp(sequence_id) -m found acl is_retrieve_hdr_found req.hdr(x-lunar-async-retrieve) -m found http-request deny status 503 content-type text/plain lf-string "Lunar Gateway is shuting down" hdr x-lunar-error 10 if proxy_shutting_down http-request set-var(txn.lunar_request_id) uuid() unique-id-format %[var(txn.lunar_request_id)] http-request set-header x-lunar-sequence-id %[var(txn.lunar_request_id)] if !skip_all http-response set-header x-lunar-sequence-id %[var(txn.lunar_request_id)] if !skip_all default_backend async_service frontend http-in mode http bind *:${BIND_PORT} # _ # Lunar_HTTPS_Binder # _ # option http-buffer-request # Buffer the request to allow for SPOE processing option h1-case-adjust-bogus-client acl skip_all var(proc.skip_all) -m found acl proxy_shutting_down var(proc.disable_requests) -m found log-format '{ "termination_state": "%ts","internal": %[var(txn.is_internal)], "request_id": "%[var(txn.lunar_request_id)]", "timestamp":%Ts%ms, "duration":%Tr, "total_duration":%Ta, "method":"%HM", "url":"%[var(txn.url)]", "host":"%[var(txn.host)]", "path":"%HP", "status_code":%ST, "request_active_remedies":%[var(txn.lunar.request_active_remedies)], "response_active_remedies":%[var(txn.lunar.response_active_remedies)], "interceptor":"%[var(txn.interceptor)]", "consumer_tag":"%[var(txn.lunar_consumer_tag)]", "x_lunar_error": "%[var(txn.x_lunar_error)]", "error_in_body": "%[var(txn.error_in_body)]" }' # Define an ACL to check for the x-lunar-internal header acl is_internal req.hdr(x-lunar-internal) -m str true # Set txn.is_internal to "true" if the header is present, "false" otherwise http-request set-var(txn.is_internal) str("true") if is_internal http-request set-var(txn.is_internal) str("false") unless is_internal http-request deny status 503 content-type text/plain lf-string "Lunar Gateway is shuting down" hdr x-lunar-error 10 if proxy_shutting_down http-request set-var(txn.lunar_request_id) req.hdr(x-lunar-req-id) if { req.hdr(x-lunar-req-id) -m found } http-request set-var(txn.lunar_request_id) uuid() unless { req.hdr(x-lunar-req-id) -m found } unique-id-format %[var(txn.lunar_request_id)] http-request set-var(txn.lunar_sequence_id) req.hdr(x-lunar-sequence-id) if { req.hdr(x-lunar-sequence-id) -m found } http-request set-var(txn.lunar_sequence_id) unique-id unless { req.hdr(x-lunar-sequence-id) -m found } http-response set-header x-lunar-sequence-id %[var(txn.lunar_sequence_id)] if !skip_all http-request set-var(txn.lunar.method) method http-request set-var(txn.lunar.request_active_remedies) str({}) unless { var(txn.lunar.request_active_remedies) -m found } http-request set-var(txn.lunar.response_active_remedies) str({}) unless { var(txn.lunar.response_active_remedies) -m found } # On Proxy generated error we want to add the x-lunar-error header to notify the Interceptor. # Reference: https://docs.haproxy.org/2.6/configuration.html#4-http-error http-error status 503 content-type text/plain string "The endpoint cannot be reached" hdr x-lunar-error 2 http-error status 504 content-type text/plain string "Gateway timeout" hdr x-lunar-error 3 http-error status 404 content-type text/plain string "Endpoint not found" hdr x-lunar-error 4 acl redirection_by_query_params env(LUNAR_REDIRECTION_BY_QUERY_PARAMS) -m str 1 # extract routing info from query params if needed and remove them from request http-request set-var(txn.host) urlp(lunar_original_host) if redirection_by_query_params http-request set-var(txn.scheme) urlp(lunar_original_scheme) if redirection_by_query_params http-request set-var(txn.lunar_consumer_tag) urlp(lunar_consumer_tag) if redirection_by_query_params http-request set-header Host %[var(txn.host)] if redirection_by_query_params http-request set-query %[query,regsub(&?lunar_original_host=[^&]*,)] if redirection_by_query_params http-request set-query %[query,regsub(&?lunar_original_scheme=[^&]*,)] if redirection_by_query_params http-request set-query %[query,regsub(&?lunar_consumer_tag=[^&]*,)] if redirection_by_query_params acl host-found var(txn.host) -m found http-request set-var(txn.x_lunar_error) str(1) if !host-found redirection_by_query_params http-request set-var(txn.error_in_body) str("Could not locate query params lunar_original_host and lunar_original_scheme") if !host-found redirection_by_query_params http-request deny status 503 content-type text/plain lf-string "Could not locate query params lunar_original_host and lunar_original_scheme" hdr x-lunar-error 1 if !host-found redirection_by_query_params # extract routing info from headers if needed and remove lunar-specific ones from request acl x-lunar-host-found req.hdr(x-lunar-host) -m found # deny request if x-lunar-host header is not found and redirection_by_query_params is not enabled http-request set-var(txn.x_lunar_error) str(1) if !x-lunar-host-found !redirection_by_query_params http-request set-var(txn.error_in_body) str("Could not locate header x-lunar-host") if !x-lunar-host-found !redirection_by_query_params http-request deny status 401 content-type text/plain lf-string "Could not locate header x-lunar-host" hdr x-lunar-error 1 if !x-lunar-host-found !redirection_by_query_params http-request set-var(txn.host) req.hdr(x-lunar-host) if !redirection_by_query_params x-lunar-host-found http-request set-var(txn.host) req.hdr(Host) if !redirection_by_query_params !x-lunar-host-found http-request set-header Host %[var(txn.host)] if !redirection_by_query_params x-lunar-host-found http-request set-var(txn.scheme) req.hdr(x-lunar-scheme) unless redirection_by_query_params http-request del-header x-lunar-scheme http-request set-var(txn.lunar_consumer_tag) hdr(x-lunar-consumer-tag) unless redirection_by_query_params # txn.url is set to the full URL excluding scheme, port and query params (e.g. domain.com/path/to/resource) http-request set-var(txn.path) path http-request set-var(txn.url) var(txn.host),concat(,txn.path) # txn.lunar_interceptor http-request set-var(txn.interceptor) str("lunar-direct/0") unless { req.hdr(x-lunar-interceptor) -m found } http-request set-var(txn.interceptor) req.hdr(x-lunar-interceptor) if { req.hdr(x-lunar-interceptor) -m found } http-request del-header x-lunar-interceptor if { req.hdr(x-lunar-interceptor) -m found } # Block the request if it is not in allowed domains. By default all domains are allowed (.*). acl allowed_domain var(txn.host) -m reg -f /etc/haproxy/allowed_domains.lst http-request set-var(txn.x_lunar_error) str(6) if !allowed_domain http-request set-var(txn.error_in_body) str("Host is not in allow list") if !allowed_domain http-request deny if !allowed_domain # Block the request if it is in blocked domains acl blocked_domain var(txn.host) -m reg -f /etc/haproxy/blocked_domains.lst http-request set-var(txn.x_lunar_error) str(7) if blocked_domain http-request set-var(txn.error_in_body) str("Host is in block list") if blocked_domain http-request deny if blocked_domain # if no port in host string, it will return 0. (https://bit.ly/3ly3kGw) http-request set-var(txn.dst_port) var(txn.host),port_only acl dst_port_not_found var(txn.dst_port) -m int 0 acl is_https_scheme var(txn.scheme) -m str https acl use_mtls var(txn.host),lower,map_reg(/etc/haproxy/maps/mtls.map) -m found http-request set-var(txn.dst_port) int(443) if dst_port_not_found is_https_scheme http-request set-var(txn.dst_port) int(80) if dst_port_not_found !is_https_scheme http-request set-var(txn.x_lunar_error) str(5) if { var(txn.dst_port) -m int 0 } http-request set-var(txn.error_in_body) str("Could not resolve port") if { var(txn.dst_port) -m int 0 } http-request deny status 503 content-type text/plain lf-string "Could not resolve port" hdr x-lunar-error 5 if { var(txn.dst_port) -m int 0 } # Check if the host is an IPv4 address and resolve it if it is not acl is_host_ipv4 var(txn.host),host_only -i -m reg (\d+)\.(\d+)\.(\d+)\.(\d+) # Lunar SPOE acl is_managed var(proc.manage_all) -m found acl is_managed capture.req.method,concat(":::",txn.url),map_reg(/etc/haproxy/maps/endpoints.map) -m found acl body_required var(proc.body_from_all) -m found acl body_required capture.req.method,concat(":::",txn.url),map_reg(/etc/haproxy/maps/spoe_with_body.map) -m found acl is_res_error res.hdr(x-lunar-error) -m found acl is_early_response var(txn.lunar.return_early_response) -m bool # Capture request payload for requests that could be retried acl capture_required capture.req.method,concat(":::",txn.url),map_reg(/etc/haproxy/maps/req_capture.map) -m found acl capture_required var(proc.capture_all) -m found # <len> is the maximum number of characters to extract from the value and # report in the logs. The string will be truncated on the right if # it exceeds. declare capture request len 8000000 if capture_required is_managed http-request capture req.body id 0 if capture_required is_managed body_required # Store that captured body in txn.lunar.request_body http-request set-var(txn.lunar.request_body) capture.req.hdr(0) if { capture.req.hdr(0) -m found } !skip_all capture_required is_managed body_required http-request set-var(txn.lunar.request_headers_str) req.hdrs if !skip_all capture_required is_managed http-request set-var(txn.lunar.query_params) query if !skip_all capture_required is_managed filter spoe engine lunar config "${LUNAR_SPOE_CONFIG}" http-request send-spoe-group lunar lunar-request-group if !skip_all !body_required is_managed http-request send-spoe-group lunar lunar-full-request-group if !skip_all is_managed body_required # Modify request (apply modifications and send back to proxy - localhost:8000) acl is_looped_req req.hdr(x-lunar-lua-handled) -m found http-request set-var(req.is_looped) str(true) if is_looped_req http-request use-service lua.modify_request if !skip_all { var(req.lunar.modify_request) -m bool } !{ var(req.is_looped) -m found } http-response wait-for-body time 10000 if !skip_all is_managed body_required # Max time to wait for response body is 10 seconds http-response send-spoe-group lunar lunar-response-group if !is_res_error !skip_all !body_required !is_early_response is_managed ! { var(txn.lua_handled) -m found } http-response send-spoe-group lunar lunar-full-response-group if !is_res_error !skip_all !is_early_response is_managed body_required ! { var(txn.lua_handled) -m found } # Modify headers http-request lua.modify_headers if !skip_all { var(req.lunar.modify_headers) -m bool } http-request use-service lua.generate_request if !skip_all { var(req.lunar.generate_request) -m bool } # re-resolve the host after modification http-request do-resolve(req.host_ip,resolv-conf,ipv4) var(txn.host),host_only http-request set-var(txn.x_lunar_error) str(5) unless { var(req.host_ip) -m found } http-request set-var(txn.error_in_body) str("Could not resolve host") unless { var(req.host_ip) -m found } http-request deny status 503 content-type text/plain lf-string "Could not resolve host" hdr x-lunar-error 5 unless { var(req.host_ip) -m found } # Received an early response from Lunar acl is_resp_internal var(txn.lunar.is_internal) -m bool http-request set-var(txn.is_internal) str("true") if is_resp_internal http-request use-service lua.mock_response if !skip_all is_early_response http-request set-dst var(req.host_ip) # Set new destination IP http-request set-dst-port var(txn.dst_port) # Remove headers starting with x-lunar # ***Attention***: this is done right before sending request to provider # In the case we would need to refer to request headers in the response lifecycle, # said header should be extracted and stored in a variable before this point http-request del-header x-lunar -m beg # Send request to provider use_backend %[var(txn.host)] if use_mtls use_backend provider if is_https_scheme !use_mtls default_backend insecure_provider # Modify response http-response lua.retry_request if !skip_all { var(res.lunar.retry_request) -m bool } http-response lua.modify_response if !skip_all { var(res.lunar.modify_response) -m bool } acl is_resp_internal var(res.lunar.is_internal) -m bool http-response set-var(txn.is_internal) str("true") if is_resp_internal # Update the endpoints are managed by Lunar frontend endpoints bind *:${HAPROXY_MANAGE_ENDPOINTS_PORT} mode http option http-buffer-request # Buffer the request to allow reading body acl method_get method GET acl method_put method PUT acl method_delete method DELETE http-request deny status 405 unless method_get or method_put or method_delete acl path_manage_all path /manage_all acl path_managed_endpoint path /managed_endpoint acl path_unmanage_all path /unmanage_all acl path_unmanage_global path /unmanage_global acl path_include_body path /include_body_from acl path_include_body_to_all path /include_body_from_all acl path_remove_body_from_all path /remove_body_from_all acl path_capture_req path /capture_req_from acl path_capture_req_all path /capture_req_all acl path_stop_capturing_req_all path /stop_capturing_req_all http-request deny status 404 unless path_manage_all or path_managed_endpoint or path_unmanage_all or path_unmanage_global or path_include_body or path_managed_endpoint or path_include_body_to_all or path_remove_body_from_all or path_capture_req or path_capture_req_all or path_stop_capturing_req_all acl body_found req.body -m found http-request deny status 400 if path_managed_endpoint !body_found or path_include_body !body_found or path_capture_req !body_found use_backend get_manage_all if method_get path_manage_all use_backend get_unmanage_all if method_get path_unmanage_all use_backend get_managed_endpoint if method_get path_managed_endpoint body_found use_backend manage_all if method_put path_manage_all use_backend unmanage_all if method_put path_unmanage_all use_backend manage_endpoint if method_put path_managed_endpoint body_found use_backend unmanage_endpoint if method_delete path_managed_endpoint body_found use_backend unmanage_global if method_delete path_unmanage_global use_backend include_body_from if method_put path_include_body use_backend remove_body_from if method_delete path_include_body use_backend include_body_from_all if method_put path_include_body_to_all use_backend remove_body_from_all if method_put path_remove_body_from_all use_backend capture_req_from if method_put path_capture_req use_backend stop_capturing_req_from if method_delete path_capture_req use_backend capture_req_from_all if method_put path_capture_req_all use_backend stop_capturing_req_from_all if method_put path_stop_capturing_req_all # Informative endpoints backend get_manage_all mode http acl manage_all var(proc.manage_all) -m found http-request set-var(txn.resp_body) str(true) if manage_all http-request set-var(txn.resp_body) str(false) unless manage_all http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend get_unmanage_all mode http acl skip_all var(proc.skip_all) -m found http-request set-var(txn.resp_body) str(true) if skip_all http-request set-var(txn.resp_body) str(false) unless skip_all http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend get_managed_endpoint mode http acl endpoint_is_managed req.body,map_reg(/etc/haproxy/maps/endpoints.map) -m found http-request set-var(txn.resp_body) str(true) if endpoint_is_managed http-request set-var(txn.resp_body) str(false) unless endpoint_is_managed http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" # Operational endpoints backend manage_all mode http http-request unset-var(proc.skip_all) http-request set-var(proc.manage_all) str(true) http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend manage_endpoint mode http http-request unset-var(proc.skip_all) http-request set-map(/etc/haproxy/maps/endpoints.map) %[req.body] str(true) http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend unmanage_endpoint mode http http-request del-map(/etc/haproxy/maps/endpoints.map) %[req.body] http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend unmanage_all mode http http-request unset-var(proc.manage_all) http-request set-var(proc.skip_all) str(true) http-request del-map(/etc/haproxy/maps/endpoints.map) . http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" # Unlike unmanage_all, unmanage_global does not remove the endpoints from the map backend unmanage_global mode http http-request unset-var(proc.manage_all) http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend include_body_from mode http http-request set-map(/etc/haproxy/maps/spoe_with_body.map) %[req.body] str(true) http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend include_body_from_all mode http http-request set-var(proc.body_from_all) str(true) http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend remove_body_from mode http http-request del-map(/etc/haproxy/maps/spoe_with_body.map) %[req.body] http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" # This is not used yet, but it is here for future use backend remove_body_from_all mode http http-request del-map(/etc/haproxy/maps/spoe_with_body.map) . http-request unset-var(proc.body_from_all) http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend capture_req_from mode http http-request set-map(/etc/haproxy/maps/req_capture.map) %[req.body] str(true) http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend capture_req_from_all mode http http-request set-var(proc.capture_all) str(true) http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" backend stop_capturing_req_from mode http http-request del-map(/etc/haproxy/maps/req_capture.map) %[req.body] http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" # This is not used yet, but it is here for future use backend stop_capturing_req_from_all mode http http-request unset-var(proc.capture_all) http-request del-map(/etc/haproxy/maps/req_capture.map) . http-request set-var(txn.resp_body) str(true) http-request return status 200 content-type text/plain lf-string "%[var(txn.resp_body)]" # Backend used by the SPOE backend lunar mode tcp timeout connect 20s # greater than hello timeout timeout server "${LUNAR_SPOE_PROCESSING_TIMEOUT_SEC}"s option spop-check server agent localhost:12345 backend provider mode http server clear 0.0.0.0:0 check-ssl ssl check-sni var(txn.host),host_only sni var(txn.host),host_only verify none backend insecure_provider mode http server clear 0.0.0.0:0 backend async_service mode http server steve 127.0.0.1:${ASYNC_SERVICE_PORT} # DNS resolvers resolv-conf parse-resolv-conf

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TheLunarCompany/lunar'

If you have feedback or need assistance with the MCP directory API, please join our Discord server