Imported Upstream version 0.2

This commit is contained in:
Mario Fetka 2020-02-12 11:53:48 +01:00
commit 29b6a57ca2
20 changed files with 1326 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.libs
mod_proxy_protocol.la
mod_proxy_protocol.lo
mod_proxy_protocol.slo

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

33
Makefile Normal file
View File

@ -0,0 +1,33 @@
##
## Makefile -- Build procedure for sample proxy_protocol Apache module
## Autogenerated via ``apxs -n proxy_protocol -g''.
##
# the used tools
APXS=apxs
XSLT=xsltproc
# the default target
all: .libs/mod_proxy_protocol.so
# build the so in the current directory
.libs/mod_proxy_protocol.so: mod_proxy_protocol.c
$(APXS) -c -Wc,-Wall mod_proxy_protocol.c
# install the so - usually needs root access
install: .libs/mod_proxy_protocol.so
$(APXS) -i mod_proxy_protocol.la
# generate the html doc
docs: mod_proxy_protocol.html
mod_proxy_protocol.html: mod_proxy_protocol.xml mod_proxy_protocol.xml.meta
$(XSLT) -o $@ $<
# generate packages
dpkg:
debuild --no-tgz-check -uc -us
# cleanup
clean:
-rm -rf mod_proxy_protocol.o mod_proxy_protocol.lo mod_proxy_protocol.slo mod_proxy_protocol.la .libs mod_proxy_protocol.html

52
README.md Normal file
View File

@ -0,0 +1,52 @@
# Apache Proxy Protocol Module
This is an [Apache](http://httpd.apache.org/) module that implements the
server side of HAProxy's
[Proxy Protocol](http://blog.haproxy.com/haproxy/proxy-protocol/).
Note: as of Apache 2.4.30 this code has been merged into
[mod_remoteip](https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html),
with the `ProxyProtocol` directive renamed to `RemoteIPProxyProtocol`.
## Build and Install
You'll need the apache development packages installed (typically something
like `apache-devel` or `apache2-dev`). Then simply running `make` will
create the shared library in `.libs/mod_proxy_protocol.so`; `make install`
will attempt to install it in your current apache installation (you'll
probably need to be root for this).
## Configuration
Add the following directive to your apache config to load the module
LoadModule proxy_protocol_module <path-to-module>/mod_proxy_protocol.so
or try running
apxs -a mod_proxy_protocol.c
For configuration details see the
[module docs](http://roadrunner2.github.io/mod-proxy-protocol/mod_proxy_protocol.html)
## Amazon EC2 Notes
To properly secure Apache when using this on EC2 behind an ELB you'll probably
want to do something like the following to ensure that only the ELB can
provide the proxy protocol header while still being able to access the site
directly (not via the ELB):
1. In Apache create a copy of virtual host with a new port, and add the
'ProxyProtocol On' directive to this new virtual host.
2. Add an entry to the security group for the server that only allows access
to this new port from the ELB (specify the source as `amazon-elb/amazon-elb-sg`)
3. Point the ELB listener at this new port (and of course
[enable](http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/enable-proxy-protocol.html)
the proxy protocol for that)
## TODO
* Add access-control (see commented out `ProxyProtocolTrustedProxies` in
`mod_proxy_protocol.xml`)
* Add support for outgoing connections (`mod_proxy`)

5
debian/README.Debian vendored Normal file
View File

@ -0,0 +1,5 @@
## To activate mod_proxy_protocol, do:
a2enmod proxy_protocol && /etc/init.d/apache2 force-reload
## And then enable ProxyProtocol for the desired hosts.

3
debian/apache2 vendored Normal file
View File

@ -0,0 +1,3 @@
mod .libs/mod_proxy_protocol.so
mod debian/proxy_protocol.load
mod debian/proxy_protocol.conf

11
debian/changelog vendored Normal file
View File

@ -0,0 +1,11 @@
mod-proxy-protocol (0.2-1) xenial; urgency=low
* Merged patch to fix http/2 issues.
-- Roadrunner2 <roadrunner2@github.com> Tue, 20 Mar 2018 02:11:49 -0700
mod-proxy-protocol (0.1-1) trusty; urgency=low
* Initial release
-- Roadrunner2 <roadrunner2@github.com> Thu, 30 Oct 2014 16:46:21 -0700

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

17
debian/control vendored Normal file
View File

@ -0,0 +1,17 @@
Source: mod-proxy-protocol
Section: web
Priority: optional
Maintainer: Roadrunner2 <roadrunner2@github.com>
Build-Depends: debhelper (>= 7.0.50~), apache2-dev (>= 2.4.0), xsltproc
Standards-Version: 3.8.4
Homepage: https://github.com/roadrunner2/mod-proxy-protocol
Package: libapache2-mod-proxy-protocol
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}, apache2-api-20120211
Description: Apache module for proxy protocol
The proxy protocol is a way for upstream proxies and load-balancers to
for the ip-address of the client to the server. This package contains
an Apache module that implements the server-side of this protocol, thereby
allowing other modules to see and use actual client's ip-address instead
of that of the upstream proxy/load-balancer.

13
debian/copyright vendored Normal file
View File

@ -0,0 +1,13 @@
Copyright 2014 Cloudzilla Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

1
debian/dirs vendored Normal file
View File

@ -0,0 +1 @@
usr/lib/apache2/modules

4
debian/docs vendored Normal file
View File

@ -0,0 +1,4 @@
README.md
mod_proxy_protocol.xml
mod_proxy_protocol.xml.meta
debian/README.Debian

2
debian/lintian-overrides vendored Normal file
View File

@ -0,0 +1,2 @@
libapache2-mod-proxy-protocol: apache2-module-depends-on-real-apache2-package
libapache2-mod-proxy-protocol: copyright-should-refer-to-common-license-file-for-apache-2

3
debian/proxy_protocol.conf vendored Normal file
View File

@ -0,0 +1,3 @@
<IfModule mod_proxy_protocol.c>
#ProxyProtocol On
</IfModule>

1
debian/proxy_protocol.load vendored Normal file
View File

@ -0,0 +1 @@
LoadModule proxy_protocol_module /usr/lib/apache2/modules/mod_proxy_protocol.so

43
debian/rules vendored Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/make -f
# debian rules file for mod_proxy_protocol
build:
dh_testdir
make all # docs
clean:
dh_testdir
dh_testroot
make clean
dh_clean
install: build
dh_testdir
dh_testroot
dh_prep
dh_installdirs
dh_install
dh_apache2 -e
# Build architecture-dependent files here.
binary-arch: build install
dh_testdir
dh_testroot
dh_installdocs
dh_installchangelogs
dh_link
dh_strip
dh_compress
dh_fixperms
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
# Build architecture-independent files here.
binary-indep: build install
binary: binary-arch binary-indep
.PHONY: build clean binary-indep binary-arch binary install configure

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
1.0

797
mod_proxy_protocol.c Normal file
View File

@ -0,0 +1,797 @@
/*
* Copyright 2014 Cloudzilla Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* mod_proxy_protocol.c -- Apache proxy_protocol module
*
* This implements the server side of the proxy protocol decribed in
* http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt . It works
* by installing itself (where enabled) as a connection filter (ahead of
* mod_ssl) to parse and remove the proxy protocol header, and by then
* modifying the useragent_* fields in the requests accordingly.
*
* TODO:
* * add the following configs:
* ProxyProtocolTrustedProxies "all"|ip-addr|host [ip-addr|host] ... (default all)
* ProxyProtocolRejectUntrusted Yes|No (default Yes)
* What to do if a connection is received from an untrusted proxy:
* yes = abort the connection
* no = allow connection and remove header, but ignore header
* * add support for sending the header on outgoing connections (mod_proxy),
* and config for choosing which hosts to enable it for
* (ProxyProtocolDownstreamHosts?)
*/
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_connection.h"
#include "http_main.h"
#include "http_log.h"
#include "ap_config.h"
#include "ap_listen.h"
#include "apr_strings.h"
#include "apr_version.h"
module AP_MODULE_DECLARE_DATA proxy_protocol_module;
/*
* Module configuration
*/
typedef struct pp_addr_info {
struct pp_addr_info *next;
apr_sockaddr_t *addr;
server_rec *source;
} pp_addr_info;
typedef struct {
pp_addr_info *enabled;
pp_addr_info *disabled;
apr_pool_t *pool;
} pp_config;
static int pp_hook_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp)
{
pp_config *conf;
conf = (pp_config *) apr_palloc(pconf, sizeof(pp_config));
conf->enabled = NULL;
conf->disabled = NULL;
conf->pool = pconf;
ap_set_module_config(ap_server_conf->module_config, &proxy_protocol_module,
conf);
return OK;
}
#if !(APR_VERSION_AT_LEAST(1,5,0))
static APR_DECLARE(int) apr_sockaddr_is_wildcard(const apr_sockaddr_t *addr)
{
static const char inaddr_any[
#if APR_HAVE_IPV6
sizeof(struct in6_addr)
#else
sizeof(struct in_addr)
#endif
] = {0};
if (addr->ipaddr_ptr /* IP address initialized */
&& addr->ipaddr_len <= sizeof inaddr_any) { /* else bug elsewhere? */
if (!memcmp(inaddr_any, addr->ipaddr_ptr, addr->ipaddr_len)) {
return 1;
}
#if APR_HAVE_IPV6
if (addr->family == AF_INET6
&& IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr)) {
struct in_addr *v4 = (struct in_addr *)&((apr_uint32_t *)addr->ipaddr_ptr)[3];
if (!memcmp(inaddr_any, v4, sizeof *v4)) {
return 1;
}
}
#endif
}
return 0;
}
#endif
/* Similar apr_sockaddr_equal, except that it compares ports too. */
static int pp_sockaddr_equal(apr_sockaddr_t *addr1, apr_sockaddr_t *addr2)
{
return (addr1->port == addr2->port && apr_sockaddr_equal(addr1, addr2));
}
/* Similar pp_sockaddr_equal, except that it handles wildcard addresses
* and ports too.
*/
static int pp_sockaddr_compat(apr_sockaddr_t *addr1, apr_sockaddr_t *addr2)
{
/* test exact address equality */
if (apr_sockaddr_equal(addr1, addr2) &&
(addr1->port == addr2->port || addr1->port == 0 || addr2->port == 0)) {
return 1;
}
/* test address wildcards */
if (apr_sockaddr_is_wildcard(addr1) &&
(addr1->port == 0 || addr1->port == addr2->port)) {
return 1;
}
if (apr_sockaddr_is_wildcard(addr2) &&
(addr2->port == 0 || addr2->port == addr1->port)) {
return 1;
}
return 0;
}
static int pp_addr_in_list(pp_addr_info *list, apr_sockaddr_t *addr)
{
for (; list; list = list->next) {
if (pp_sockaddr_compat(list->addr, addr)) {
return 1;
}
}
return 0;
}
static void pp_warn_enable_conflict(pp_addr_info *prev, server_rec *new, int on)
{
char buf[INET6_ADDRSTRLEN];
apr_sockaddr_ip_getbuf(buf, sizeof(buf), prev->addr);
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, new,
"ProxyProtocol: previous setting for %s:%hu from virtual "
"host {%s:%hu in %s} is being overriden by virtual host "
"{%s:%hu in %s}; new setting is '%s'",
buf, prev->addr->port, prev->source->server_hostname,
prev->source->addrs->host_port, prev->source->defn_name,
new->server_hostname, new->addrs->host_port, new->defn_name,
on ? "On" : "Off");
}
static const char *pp_enable_proxy_protocol(cmd_parms *cmd, void *config,
int flag)
{
pp_config *conf;
server_addr_rec *addr;
pp_addr_info **add;
pp_addr_info **rem;
pp_addr_info *list;
conf = ap_get_module_config(ap_server_conf->module_config,
&proxy_protocol_module);
if (flag) {
add = &conf->enabled;
rem = &conf->disabled;
}
else {
add = &conf->disabled;
rem = &conf->enabled;
}
for (addr = cmd->server->addrs; addr; addr = addr->next) {
/* remove address from opposite list */
if (*rem) {
if (pp_sockaddr_equal((*rem)->addr, addr->host_addr)) {
pp_warn_enable_conflict(*rem, cmd->server, flag);
*rem = (*rem)->next;
}
else {
for (list = *rem; list->next; list = list->next) {
if (pp_sockaddr_equal(list->next->addr, addr->host_addr)) {
pp_warn_enable_conflict(list->next, cmd->server, flag);
list->next = list->next->next;
break;
}
}
}
}
/* add address to desired list */
if (!pp_addr_in_list(*add, addr->host_addr)) {
pp_addr_info *info = apr_palloc(conf->pool, sizeof(*info));
info->addr = addr->host_addr;
info->source = cmd->server;
info->next = *add;
*add = info;
}
}
return NULL;
}
static int pp_hook_post_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
pp_config *conf;
pp_addr_info *info;
char buf[INET6_ADDRSTRLEN];
conf = ap_get_module_config(ap_server_conf->module_config,
&proxy_protocol_module);
for (info = conf->enabled; info; info = info->next) {
apr_sockaddr_ip_getbuf(buf, sizeof(buf), info->addr);
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
"ProxyProtocol: enabled on %s:%hu", buf, info->addr->port);
}
for (info = conf->disabled; info; info = info->next) {
apr_sockaddr_ip_getbuf(buf, sizeof(buf), info->addr);
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
"ProxyProtocol: disabled on %s:%hu", buf, info->addr->port);
}
return OK;
}
static const command_rec proxy_protocol_cmds[] = {
AP_INIT_FLAG("ProxyProtocol", pp_enable_proxy_protocol, NULL, RSRC_CONF,
"Enable proxy-protocol handling (`on', `off')"),
{ NULL }
};
/*
* Proxy-protocol implementation
*/
static const char *pp_inp_filter = "ProxyProtocol Filter";
typedef struct {
char line[108];
} proxy_v1;
typedef union {
struct { /* for TCP/UDP over IPv4, len = 12 */
uint32_t src_addr;
uint32_t dst_addr;
uint16_t src_port;
uint16_t dst_port;
} ip4;
struct { /* for TCP/UDP over IPv6, len = 36 */
uint8_t src_addr[16];
uint8_t dst_addr[16];
uint16_t src_port;
uint16_t dst_port;
} ip6;
struct { /* for AF_UNIX sockets, len = 216 */
uint8_t src_addr[108];
uint8_t dst_addr[108];
} unx;
} proxy_v2_addr;
typedef 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 */
proxy_v2_addr addr;
} proxy_v2;
typedef union {
proxy_v1 v1;
proxy_v2 v2;
} proxy_header;
static const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
#define MIN_V1_HDR_LEN 15
#define MIN_V2_HDR_LEN 16
#define MIN_HDR_LEN MIN_V1_HDR_LEN
typedef struct {
char header[sizeof(proxy_header)];
apr_size_t rcvd;
apr_size_t need;
int version;
ap_input_mode_t mode;
apr_bucket_brigade *bb;
int done;
} pp_filter_context;
typedef struct {
apr_sockaddr_t *client_addr;
char *client_ip;
} pp_conn_config;
static int pp_is_server_port(apr_port_t port)
{
ap_listen_rec *lr;
for (lr = ap_listeners; lr; lr = lr->next) {
if (lr->bind_addr && lr->bind_addr->port == port) {
return 1;
}
}
return 0;
}
/* Add our filter to the connection.
*/
static int pp_hook_pre_connection(conn_rec *c, void *csd)
{
pp_config *conf;
pp_conn_config *conn_conf;
/* Establish master config in slave connections, so that request
* processing finds it. */
if (c->master != NULL) {
conn_conf = ap_get_module_config(c->master->conn_config,
&proxy_protocol_module);
if (conn_conf) {
ap_set_module_config(c->conn_config, &proxy_protocol_module,
conn_conf);
}
return DECLINED;
}
/* check if we're enabled for this connection */
conf = ap_get_module_config(ap_server_conf->module_config,
&proxy_protocol_module);
if (!pp_addr_in_list(conf->enabled, c->local_addr) ||
pp_addr_in_list(conf->disabled, c->local_addr)) {
return DECLINED;
}
/* mod_proxy creates outgoing connections - we don't want those */
if (!pp_is_server_port(c->local_addr->port)) {
return DECLINED;
}
/* add our filter */
if (!ap_add_input_filter(pp_inp_filter, NULL, NULL, c)) {
return DECLINED;
}
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
"ProxyProtocol: enabled on connection to %s:%hu",
c->local_ip, c->local_addr->port);
/* this holds the resolved proxy info for this connection */
conn_conf = apr_pcalloc(c->pool, sizeof(*conn_conf));
ap_set_module_config(c->conn_config, &proxy_protocol_module, conn_conf);
return OK;
}
/* Set the request's useragent fields to our client info.
*/
static int pp_hook_post_read_request(request_rec *r)
{
pp_conn_config *conn_conf;
conn_conf = ap_get_module_config(r->connection->conn_config,
&proxy_protocol_module);
if (!conn_conf || !conn_conf->client_addr) {
return DECLINED;
}
r->useragent_addr = conn_conf->client_addr;
r->useragent_ip = conn_conf->client_ip;
return OK;
}
typedef enum { HDR_DONE, HDR_ERROR, HDR_NEED_MORE } pp_parse_status_t;
/*
* Human readable format:
* PROXY {TCP4|TCP6|UNKNOWN} <client-ip-addr> <dest-ip-addr> <client-port> <dest-port><CR><LF>
*/
static pp_parse_status_t pp_process_v1_header(conn_rec *c,
pp_conn_config *conn_conf,
proxy_header *hdr, apr_size_t len,
apr_size_t *hdr_len)
{
char *end, *word, *host, *valid_addr_chars, *saveptr;
char buf[sizeof(hdr->v1.line)];
apr_port_t port;
apr_status_t ret;
apr_int32_t family;
#define GET_NEXT_WORD(field) \
word = apr_strtok(NULL, " ", &saveptr); \
if (!word) { \
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, \
"ProxyProtocol: no " field " found in header '%s'", \
hdr->v1.line); \
return HDR_ERROR; \
}
end = memchr(hdr->v1.line, '\r', len - 1);
if (!end || end[1] != '\n') {
return HDR_NEED_MORE; /* partial or invalid header */
}
*end = '\0';
*hdr_len = end + 2 - hdr->v1.line; /* skip header + CRLF */
/* parse in separate buffer so have the original for error messages */
strcpy(buf, hdr->v1.line);
apr_strtok(buf, " ", &saveptr);
/* parse family */
GET_NEXT_WORD("family")
if (strcmp(word, "UNKNOWN") == 0) {
conn_conf->client_addr = c->client_addr;
conn_conf->client_ip = c->client_ip;
return HDR_DONE;
}
else if (strcmp(word, "TCP4") == 0) {
family = APR_INET;
valid_addr_chars = "0123456789.";
}
else if (strcmp(word, "TCP6") == 0) {
family = APR_INET6;
valid_addr_chars = "0123456789abcdefABCDEF:";
}
else {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
"ProxyProtocol: unknown family '%s' in header '%s'",
word, hdr->v1.line);
return HDR_ERROR;
}
/* parse client-addr */
GET_NEXT_WORD("client-address")
if (strspn(word, valid_addr_chars) != strlen(word)) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
"ProxyProtocol: invalid client-address '%s' found in "
"header '%s'", word, hdr->v1.line);
return HDR_ERROR;
}
host = word;
/* parse dest-addr */
GET_NEXT_WORD("destination-address")
/* parse client-port */
GET_NEXT_WORD("client-port")
if (sscanf(word, "%hu", &port) != 1) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
"ProxyProtocol: error parsing port '%s' in header '%s'",
word, hdr->v1.line);
return HDR_ERROR;
}
/* parse dest-port */
/* GET_NEXT_WORD("destination-port") - no-op since we don't care about it */
/* create a socketaddr from the info */
ret = apr_sockaddr_info_get(&conn_conf->client_addr, host, family, port, 0,
c->pool);
if (ret != APR_SUCCESS) {
conn_conf->client_addr = NULL;
ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c,
"ProxyProtocol: error converting family '%d', host '%s',"
" and port '%hu' to sockaddr; header was '%s'",
family, host, port, hdr->v1.line);
return HDR_ERROR;
}
conn_conf->client_ip = apr_pstrdup(c->pool, host);
return HDR_DONE;
}
/* Binary format:
* <sig><cmd><proto><addr-len><addr>
* sig = \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A
* cmd = <4-bits-version><4-bits-command>
* 4-bits-version = \x02
* 4-bits-command = {\x00|\x01} (\x00 = LOCAL: discard con info; \x01 = PROXY)
* proto = <4-bits-family><4-bits-protocol>
* 4-bits-family = {\x00|\x01|\x02|\x03} (AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX)
* 4-bits-protocol = {\x00|\x01|\x02} (UNSPEC, STREAM, DGRAM)
*/
static pp_parse_status_t pp_process_v2_header(conn_rec *c,
pp_conn_config *conn_conf,
proxy_header *hdr)
{
apr_status_t ret;
switch (hdr->v2.ver_cmd & 0xF) {
case 0x01: /* PROXY command */
switch (hdr->v2.fam) {
case 0x11: /* TCPv4 */
ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL,
APR_INET,
ntohs(hdr->v2.addr.ip4.src_port),
0, c->pool);
if (ret != APR_SUCCESS) {
conn_conf->client_addr = NULL;
ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c,
"ProxyProtocol: error creating sockaddr");
return HDR_ERROR;
}
conn_conf->client_addr->sa.sin.sin_addr.s_addr =
hdr->v2.addr.ip4.src_addr;
break;
case 0x21: /* TCPv6 */
ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL,
APR_INET6,
ntohs(hdr->v2.addr.ip6.src_port),
0, c->pool);
if (ret != APR_SUCCESS) {
conn_conf->client_addr = NULL;
ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c,
"ProxyProtocol: error creating sockaddr");
return HDR_ERROR;
}
memcpy(&conn_conf->client_addr->sa.sin6.sin6_addr.s6_addr,
hdr->v2.addr.ip6.src_addr, 16);
break;
default:
/* unsupported protocol */
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(10183)
"RemoteIPProxyProtocol: unsupported protocol %.2hx",
(unsigned short)hdr->v2.fam);
return HDR_ERROR;
}
break; /* we got a sockaddr now */
default:
/* not a supported command */
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
"ProxyProtocol: unsupported command %.2hx",
hdr->v2.ver_cmd);
return HDR_ERROR;
}
/* got address - compute the client_ip from it */
ret = apr_sockaddr_ip_get(&conn_conf->client_ip, conn_conf->client_addr);
if (ret != APR_SUCCESS) {
conn_conf->client_addr = NULL;
ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, c,
"ProxyProtocol: error converting address to string");
return HDR_ERROR;
}
return HDR_DONE;
}
/* Determine if this is a v1 or v2 header.
*/
static int pp_determine_version(conn_rec *c, const char *ptr)
{
proxy_header *hdr = (proxy_header *) ptr;
/* assert len >= 14 */
if (memcmp(&hdr->v2, v2sig, sizeof(v2sig)) == 0 &&
(hdr->v2.ver_cmd & 0xF0) == 0x20) {
return 2;
}
else if (memcmp(hdr->v1.line, "PROXY ", 6) == 0) {
return 1;
}
else {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
"ProxyProtocol: no valid header found");
return -1;
}
}
/* Capture the first bytes on the protocol and parse the proxy protocol header.
* Removes itself when the header is complete.
*/
static apr_status_t pp_input_filter(ap_filter_t *f,
apr_bucket_brigade *bb_out,
ap_input_mode_t mode,
apr_read_type_e block,
apr_off_t readbytes)
{
apr_status_t ret;
pp_filter_context *ctx = f->ctx;
pp_conn_config *conn_conf;
apr_bucket *b;
pp_parse_status_t psts;
const char *ptr;
apr_size_t len;
if (f->c->aborted) {
return APR_ECONNABORTED;
}
/* allocate/retrieve the context that holds our header */
if (!ctx) {
ctx = f->ctx = apr_palloc(f->c->pool, sizeof(*ctx));
ctx->rcvd = 0;
ctx->need = MIN_HDR_LEN;
ctx->version = 0;
ctx->mode = AP_MODE_READBYTES;
ctx->bb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
ctx->done = 0;
}
if (ctx->done) {
/* Note: because we're a connection filter we can't remove ourselves
* when we're done, so we have to stay in the chain and just go into
* passthrough mode.
*/
return ap_get_brigade(f->next, bb_out, mode, block, readbytes);
}
conn_conf = ap_get_module_config(f->c->conn_config, &proxy_protocol_module);
/* try to read a header's worth of data */
while (!ctx->done) {
if (APR_BRIGADE_EMPTY(ctx->bb)) {
apr_off_t got, want = ctx->need - ctx->rcvd;
ret = ap_get_brigade(f->next, ctx->bb, ctx->mode, block, want);
if (ret != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, f->c, APLOGNO(10184)
"failed reading input");
return ret;
}
ret = apr_brigade_length(ctx->bb, 1, &got);
if (ret || got > want) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, f->c, APLOGNO(10185)
"RemoteIPProxyProtocol header too long, "
"got %" APR_OFF_T_FMT " expected %" APR_OFF_T_FMT,
got, want);
f->c->aborted = 1;
return APR_ECONNABORTED;
}
}
if (APR_BRIGADE_EMPTY(ctx->bb)) {
return APR_EOF;
}
while (!ctx->done && !APR_BRIGADE_EMPTY(ctx->bb)) {
b = APR_BRIGADE_FIRST(ctx->bb);
ret = apr_bucket_read(b, &ptr, &len, block);
if (APR_STATUS_IS_EAGAIN(ret) && block == APR_NONBLOCK_READ) {
return APR_SUCCESS;
}
if (ret != APR_SUCCESS) {
return ret;
}
memcpy(ctx->header + ctx->rcvd, ptr, len);
ctx->rcvd += len;
apr_bucket_delete(b);
psts = HDR_NEED_MORE;
if (ctx->version == 0) {
/* reading initial chunk */
if (ctx->rcvd >= MIN_HDR_LEN) {
ctx->version = pp_determine_version(f->c, ctx->header);
if (ctx->version < 0) {
psts = HDR_ERROR;
}
else if (ctx->version == 1) {
ctx->mode = AP_MODE_GETLINE;
ctx->need = sizeof(proxy_v1);
}
else if (ctx->version == 2) {
ctx->need = MIN_V2_HDR_LEN;
}
}
}
else if (ctx->version == 1) {
psts = pp_process_v1_header(f->c, conn_conf,
(proxy_header *) ctx->header,
ctx->rcvd, &ctx->need);
}
else if (ctx->version == 2) {
proxy_header *hdr = (proxy_header *) ctx->header;
if (ctx->rcvd >= MIN_V2_HDR_LEN) {
ctx->need = MIN_V2_HDR_LEN + ntohs(hdr->v2.len);
if (ctx->need > sizeof(proxy_v2)) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(10186)
"RemoteIPProxyProtocol protocol header length too long");
f->c->aborted = 1;
apr_brigade_destroy(ctx->bb);
return APR_ECONNABORTED;
}
}
if (ctx->rcvd >= ctx->need) {
psts = pp_process_v2_header(f->c, conn_conf, hdr);
}
}
else {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c,
"ProxyProtocol: internal error: unknown version "
"%d", ctx->version);
f->c->aborted = 1;
apr_brigade_destroy(ctx->bb);
return APR_ECONNABORTED;
}
switch (psts) {
case HDR_ERROR:
f->c->aborted = 1;
apr_brigade_destroy(ctx->bb);
return APR_ECONNABORTED;
case HDR_DONE:
ctx->done = 1;
break;
case HDR_NEED_MORE:
break;
}
}
}
/* we only get here when done == 1 */
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
"ProxyProtocol: received valid header: %s:%hu",
conn_conf->client_ip, conn_conf->client_addr->port);
if (ctx->rcvd > ctx->need || !APR_BRIGADE_EMPTY(ctx->bb)) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c,
"ProxyProtocol: internal error: have data left over; "
" need=%lu, rcvd=%lu, brigade-empty=%d", ctx->need,
ctx->rcvd, APR_BRIGADE_EMPTY(ctx->bb));
f->c->aborted = 1;
apr_brigade_destroy(ctx->bb);
return APR_ECONNABORTED;
}
/* clean up */
apr_brigade_destroy(ctx->bb);
ctx->bb = NULL;
/* now do the real read for the upper layer */
return ap_get_brigade(f->next, bb_out, mode, block, readbytes);
}
static void proxy_protocol_register_hooks(apr_pool_t *p)
{
/* mod_ssl is CONNECTION + 5, so we want something higher (earlier);
* mod_reqtimeout is CONNECTION + 8, so we want something lower (later) */
ap_register_input_filter(pp_inp_filter, pp_input_filter, NULL,
AP_FTYPE_CONNECTION + 7);
ap_hook_pre_config(pp_hook_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(pp_hook_post_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_pre_connection(pp_hook_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_read_request(pp_hook_post_read_request, NULL, NULL,
APR_HOOK_REALLY_FIRST);
}
/* Dispatch list for API hooks */
AP_DECLARE_MODULE(proxy_protocol) = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
proxy_protocol_cmds, /* table of config file commands */
proxy_protocol_register_hooks /* register hooks */
};

126
mod_proxy_protocol.xml Normal file
View File

@ -0,0 +1,126 @@
<?xml version="1.0"?>
<!DOCTYPE modulesynopsis SYSTEM "http://httpd.apache.org/docs/2.4/style/modulesynopsis.dtd">
<?xml-stylesheet type="text/xsl" href="http://httpd.apache.org/docs/2.4/style/manual.en.xsl"?>
<!--
Copyright 2014 Cloudzilla Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<modulesynopsis metafile="mod_proxy_protocol.xml.meta">
<name>mod_proxy_protocol</name>
<description>Implements the server side of the proxy protocol.</description>
<status>Extension</status>
<sourcefile>mod_proxy_protocol.c</sourcefile>
<identifier>proxy_protocol_module</identifier>
<summary>
<p><module>mod_proxy_protocol</module> implements the server side of
HAProxy's
<a href="http://blog.haproxy.com/haproxy/proxy-protocol/">Proxy Protocol</a>.</p>
<p>The module overrides the client IP address for the connection
with the information supplied by the upstream proxy in the proxy
protocol (connection) header.</p>
<p>This overridden useragent IP address is then used for the
<module>mod_authz_host</module>
<directive module="mod_authz_core" name="require">Require ip</directive>
feature, is reported by <module>mod_status</module>, and is recorded by
<module>mod_log_config</module> <code>%a</code> and <module>core</module>
<code>%a</code> format strings. The underlying client IP of the connection
is available in the <code>%{c}a</code> format string.</p>
<note type="warning">It is critical to only enable this behavior from
intermediate proxies which are trusted by this server, since it is trivial
for the remote client to impersonate another client. Currently this must
be done by external means (such as a firewall) as this module does not
(yet) implement access controls.</note>
</summary>
<seealso><a href="http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt">Proxy Protocol Spec</a></seealso>
<directivesynopsis>
<name>ProxyProtocol</name>
<description>Enable or disable the proxy protocol handling</description>
<syntax>ProxyProtocol On|Off</syntax>
<contextlist><context>server config</context><context>virtual host</context>
</contextlist>
<usage>
<p>The <directive>ProxyProtocol</directive> enables or disables the
reading and handling of the proxy protocol connection header. If enabled
the upstream client <em>must</em> send the header every time it opens a
connection or the connection will get aborted.</p>
<p>While this directive may be specified in any virtual host, it is
important to understand that because the proxy protocol is connection
based and protocol agnostic, the enabling and disabling is actually based
on ip-address and port. This means that if you have multiple name-based
virtual hosts for the same host and port, and you enable it any one of
them, then it is enabled for all them (with that host and port). It also
means that if you attempt to enable the proxy protocol in one and disable
in the other, that won't work; in such a case the last one wins and a
notice will be logged indicating which setting was being overridden.</p>
<highlight language="config">
ProxyProtocol On
</highlight>
</usage>
</directivesynopsis>
<!--
<directivesynopsis>
<name>ProxyProtocolTrustedProxies</name>
<description>A listed of clients that are trusted to provide the proxy
protocol header.</description>
<syntax>ProxyProtocolTrustedProxies <var>levels</var></syntax>
<syntax>ProxyProtocolTrustedProxies all|<var>host</var> [<var>host</var>] ...</syntax>
<default>ProxyProtocolTrustedProxies all</default>
<contextlist><context>server config</context><context>virtual host</context>
</contextlist>
<usage>
<p>The <directive>ProxyProtocolTrustedProxies</directive> directive limits
which clients are trusted to use the proxy protocol. What happens when a
client is not trusted is controlled by the
<directive module="mod_proxy_protocol">ProxyProtocolRejectUntrusted</directive>
directive.</p>
</usage>
</directivesynopsis>
<directivesynopsis>
<name>ProxyProtocolRejectUntrusted</name>
<description>The number of characters in subdirectory names</description>
<syntax>ProxyProtocolRejectUntrusted On|Off</syntax>
<default>ProxyProtocolRejectUntrusted On</default>
<contextlist><context>server config</context><context>virtual host</context>
</contextlist>
<usage>
<p>The <directive>ProxyProtocolRejectUntrusted</directive> directive
controls the behavior when a connection is received from an untrusted
client (as configured by the
<directive module="mod_proxy_protocol">ProxyProtocolTrustedProxies</directive>
directive) on a host and port for which the proxy protocol has been enabled.
If set to On (the default) then the connection is aborted; if set to Off
then the connection is allowed, and client must send a valid proxy protocol
header, but the contents of the header are ignored and the client IP for
the connection left untouched (i.e. will be that of the immediate client).
</p>
</usage>
</directivesynopsis>
-->
</modulesynopsis>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<metafile reference="mod_proxy_protocol.xml">
<basename>mod_proxy_protocol</basename>
<path>/mod/</path>
<relpath>https://httpd.apache.org/docs/2.4</relpath>
</metafile>