From 3e29737b445f617621278b9de29a0be60a00d53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Guti=C3=A9rrez=20de=20Quevedo=20P=C3=A9?= =?UTF-8?q?rez?= Date: Tue, 16 Dec 2025 13:41:24 +0100 Subject: [PATCH] Implement proxy protocol --- src/Configfile.cpp.in | 5 ++++- src/Configfile.tmpl | 12 +++++++++--- src/Proxy.cpp | 28 +++++++++++++++++++++++++++- src/Proxy.h | 2 +- src/Socket.cpp | 28 +++++++++++++++++++++++++--- src/Socket.h | 1 + src/Utils.cpp | 11 +++++++---- 7 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/Configfile.cpp.in b/src/Configfile.cpp.in index d5b61e8..cd603c9 100644 --- a/src/Configfile.cpp.in +++ b/src/Configfile.cpp.in @@ -147,7 +147,10 @@ list Configfile::parseAsList(string str) string::size_type startpos=0,endpos=0,len; string tmpstr; - str=Configfile::parseAsString(str); //remove quotes around string + if (str.length()) + { + str=Configfile::parseAsString(str); //remove quotes around string + } len=str.length(); while(startpos private.key +* # openssl genrsa 2048 > hermes.key string,private_key_file,"/etc/hermes/hermes.key" * file with our server certificate (PEM format). * to generate, execute: -* # openssl req -new -x509 -nodes -sha1 -days 365 -key private.key > certificate.crt +* # openssl req -new -x509 -nodes -sha1 -days 365 -key hermes.key > certificate.crt * and answer the questions -string,certificate_file,"/etc/hermes/hermes.cert" +string,certificate_file,"/etc/hermes/certificate.crt" * optional file with Diffie-Hellman parameters for Perfect Forward Secrecy. * to generate, execute: @@ -273,3 +273,9 @@ bool,query_spf,false * You should enable this while debugging your hermes installation, * as configuration errors won't be fatal. bool,return_temp_error_on_reject,false + +* whether to accept connections using the proxy protocol or not +* see https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt +* only version 2.0 is supported at the moment +* only IPv4 is supported +bool,proxy_protocol,false diff --git a/src/Proxy.cpp b/src/Proxy.cpp index 319417c..6d37c55 100644 --- a/src/Proxy.cpp +++ b/src/Proxy.cpp @@ -34,7 +34,7 @@ void Proxy::setOutside(Socket& p_outside) * TODO: fill diagram and point to website with graphical version * */ -void Proxy::run(string &peer_address) +void Proxy::run(const string &real_peer_address) { #ifdef HAVE_SPF Spf spf_checker; @@ -44,6 +44,32 @@ void Proxy::run(string &peer_address) string to=""; string ehlostr=""; string resolvedname=""; + string peer_address = ""; + if (cfg.getProxyProtocol()) + { + struct { + uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */ + uint8_t ver_cmd; /* protocol version and command */ + uint8_t fam; /* protocol family and address */ + uint16_t len; /* number of following bytes part of the header */ + } hdr_v2; + + struct { /* for TCP/UDP over IPv4, len = 12 */ + uint32_t src_addr; + uint32_t dst_addr; + uint16_t src_port; + uint16_t dst_port; + } ipv4_addr; + + outside.readBytes(&hdr_v2, sizeof(hdr_v2)); + outside.readBytes(&ipv4_addr, sizeof(ipv4_addr)); + peer_address = Socket::intToIpv4Address(ipv4_addr.src_addr); + std::cout << "received proxied connection from " << peer_address << std::endl; + } + else + { + peer_address = real_peer_address; + } unsigned char last_state=SMTP_STATE_WAIT_FOR_HELO; long unimplemented_requests=0; diff --git a/src/Proxy.h b/src/Proxy.h index fd55da2..8dbeeaf 100644 --- a/src/Proxy.h +++ b/src/Proxy.h @@ -55,7 +55,7 @@ class Proxy public: //Proxy():outside(NULL),inside(NULL){}; void setOutside(Socket&); - void run(string&); + void run(const string&); }; #endif //PROXY_H diff --git a/src/Socket.cpp b/src/Socket.cpp index d725c79..602e279 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -21,6 +21,8 @@ #include #include +#include + int Socket::created_sockets=0; #ifdef HAVE_SSL @@ -60,8 +62,12 @@ Socket::Socket():fd(-1) if(!ssl_ctx_server) throw Exception(_("Error creating SSL context"),__FILE__,__LINE__); /* load certificate */ - if(SSL_CTX_use_certificate_chain_file(ssl_ctx_server,cfg.getCertificateFile().c_str())==-1) + ERR_clear_error(); + if(SSL_CTX_use_certificate_chain_file(ssl_ctx_server,cfg.getCertificateFile().c_str())!=1) + { + ERR_print_errors_fp(stderr); throw Exception(_("Error loading certificate"),__FILE__,__LINE__); + } /* load DH params */ BIO *bio; @@ -87,12 +93,21 @@ Socket::Socket():fd(-1) } } + ERR_clear_error(); /* load private key */ - if(SSL_CTX_use_PrivateKey_file(ssl_ctx_server,cfg.getPrivateKeyFile().c_str(),SSL_FILETYPE_PEM)==-1) + int retval = SSL_CTX_use_PrivateKey_file(ssl_ctx_server,cfg.getPrivateKeyFile().c_str(),SSL_FILETYPE_PEM); + if(retval != 1) + { + ERR_print_errors_fp(stderr); throw Exception(_("Error loading private key"),__FILE__,__LINE__); + } /* check that private key and cert match */ - if(!SSL_CTX_check_private_key(ssl_ctx_server)) + retval = SSL_CTX_check_private_key(ssl_ctx_server); + if(retval != 1) + { + ERR_print_errors_fp(stderr); throw Exception(_("Private key doesn't match certificate file"),__FILE__,__LINE__); + } //client ssl_ctx_client=NULL; @@ -590,6 +605,13 @@ string Socket::resolveInverselyToString(string ip) } #endif //HAVE_GETADDRINFO +string Socket::intToIpv4Address(uint32_t ip) +{ + struct in_addr ip_addr; + ip_addr.s_addr = ip; + return inet_ntoa(ip_addr); +} + int Socket::getFD() { return fd; diff --git a/src/Socket.h b/src/Socket.h index 889c0df..337abd8 100644 --- a/src/Socket.h +++ b/src/Socket.h @@ -84,6 +84,7 @@ class Socket bool connect(string,unsigned int); int getFD(); static struct sockaddr resolve(string); + static string intToIpv4Address(uint32_t); static string resolveToString(string); static string resolveInverselyToString(string); diff --git a/src/Utils.cpp b/src/Utils.cpp index 20c3631..185f2a1 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -85,11 +85,14 @@ string Utils::strtolower(string s) */ string Utils::trim(string s) { - while(isspace(s[0])) - s.erase(0,1); + if(s.length()) + { + while(isspace(s[0])) + s.erase(0,1); - while(isspace(s[s.length()-1])) - s.erase(s.length()-1,1); + while(isspace(s[s.length()-1])) + s.erase(s.length()-1,1); + } return s; }