summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2020-03-25 11:44:40 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2020-03-25 11:48:56 (GMT)
commite801d94e40f23c09f27a807fec48fc0133ddb1c6 (patch) (side-by-side diff)
treea7cae42d7f81c4becddecdf69fc854a42e81e555
parenta6d6598a99f654cf25a65e512ce7bf4856fda3b2 (diff)
downloadvmod-remoteip-e801d94e40f23c09f27a807fec48fc0133ddb1c6.tar.gz
vmod-remoteip-e801d94e40f23c09f27a807fec48fc0133ddb1c6.tar.bz2
Use ACL to hold IPs of the trusted proxies.v2.0
Suggested by Dridi Boukelmoune.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--NEWS9
-rw-r--r--README11
-rw-r--r--configure.ac2
-rw-r--r--src/vmod_remoteip.c313
-rw-r--r--src/vmod_remoteip.vcc34
-rw-r--r--tests/ipv4found.at8
-rw-r--r--tests/ipv4notfound.at8
-rw-r--r--tests/ipv6found.at8
-rw-r--r--tests/ipv6notfound.at8
9 files changed, 66 insertions, 335 deletions
diff --git a/NEWS b/NEWS
index 85185fc..aed17ee 100644
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,15 @@
-vmod_remoteip -- history of user-visible changes. 2020-03-21
+vmod_remoteip -- history of user-visible changes. 2020-03-25
See the end of file for copying conditions.
Please send vmod_remoteip bug reports to <gray@gnu.org>
+Version 2.0, 2020-03-25
+
+* Use ACL to hold IPs of the trusted proxies.
+
+The ACL name is passed as the first argument to the remoteip.get
+function. The remoteip.init function is now gone.
+
Version 1.0, 2020-03-21
Initial release
diff --git a/README b/README
index ca814ba..d022c28 100644
--- a/README
+++ b/README
@@ -20,10 +20,11 @@ hosts from the ACL "allowed":
#+BEGIN_SRC vcl-script
import std;
import remoteip;
-
- sub vcl_init {
- // Register trusted proxy server addresses
- remoteip.init("192.0.2.1, 127.0.0.1");
+
+ // Register trusted proxy server addresses
+ acl trusted {
+ "192.0.2.1";
+ "127.0.0.1";
}
acl allowed {
@@ -32,7 +33,7 @@ hosts from the ACL "allowed":
}
sub vcl_recv {
- set req.http.x-real-ip = remoteip.get(req.http.X-Forwarded-For);
+ set req.http.x-real-ip = remoteip.get(trusted, req.http.X-Forwarded-For);
if (std.ip(req.http.x-real-ip) ~ allowed) {
...
}
diff --git a/configure.ac b/configure.ac
index cc8480a..1fcc3bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,7 +15,7 @@
# along with vmod_remoteip. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.69)
-AC_INIT([vmod-remoteip], [1.0], [gray@gnu.org])
+AC_INIT([vmod-remoteip], [2.0], [gray@gnu.org])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR(src/vmod_remoteip.vcc)
diff --git a/src/vmod_remoteip.c b/src/vmod_remoteip.c
index 528b0a4..8bba147 100644
--- a/src/vmod_remoteip.c
+++ b/src/vmod_remoteip.c
@@ -26,314 +26,43 @@
#include <cache/cache.h>
#include <vcl.h>
#include <vcc_if.h>
-#include <vqueue.h>
-#ifdef VPFX
-# define VEVENT(a) VPFX(a)
-#else
-/* For compatibility with varnish prior to 6.2 */
-# define VEVENT(a) a
-#endif
-
-/* Max. number of bytes in an IPv6 address */
-#define MU_INADDR_BYTES 16
-
-/* CIDR representation */
-struct cidr {
- int family; /* Address family */
- int len; /* Number of bytes in the address */
- unsigned char address[MU_INADDR_BYTES];
- unsigned char netmask[MU_INADDR_BYTES];
- VSLIST_ENTRY(cidr) next;
-};
-
-typedef VSLIST_HEAD(,cidr) CIDRHEAD;
-
-/* Returns 1 if ADDR is a valid string representation of IPv4 address */
-static int
-str_is_ipv4(const char *addr, size_t len)
-{
- int dot_count = 0;
- int digit_count = 0;
-
- for (; len; addr++, len--) {
- if (!isascii(*addr))
- return 0;
- if (*addr == '.') {
- if (++dot_count > 3)
- break;
- digit_count = 0;
- }
- else if (!(isdigit(*addr) && ++digit_count <= 3))
- return 0;
- }
- return (dot_count == 3);
-}
-
-#define PFXSTR_IPV4_MAPPED "::ffff:"
-#define PFXLEN_IPV4_MAPPED (sizeof PFXSTR_IPV4_MAPPED - 1)
-
-static int
-str_is_ipv4mapped(const char *addr, size_t len)
-{
- return len > PFXLEN_IPV4_MAPPED
- && strncasecmp(PFXSTR_IPV4_MAPPED, addr, PFXLEN_IPV4_MAPPED)
- == 0
- && str_is_ipv4(addr + PFXLEN_IPV4_MAPPED, len - PFXLEN_IPV4_MAPPED);
-}
-
-/* Returns 1 if ADDR is a valid IPv6 address */
-static int
-str_is_ipv6(const char *addr, size_t len)
-{
- int col_count = 0; /* Number of colons */
- int dcol = 0; /* Did we encounter a double-colon? */
- int dig_count = 0; /* Number of digits in the last group */
-
- for (; len; addr++, len--) {
- if (!isascii (*addr))
- return 0;
- else if (isxdigit(*addr)) {
- if (++dig_count > 4)
- return 0;
- } else if (*addr == ':') {
- if (col_count && dig_count == 0 && ++dcol > 1)
- return 0;
- if (++col_count > 7)
- return 0;
- dig_count = 0;
- }
- else
- return 0;
- }
- return (col_count == 7 || dcol);
-}
-
-static void
-uint32_to_bytes(unsigned char *bytes, uint32_t u)
-{
- int i;
-
- for (i = 0; i < 4; i++) {
- bytes[i] = u & 0xff;
- u >>= 8;
- }
-}
+#include <vsa.h>
static int
-inaddr_to_bytes(int af, void *buf, unsigned char *bytes)
-{
- uint32_t u;
-
- switch (af) {
- case AF_INET:
- memcpy(&u, buf, sizeof u);
- uint32_to_bytes(bytes, u);
- return 4;
-
- case AF_INET6:
- memcpy(bytes, buf, 16);
- return 16;
- }
-
- abort();
-}
-
-/* Fill in the BUF (LEN bytes long) with a network mask corresponding to
- the mask length MASKLEN */
-static void
-masklen_to_netmask(unsigned char *buf, size_t len, size_t masklen)
-{
- int i, cnt;
-
- cnt = masklen / 8;
- for (i = 0; i < cnt; i++)
- buf[i] = 0xff;
- if (i == len)
- return;
- cnt = 8 - masklen % 8;
- buf[i++] = (0xff >> cnt) << cnt;
- for (; i < len; i++)
- buf[i] = 0;
-}
-
-#define CIDR_MAXBUFSIZE 81
-
-/* Convert string STR to CIDR. Return 0 on success and -1 on failure. */
-static int
-str_to_cidr(char const *str, struct cidr *cidr, char **endp)
+is_trusted_ip(VRT_CTX, VCL_ACL acl, char const *ipstr)
{
+ struct addrinfo hints;
+ struct addrinfo *res;
+ struct suckaddr *ip;
int rc;
- char *ipbuf, *ipstart;
- union {
- struct in_addr in;
- struct in6_addr in6;
- } inaddr;
- size_t len;
-
- len = strspn(str, "0123456789abcdefABCDEF.:");
- ipbuf = malloc(len + 1);
- AN(ipbuf);
- memcpy(ipbuf, str, len);
- ipbuf[len] = 0;
- str += len;
-
- ipstart = ipbuf;
-
- if (str_is_ipv4(ipstart, len))
- cidr->family = AF_INET;
- else if (str_is_ipv4mapped(ipstart, len)) {
- cidr->family = AF_INET;
- ipstart += PFXLEN_IPV4_MAPPED;
- } else if (str_is_ipv6(ipbuf, len))
- cidr->family = AF_INET6;
- else {
- free(ipbuf);
- syslog(LOG_DAEMON|LOG_ERR, "invalid CIDR: %s", ipstart);
- return -1;
- }
- rc = inet_pton(cidr->family, ipstart, &inaddr);
- free(ipbuf);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
- if (rc != 1) {
- if (rc == -1) {
- syslog(LOG_DAEMON|LOG_ERR, "invalid address family");
- return -1;
- }
- if (rc == 0) {
- syslog(LOG_DAEMON|LOG_ERR,
- "invalid IPv%s address: %s",
- cidr->family == AF_INET ? "4" : "6",
- str - len);
- return -1;
- }
- }
+ rc = getaddrinfo(ipstr, NULL, &hints, &res);
+ if (rc)
+ return 0;
- cidr->len = inaddr_to_bytes(cidr->family, &inaddr, cidr->address);
+ ip = VSA_Malloc(res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
- if (*str == '/') {
- char *end;
- unsigned long masklen;
-
- str++;
+ rc = VRT_acl_match(ctx, acl, ip);
- masklen = strtoul(str, &end, 10);
- if (*end == 0) {
- masklen_to_netmask(cidr->netmask, cidr->len, masklen);
- str = end;
- } else {
- size_t k = strspn(str, "0123456789abcdefABCDEF.:");
-
- if ((cidr->family == AF_INET
- && str_is_ipv4(str, k))
- || (cidr->family == AF_INET6
- && str_is_ipv6(str, k))) {
- char mbuf[CIDR_MAXBUFSIZE];
- memcpy(mbuf, str, k);
- mbuf[k] = 0;
- rc = inet_pton(cidr->family, mbuf, &inaddr);
- if (rc != 1) {
- syslog(LOG_DAEMON|LOG_ERR,
- "bad netmask: %s", mbuf);
- return -1;
- }
- inaddr_to_bytes(cidr->family, &inaddr,
- cidr->netmask);
- } else {
- syslog(LOG_DAEMON|LOG_ERR,
- "bad CIDR (near %s)", str);
- return -1;
- }
- }
- } else
- masklen_to_netmask(cidr->netmask, cidr->len, cidr->len * 8);
- *endp = (char*) str;
- return 0;
-}
-
-/* A is a valid CIDR, and B is a valid IP address represented as CIDR.
- Return 0 if B falls within A */
-static int
-cidr_match(struct cidr *a, struct cidr *b)
-{
- int i;
-
- if (a->family != b->family)
- return 1;
- for (i = 0; i < a->len; i++) {
- if (a->address[i] != (b->address[i] & a->netmask[i]))
- return 1;
- }
- return 0;
-}
-
-static int
-is_trusted_ip(CIDRHEAD *cl, char const *ipstr)
-{
- struct cidr cidr, *cp;
- char *endp;
-
- VSLIST_FOREACH(cp, cl, next) {
- if (str_to_cidr(ipstr, &cidr, &endp) || *endp)
- return 0;
- if (cidr_match(cp, &cidr) == 0)
- return 1;
- }
- return 0;
+ free(ip);
+
+ return rc;
}
-int
-VEVENT(remoteip_event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
-{
- CIDRHEAD *p;
-
- switch (e) {
- case VCL_EVENT_LOAD:
- p = calloc(1, sizeof(*p));
- AN(p);
- priv->priv = p;
- break;
- default:
- break;
- }
- return 0;
-}
-
-void
-vmod_init(VRT_CTX, struct vmod_priv *priv, VCL_STRING s)
-{
- CIDRHEAD *cl = priv->priv;
- struct cidr *cidr;
- char *endp;
-
- while (*s) {
- cidr = calloc(1, sizeof(cidr[0]));
- if (str_to_cidr(s, cidr, &endp)) {
- free(cidr);
- break;
- }
- VSLIST_INSERT_HEAD(cl, cidr, next);
- while (*endp && isspace(*endp))
- ++endp;
- if (*endp == 0)
- break;
- AN(*endp == ',');
- ++endp;
- while (*endp && isspace(*endp))
- ++endp;
- s = endp;
- }
-}
-
VCL_STRING
-vmod_get(VRT_CTX, struct vmod_priv *priv, VCL_STRING hdr)
+vmod_get(VRT_CTX, VCL_ACL acl, VCL_STRING hdr)
{
- CIDRHEAD *cl = priv->priv;
unsigned u;
char const *end;
char *buf;
int i = 0;
-
+
u = WS_ReserveAll(ctx->ws);
buf = ctx->ws->f;
@@ -360,7 +89,7 @@ vmod_get(VRT_CTX, struct vmod_priv *priv, VCL_STRING hdr)
}
buf[i++] = 0;
- if (!is_trusted_ip(cl, buf)) {
+ if (!is_trusted_ip(ctx, acl, buf)) {
break;
}
end = p;
diff --git a/src/vmod_remoteip.vcc b/src/vmod_remoteip.vcc
index 55b161e..e1c81f6 100644
--- a/src/vmod_remoteip.vcc
+++ b/src/vmod_remoteip.vcc
@@ -3,7 +3,7 @@ $Module remoteip 3 "Return probable IP address based on request headers"
DESCRIPTION
===========
-This modules is for Varnish Cache what mod_remoteip is for Apache. It
+This module is for Varnish Cache what mod_remoteip is for Apache. It
determines the actual client IP address for the connection, using the
useragent IP address list presented by a proxies or a load balancer
via the request headers and a preconfigred list of trusted IP
@@ -12,43 +12,29 @@ balancer or yet another reverse proxy (such as pound or haproxy to
handle the TLS connection), you can use this module to get the real
incoming connection IP address from the **X-Forwarded-For** header.
-$Event remoteip_event
-
-$Function VOID init(PRIV_VCL, STRING trusted)
-
-Description
- Initializes the module. The **trusted** argument is a string
- containing comma-separated list of IP addresses of hosts which
- are trusted to correctly set the value of the **X-Forwarded-For**
- header (or other header whose value is used as argument in the
- **remoteip.get** call, which is described below). Arbitrary
- amount of whitespace is allowed to surround each address.
- Each address cna be a valid IPv4 or IPv6 address, optionally
- followed by a slash and the netmask or netmask length.
-
-$Function STRING get(PRIV_VCL, STRING header)
+$Function STRING get(ACL acl, STRING header)
Description
The **header** argument is the value of the **X-Forwarded-For**
or a similar header, i.e. a comma-delimited list of useragent
IP addresses with optional whitespace around them. The
function scans this list from right to left, comparing each
- address with the trusted IP address list, configured with a
- prior call to **init**. Processing halts when the IP address
- is not found in that list or when the list is exhausted. In
- the latter case, the first address from the **header** list is
- returned.
+ address with the trusted IPs from the *acl* argument. Processing
+ halts when the IP address is not found in that list or when the
+ list is exhausted. In the latter case, the first address from the
+ **header** list is returned.
EXAMPLE
=======
::
- sub vcl_init {
- remoteip.init("192.0.2.1, 127.0.0.0/8");
+ acl trusted {
+ "192.0.2.1";
+ "127.0.0.0/8";
}
sub vcl_recv {
- set req.http.x-real-ip = remoteip.get(req.http.X-Forwarded-For);
+ set req.http.x-real-ip = remoteip.get(acl, req.http.X-Forwarded-For);
...
}
diff --git a/tests/ipv4found.at b/tests/ipv4found.at
index 23666da..c6668ef 100644
--- a/tests/ipv4found.at
+++ b/tests/ipv4found.at
@@ -16,11 +16,13 @@
AT_SETUP([IPv4 found])
AT_VARNISHTEST([
- sub vcl_init {
- remoteip.init("198.51.100.1, 192.0.2.1, 127.0.0.0/8");
+ acl trusted {
+ "198.51.100.1";
+ "192.0.2.1";
+ "127.0.0.0/8";
}
sub vcl_deliver {
- set resp.http.result = remoteip.get("203.0.113.10, 203.0.113.2, 192.0.2.1, 127.0.0.1");
+ set resp.http.result = remoteip.get(trusted, "203.0.113.10, 203.0.113.2, 192.0.2.1, 127.0.0.1");
}
],
[ txreq
diff --git a/tests/ipv4notfound.at b/tests/ipv4notfound.at
index 0184d96..b91fa33 100644
--- a/tests/ipv4notfound.at
+++ b/tests/ipv4notfound.at
@@ -16,11 +16,13 @@
AT_SETUP([IPv4 not found])
AT_VARNISHTEST([
- sub vcl_init {
- remoteip.init("198.51.100.1, 192.0.2.1, 127.0.0.0/8");
+ acl trusted {
+ "198.51.100.1";
+ "192.0.2.1";
+ "127.0.0.0/8";
}
sub vcl_deliver {
- set resp.http.result = remoteip.get("198.51.100.1, 192.0.2.1, 127.0.0.1");
+ set resp.http.result = remoteip.get(trusted, "198.51.100.1, 192.0.2.1, 127.0.0.1");
}
],
[ txreq
diff --git a/tests/ipv6found.at b/tests/ipv6found.at
index 8935b71..a1e4d23 100644
--- a/tests/ipv6found.at
+++ b/tests/ipv6found.at
@@ -16,11 +16,13 @@
AT_SETUP([IPv6 found])
AT_VARNISHTEST([
- sub vcl_init {
- remoteip.init("2001:DB8:AC12::1, 2001:DB8:ACB2::1, 2001:DB8:AC12::10");
+ acl trusted {
+ "2001:DB8:AC12::1";
+ "2001:DB8:ACB2::1";
+ "2001:DB8:AC12::10";
}
sub vcl_deliver {
- set resp.http.result = remoteip.get("2001:DB8:BE01::10, 2001:DB8:BFEE::1, 2001:DB8:AC12::1, 2001:DB8:ACB2::1");
+ set resp.http.result = remoteip.get(trusted, "2001:DB8:BE01::10, 2001:DB8:BFEE::1, 2001:DB8:AC12::1, 2001:DB8:ACB2::1");
}
],
[ txreq
diff --git a/tests/ipv6notfound.at b/tests/ipv6notfound.at
index f93c96b..cae6937 100644
--- a/tests/ipv6notfound.at
+++ b/tests/ipv6notfound.at
@@ -16,11 +16,13 @@
AT_SETUP([IPv6 not found])
AT_VARNISHTEST([
- sub vcl_init {
- remoteip.init("2001:DB8:AC12::1, 2001:DB8:ACB2::1, 2001:DB8:AC12::10");
+ acl trusted {
+ "2001:DB8:AC12::1";
+ "2001:DB8:ACB2::1";
+ "2001:DB8:AC12::10";
}
sub vcl_deliver {
- set resp.http.result = remoteip.get("2001:DB8:ACB2::1, 2001:DB8:AC12::1, 2001:DB8:AC12::10");
+ set resp.http.result = remoteip.get(trusted, "2001:DB8:ACB2::1, 2001:DB8:AC12::1, 2001:DB8:AC12::10");
}
],
[ txreq

Return to:

Send suggestions and report system problems to the System administrator.