diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 9938ce19..8719d15f 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -412,6 +412,20 @@ put the outermost URL here (the address which the end user types into his/her browser). If this option is not set then no rewriting of redirects occurs. +=item B + +Set this option to `Yes` when the tinyproxy's client is a proxy that +uses the PROXY protocol, which inserts a line before the HTTP request +line to indicate the IP address of the real client. Only version 1 +(human-readable header format) is supported. + +For instance, when using stunnel in front of tinyproxy and the +BasicAuth option is used, if the PROXY protocol is used, tinyproxy +will report the actual client IP address in the log instead of the IP +address of the stunnel server, so that the log can be parsed by +tools like fail2ban to ban clients that try to brute force the +authentication. + =back =head1 BUGS diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index b7d46a71..d15f9574 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -327,3 +327,18 @@ ViaProxyName "tinyproxy" # If not set then no rewriting occurs. # #ReverseBaseURL "http://localhost:8888/" + +# +# The tinyproxy's client is a proxy that uses the PROXY protocol, which +# inserts a line before the HTTP request line to indicate the IP +# address of the real client. Only version 1 (human-readable header +# format) is supported. +# +# For instance, when using stunnel in front of tinyproxy and the +# BasicAuth option is used, if the PROXY protocol is used, tinyproxy +# will report the actual client IP address in the log instead of the IP +# address of the stunnel server, so that the log can be parsed by +# tools like fail2ban to ban clients that try to brute force the +# authentication. +# +#ClientUsesProxyProtocol Yes diff --git a/src/conf-tokens.c b/src/conf-tokens.c index 4463d230..3007190d 100644 --- a/src/conf-tokens.c +++ b/src/conf-tokens.c @@ -59,7 +59,8 @@ config_directive_find (register const char *str, register size_t len) {"basicauth", CD_basicauth}, {"basicauthrealm", CD_basicauthrealm}, {"addheader", CD_addheader}, - {"maxrequestsperchild", CD_maxrequestsperchild} + {"maxrequestsperchild", CD_maxrequestsperchild}, + {"clientusesproxyprotocol", CD_clientusesproxyprotocol} }; for(i=0;iclient_uses_proxyproto, line, &match[2]); + + if (r) { + return r; + } + + if (conf->client_uses_proxyproto) + log_message (LOG_INFO, "Expecting PROXY protocol header."); + return 0; +} + static HANDLE_FUNC (handle_defaulterrorfile) { return set_string_arg (&conf->errorpage_undef, line, &match[2]); diff --git a/src/conf.h b/src/conf.h index 0b25afa4..9650d250 100644 --- a/src/conf.h +++ b/src/conf.h @@ -77,6 +77,8 @@ struct config_s { unsigned int disable_viaheader; /* boolean */ + unsigned int client_uses_proxyproto; /* boolean */ + /* * Error page support. Map error numbers to file paths. */ diff --git a/src/reqs.c b/src/reqs.c index a562c68a..a7d1b1c0 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -85,6 +85,74 @@ #define CHECK_LWS(header, len) \ ((len) > 0 && (header[0] == ' ' || header[0] == '\t')) +/* + * Read and parse the PROXY protocol v1 line, and update the client address. + */ +static int read_proxy_line (struct conn_s *connptr, union sockaddr_union* addr) +{ + int result = -1; + char *line = NULL; + ssize_t len; + char *src_addr, *end; + sa_family_t af; + + len = readline (connptr->client_fd, &line); + if (len <= 0) { + log_message (LOG_ERR, + "read_proxy_line: Client (file descriptor: %d) " + "closed socket before read.", connptr->client_fd); + + goto cleanup; + } + + /* + * The PROXY line looks like that: + * PROXY \r\n + * can be "TCP4" for IPv4/TCP or "TCP6" for IPv6/TCP + * and are IPv4 or IPv6 addresses + * and are TCP port numbers* + */ + + /* Expect "PROXY TCP4 " or "PROXY TCP6 " */ + if (!(!strncmp (line, "PROXY TCP", 9) + && (line[9] == '4' || line[9] == '6') + && line[10] == ' ')) { +bad_syntax: + log_message (LOG_ERR, "Bad PROXY line: %s", line); + + goto cleanup; + } + + af = line[9] == '6' ? AF_INET6 : AF_INET; + + /* Only extract the source address string */ + src_addr = line + 11; + end = strchr (src_addr, ' '); + if (end == NULL) + goto bad_syntax; + + *end = '\0'; + + /* Update "addr" with the given address */ + memset (addr, 0, sizeof(*addr)); + addr->v4.sin_family = af; + if (inet_pton (af, src_addr, SOCKADDR_UNION_ADDRESS(addr)) != 1) { + log_message (LOG_ERR, + "Cannot convert address from PROXY line: %s", + src_addr); + + goto cleanup; + } + + result = 0; + +cleanup: + + safefree (line); + + return result; +} + /* * Read in the first line from the client (the request line for HTTP * connections. The request line is allocated from the heap, but it must @@ -1598,6 +1666,11 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) char sock_ipaddr[IP_LENGTH]; char peer_ipaddr[IP_LENGTH]; + if (config->client_uses_proxyproto && read_proxy_line (connptr, addr)) { + close (fd); + return; + } + getpeer_information (addr, peer_ipaddr, sizeof(peer_ipaddr)); if (config->bindsame)