| Title: Nginx as a TCP/UDP relay | |
| Author: Solène | |
| Date: 24 February 2021 | |
| Tags: openbsd nginx networking | |
| Description: | |
| # Introduction | |
| In this tutorial I will explain how to use Nginx as a TCP or UDP relay | |
| as an alternative to Haproxy or Relayd. This mean nginx will be able | |
| to accept requests on a port (TCP/UDP) and relay it to another backend | |
| without knowing about the content. It also permits to negociates a TLS | |
| session with the client and relay to a non-TLS backend. In this | |
| example I will explain how to configure Nginx to accept TLS requests to | |
| transmit it to my Gemini server Vger, Gemini protocol has TLS as a | |
| requirement. | |
| I will explain how to install and configure Nginx and how to parse logs | |
| to obtain useful information. I will use an OpenBSD system for the | |
| examples. | |
| It is important to understand that in this context Nginx is not doing | |
| anything related to HTTP. | |
| # Installation | |
| On OpenBSD we need the package nginx-stream, if you are unsure about | |
| which package is required on your system, search which package provide | |
| the file ngx_stream_module.so . To enable Nginx at boot, you can use | |
| rcctl enable nginx. | |
| Nginx stream module core documentation | |
| Nginx stream module log documentation | |
| # Configuration | |
| The default configuration file for nginx is /etc/nginx/nginx.conf , we | |
| will want it to listen on port 1965 and relay to 127.0.0.1:11965. | |
| ```Nginx configuration file | |
| worker_processes 1; | |
| load_module modules/ngx_stream_module.so; | |
| events { | |
| worker_connections 5; | |
| } | |
| stream { | |
| log_format basic '$remote_addr $upstream_addr [$time_local] ' | |
| '$protocol $status $bytes_sent $bytes_received ' | |
| '$session_time'; | |
| access_log logs/nginx-access.log basic; | |
| upstream backend { | |
| hash $remote_addr consistent; | |
| server 127.0.0.1:11965; | |
| } | |
| server { | |
| listen 1965 ssl; | |
| ssl_certificate /etc/ssl/perso.pw:1965.crt; | |
| ssl_certificate_key /etc/ssl/private/perso.pw:1965.key; | |
| proxy_pass backend; | |
| } | |
| } | |
| ``` | |
| In the previous configuration file, the backend defines the | |
| destination, multiples servers could be defined, with weights and | |
| timeouts, there is only one in this example. | |
| The server block will tell on which port Nginx should listen and if it | |
| has to handle TLS (which is named ssl because of history), usual TLS | |
| configuration can be used here, then for a request, we have to tell to | |
| which backend Nginx have to relay the connections. | |
| The configuration file defines a custom log format that is useful for | |
| TLS connections, it includes remote host, backend destination, | |
| connection status, bytes transffered and duration. | |
| # Log parsing | |
| ## Using awk to calculate time performance | |
| I wrote a quite long shell command parsing the log defined earlier that | |
| display the number of requests, and median/min/max session time. | |
| ```awk command to parse nginx custom log defined earlier | |
| $ awk '{ print $NF }' /var/www/logs/nginx-access.log | sort -n | awk '{ data[N… | |
| Total: 566 Median:0.212 Min:0.000 Max:600.487 | |
| ``` | |
| ## Find bad clients using awk | |
| Sometimes in the logs there are clients that obtains a status 500, | |
| meaning the TLS connection haven't been established correctly. It may | |
| be some scanner that doesn't try a TLS connection, if you want to get | |
| statistics about those and see if it would be worth to block them if | |
| they do too many attempt, it is easy to use awk to get the list. | |
| ```awk command reporting clients with a status 500 | |
| awk '$(NF-3) == 500 { print $1 }' /var/www/logs/nginx-access.log | |
| ``` | |
| ## Using goaccess for real time log visualization | |
| It is also possible to use the program Goaccess to view logs in real | |
| time with many information, it is really an awesome program. | |
| ```goaccess command line with lot of parameters | |
| goaccess --date-format="%d/%b/%Y" \ | |
| --time-format="%H:%M:%S" \ | |
| --log-format="%h %r [%d:%t %^] TCP %s %^ %b %L" /var/www/logs/nginx-ac… | |
| ``` | |
| Goaccess official website | |
| # Conclusion | |
| I was using relayd before trying Nginx with stream module, while relayd | |
| worked fine it doesn't provide any of the logs Nginx offer. I am | |
| really happy with this use of Nginx because it is a very versatile | |
| program that shown to be more than a http server over time. For a | |
| minimal setup I would still recommend lighter daemon such as relayd. |