Add minimum mask parameters and a real world example

parent 97344ac3
......@@ -19,13 +19,14 @@ AM_TESTS_ENVIRONMENT = \
PATH="$(abs_builddir):$(VARNISH_TEST_PATH):$(PATH)" \
LD_LIBRARY_PATH="$(VARNISH_LIBRARY_PATH)"
TEST_EXTENSIONS = .vtc
VTC_LOG_COMPILER = varnishtest -v
VTC_LOG_COMPILER = varnishtest -lv
AM_VTC_LOG_FLAGS = \
-p vcl_path="$(abs_top_srcdir)/vcl:$(VARNISHAPI_VCLDIR)" \
-p vmod_path="$(abs_builddir)/.libs:$(vmoddir):$(VARNISHAPI_VMODDIR)"
TESTS = \
vtc/vmod_acltools.vtc
vtc/vmod_acltools.vtc \
vtc/vmod_acltools_checkmask.vtc
# Documentation
......
......@@ -123,12 +123,27 @@ vmod_dyn_single(VRT_CTX, struct VARGS(dyn_single)*args)
unsigned const char *addr;
struct vmod_priv *priv_task;
unsigned long mask = ULONG_MAX;
unsigned maxmask;
unsigned minmask, maxmask;
uintptr_t sn;
int quot = 0;
int fam;
VCL_ACL fb;
if (args->min_mask_ip4 < 0 || args->min_mask_ip6 < 0) {
VRT_fail(ctx, "min_mask_* arguments must not be negative");
return (&acl_fail);
}
if (args->min_mask_ip4 > 32) {
VRT_fail(ctx, "min_mask_ip4 value too largs (%ld)",
args->min_mask_ip4);
return (&acl_fail);
}
if (args->min_mask_ip6 > 128) {
VRT_fail(ctx, "min_mask_ip6 value too largs (%ld)",
args->min_mask_ip6);
return (&acl_fail);
}
if (args->valid_fallback && args->fallback != NULL &&
args->fallback->magic == VRT_ACL_MAGIC)
fb = args->fallback;
......@@ -218,9 +233,11 @@ vmod_dyn_single(VRT_CTX, struct VARGS(dyn_single)*args)
fam = VSA_GetPtr(vsa, &addr);
switch (fam) {
case PF_INET:
minmask = args->min_mask_ip4;
maxmask = 32;
break;
case PF_INET6:
minmask = args->min_mask_ip6;
maxmask = 128;
break;
default:
......@@ -230,12 +247,19 @@ vmod_dyn_single(VRT_CTX, struct VARGS(dyn_single)*args)
if (mask == ULONG_MAX)
mask = maxmask;
else if (mask > maxmask) {
VSLb(ctx->vsl, SLT_Error,
"acltools.dyn_single: mask %lu too long for %s",
VSLb(ctx->vsl, SLT_Error, "acltools.dyn_single: "
"mask %lu too long for %s",
mask, args->ipmask);
WS_Reset(ctx->ws, sn);
return (fb);
}
else if (mask < minmask) {
VSLb(ctx->vsl, SLT_Error, "acltools.dyn_single: "
"mask %lu shorter than minimum %d for %s",
mask, minmask, args->ipmask);
WS_Reset(ctx->ws, sn);
return (fb);
}
ads->mask = mask;
ads->acl.magic = VRT_ACL_MAGIC;
......
......@@ -23,7 +23,7 @@ SYNOPSIS
import acltools [as name] [from "path"]
ACL dyn_single(STRING ipmask, [ACL fallback], BOOL resolve)
ACL dyn_single(STRING ipmask, [ACL fallback], BOOL resolve, INT min_mask_ip4, INT min_mask_ip6)
DESCRIPTION
===========
......@@ -33,12 +33,18 @@ This vmod contains additional functions for Access Control Lists
.. _acltools.dyn_single():
ACL dyn_single(STRING ipmask, [ACL fallback], BOOL resolve)
-----------------------------------------------------------
ACL dyn_single(STRING ipmask, [ACL fallback], BOOL resolve, INT min_mask_ip4, INT min_mask_ip6)
-----------------------------------------------------------------------------------------------
::
ACL dyn_single(STRING ipmask, [ACL fallback], BOOL resolve=0)
ACL dyn_single(
STRING ipmask,
[ACL fallback],
BOOL resolve=0,
INT min_mask_ip4=0,
INT min_mask_ip6=0
)
Returns a dynamic ACL **for immediate use** (see **NOTE** below) with
a single entry constructed from the *ipmask* string, which must be of
......@@ -54,8 +60,8 @@ or, for compatibility with built-in static ACLs::
*<ip>* undergoes the same parsing as the *s* argument of
`std.ip()`_. If *<ip>* is an IPv4 address, *<mask>*, if present, must
be an integer between 0 and 32. If *<ip>* is an IPv6 address,
*<mask>*, if present, must be an integer between 0 and 128. If
be an integer between *min_mask_ip4* and 32. If *<ip>* is an IPv6 address,
*<mask>*, if present, must be an integer between *min_mask_ip6* and 128. If
*<mask>* is not present, it defaults to the maximum mask (32 or 128,
respectively).
......@@ -67,6 +73,13 @@ fail.
If the *resolve* argument is ``true``, *<ip>* will be resolved as a
DNS name.
*min_mask_ip4* must be an integer between 0 and 32, inclusively.
*min_mask_ip6* must be an integer between 0 and 128, inclusively.
For both *min_mask_* arguments, a VCL error is triggered if they are
out of range. Both default to zero.
Example
::
......
......@@ -7,7 +7,7 @@ This vmod contains additional functions for Access Control Lists
(ACLs).
$Function ACL dyn_single(STRING ipmask, [ACL fallback],
BOOL resolve = 0)
BOOL resolve = 0, INT min_mask_ip4 = 0, INT min_mask_ip6 = 0)
Returns a dynamic ACL **for immediate use** (see **NOTE** below) with
a single entry constructed from the *ipmask* string, which must be of
......@@ -23,8 +23,8 @@ or, for compatibility with built-in static ACLs::
*<ip>* undergoes the same parsing as the *s* argument of
`std.ip()`_. If *<ip>* is an IPv4 address, *<mask>*, if present, must
be an integer between 0 and 32. If *<ip>* is an IPv6 address,
*<mask>*, if present, must be an integer between 0 and 128. If
be an integer between *min_mask_ip4* and 32. If *<ip>* is an IPv6 address,
*<mask>*, if present, must be an integer between *min_mask_ip6* and 128. If
*<mask>* is not present, it defaults to the maximum mask (32 or 128,
respectively).
......@@ -36,6 +36,13 @@ fail.
If the *resolve* argument is ``true``, *<ip>* will be resolved as a
DNS name.
*min_mask_ip4* must be an integer between 0 and 32, inclusively.
*min_mask_ip6* must be an integer between 0 and 128, inclusively.
For both *min_mask_* arguments, a VCL error is triggered if they are
out of range. Both default to zero.
Example
::
......
......@@ -24,7 +24,36 @@ varnish v1 -proto PROXY -vcl {
} else {
return (synth(403, "denied"));
}
} else if (req.url == "/cov4") {
if (client.ip ~ acltools.dyn_single(req.http.acl,
fallback=fallback, min_mask_ip4 = -1)) {
return (synth(200, "matched"));
} else {
return (synth(403, "denied"));
}
} else if (req.url == "/cov6") {
if (client.ip ~ acltools.dyn_single(req.http.acl,
fallback=fallback, min_mask_ip6 = -1)) {
return (synth(200, "matched"));
} else {
return (synth(403, "denied"));
}
} else if (req.url == "/cov4l") {
if (client.ip ~ acltools.dyn_single(req.http.acl,
fallback=fallback, min_mask_ip4 = 33)) {
return (synth(200, "matched"));
} else {
return (synth(403, "denied"));
}
} else if (req.url == "/cov6l") {
if (client.ip ~ acltools.dyn_single(req.http.acl,
fallback=fallback, min_mask_ip6 = 129)) {
return (synth(200, "matched"));
} else {
return (synth(403, "denied"));
}
}
return (synth(404));
}
} -start
......@@ -242,8 +271,35 @@ client c3 -proxy2 "[102:304:506::d0e:f10]:2314 [8182:8384:8586::8d8e:8f80]:2828"
expect resp.status == 403
} -start
client c4 -proxy2 "[102:304:506::d0e:f10]:2314 [8182:8384:8586::8d8e:8f80]:2828" {
txreq -url "/cov4"
rxresp
expect resp.status == 503
} -start
client c5 -proxy2 "[102:304:506::d0e:f10]:2314 [8182:8384:8586::8d8e:8f80]:2828" {
txreq -url "/cov6"
rxresp
expect resp.status == 503
} -start
client c6 -proxy2 "[102:304:506::d0e:f10]:2314 [8182:8384:8586::8d8e:8f80]:2828" {
txreq -url "/cov4l"
rxresp
expect resp.status == 503
} -start
client c7 -proxy2 "[102:304:506::d0e:f10]:2314 [8182:8384:8586::8d8e:8f80]:2828" {
txreq -url "/cov6l"
rxresp
expect resp.status == 503
} -start
client c1 -wait
client c2 -wait
client c3 -wait
client c4 -wait
client c5 -wait
client c6 -wait
client c7 -wait
logexpect l1 -wait
logexpect l2 -wait
\ No newline at end of file
varnishtest "acltools real world example: check with minimum mask"
# real world use case: we get a <net/mask> and an <ip> and want to
# check if <ip> is contained in <net/mask>, with a limit by protocol
varnish v1 -vcl {
import acltools;
import std;
backend none none;
sub vcl_recv {
if (std.ip(req.http.ip, "0.0.0.0", resolve=false) ~
acltools.dyn_single(req.http.net,
min_mask_ip4 = 24,
min_mask_ip6 = 40)) {
return (synth(200));
}
return (synth(403));
}
} -start
client c1 {
txreq -hdr "ip: 1.2.3.4" -hdr "net: 1.2.3.0/24"
rxresp
expect resp.status == 200
txreq -hdr "ip: 1.2.3.4" -hdr "net: 1.2.3.0/25"
rxresp
expect resp.status == 200
txreq -hdr "ip: 1.2.3.4" -hdr "net: 1.2.2.0/23"
rxresp
expect resp.status == 403
} -run
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment