Transparent Reverse Proxy Using OpenBSD's relayd
You are here: Home > Blog > Entries > Transparent Reverse Proxy Using relayd
Transparent reverse proxy using OpenBSD relayd

I've been using OpenBSD's relayd (version shipped in OpenBSD 6.1) as a reverse proxy in front of OpenBSD's httpd webserver for a year or so. It does filtering OK (see this post). One of the problems is that I could not get relayd to pass the ip address of the client to the webserver for logging.

The relayd.conf manual includes several examples, and I have tried several permutations including:

match header set "X-Forwarded-For" value "$REMOTE_ADDR" 
match header append "X-Forwarded-For value "$REMOTE_ADDR"
match request header set "X-Forwarded-For" value "$REMOTE_ADDR" 
match request header append "X-Forwarded-For value "$REMOTE_ADDR" 

And so on. None appear to work. A transparent proxy would be the usual solution for this and is easy enough with squid or nginx which I was previously using in front of my WP blog. But, I wanted to do this with the default install of OpenBSD and this is not well documented in the man pages for pf, relayd, or httpd. There are several hints on how to do this, for example, see here and here. However, these are earlier versions of relayd; other posts on the mailing lists suggest that admins had difficulty getting it to work when PF, relayd, and httpd were all running on the same server.

After much trial and error, it does work. To get it to work, you have to first set up a redirect in pf to send http traffic to the webserver. The relevant pf.conf stanza was:

...
# Relayd anchor
anchor "relayd/*"
...
# redirect http traffic to relayd -> httpd
pass in on vr1 proto tcp to port 80 rdr-to 127.0.0.1 port 8080
pass out on vr1 divert-reply
...

Next, you have to configure relayd.conf, as follows:

##
## $OpenBSD: relayd.conf,v 1.3 2014/12/12 10:05:09 reyk
## Modified 3/3/18 by gordon

##
## Macros
##

##
## Global Options
##
# interval 10
# timeout 1000

prefork 3
log updates

##
## Tables
##

table <webserver1> {127.0.0.1}

##
## Redirections
##

##
## Protocols
##

#
# Filtering rules for reverse HTTP proxy
#

http protocol reverseproxy {

#       # TCP performance options 
        tcp {nodelay, sack, socket buffer 65536, backlog 100 }

#       # Return HTTP/HTML error pages
        return error

#       # Change timeout
        match header set "Keep-Alive" value "$TIMEOUT"

#       # Anonymize our webserver's name/type
        match response header set  "Server" value "Microsoft IIS 9 beta 1"

#       # Pass GET and HEAD; drop all other HTTP requests
        pass request quick method "HEAD" forward to <webserver1>
        pass request quick method "GET" forward to <webserver1>
        match request label "HTTP Request Not Allowed"
        block request

        }

##
## Relays
##

#
# Relay for a reverse HTTP proxy
#

relay reverseproxy {

        # listen on pf-rdr redirected port for http traffic
        listen on 127.0.0.1 port 8080

        # apply web filters listed above
        protocol reverseproxy

        # transparent forward with pf redirect
        transparent forward to <webserver1> port 8080

        }


##
## Routers
##

##
## End of /etc/relayd.conf
##

In examples on the mailing lists, using earlier versions, seemed to require "check tcp interface vr1" to be appended to the last line. In this version, that throws an error and the config works with that phrase omitted.

The relevant parts of httpd.conf:

...
#
# Macros
#

ext_addr="127.0.0.1"
ext_port="8080"

# Servers
#

# nohair.net web server
server "www.nohair.net" {
        alias "nohair.net"
        listen on $ext_addr port $ext_port
        log style combined
        root "/htdocs/vhosts/nohair.net"
        }
...

With this, I can now get the passage of the client ip address for proper logging by the webserver so I can install AWStats, webalizer, or use Google Analytics.


Posted by Gordon, No Hair Blog, Mar 2, 2018

© nohair.net and the author

For comments, corrections, and addenda, email: gordon[AT]nohair.net

Blog | Entries | Tags | Home