MEDIUM: resolvers: add global "dns-accept-family" directive
By default, DNS resolvers accept both IPv4 and IPv6 addresses. This can be influenced by the "resolve-prefer" keywords on server lines as well as the family argument to the "do-resolve" action, but that is only a preference, which does not block the other family from being used when it's alone. In some environments where dual-stack is not usable, stumbling on an unreachable IPv6-only DNS record can cause significant trouble as it will replace a previous IPv4 one which would possibly have continued to work till next request. The "dns-accept-family" global option permits to enforce usage of only one (or both) address families. The argument is a comma-delimited list of the following words: - "ipv4": query and accept IPv4 addresses ("A" records) - "ipv6": query and accept IPv6 addresses ("AAAA" records) When a single family is used, no request will be sent to resolvers for the other family, and any response for the othe family will be ignored. The default value is "ipv4,ipv6", which effectively enables both families.
This commit is contained in:
parent
0e94339eaf
commit
940fa19ad8
@ -1533,6 +1533,7 @@ The following keywords are supported in the "global" section :
|
||||
- deviceatlas-log-level
|
||||
- deviceatlas-properties-cookie
|
||||
- deviceatlas-separator
|
||||
- dns-accept-family
|
||||
- expose-deprecated-directives
|
||||
- expose-experimental-directives
|
||||
- external-check
|
||||
@ -2165,6 +2166,25 @@ deviceatlas-separator <char>
|
||||
Sets the character separator for the API properties results. This directive
|
||||
is optional and set to | by default if not set.
|
||||
|
||||
dns-accept-family <family>[,...]
|
||||
By default, DNS resolvers accept both IPv4 and IPv6 addresses. This can be
|
||||
influenced by the "resolve-prefer" keywords on server lines as well as the
|
||||
family argument to the "do-resolve" action, but that is only a preference,
|
||||
which does not block the other family from being used when it's alone. In
|
||||
some environments where dual-stack is not usable, stumbling on an unreachable
|
||||
IPv6-only DNS record can cause significant trouble as it will replace a
|
||||
previous IPv4 one which would possibly have continued to work till next
|
||||
request. The "dns-accept-family" global option permits to enforce usage of
|
||||
only one (or both) address families. The argument is a comma-delimited list
|
||||
of the following words:
|
||||
- "ipv4": query and accept IPv4 addresses ("A" records)
|
||||
- "ipv6": query and accept IPv6 addresses ("AAAA" records)
|
||||
|
||||
When a single family is used, no request will be sent to resolvers for the
|
||||
other family, and any response for the othe family will be ignored. The
|
||||
default value is "ipv4,ipv6", which effectively enables both families.
|
||||
See also: "resolve-prefer", "do-resolve"
|
||||
|
||||
expose-deprecated-directives
|
||||
This statement must appear before using some directives tagged as deprecated
|
||||
to silent warnings and make sure the config file will not be rejected. Not
|
||||
@ -15871,7 +15891,9 @@ do-resolve(<var>,<resolvers>[,ipv4|ipv6]) <expr>
|
||||
the result in the variable <var>. It uses the DNS resolvers section
|
||||
pointed by <resolvers>.
|
||||
It is possible to choose a resolution preference using the optional
|
||||
arguments 'ipv4' or 'ipv6'.
|
||||
arguments 'ipv4' or 'ipv6'. See also the global "dns-accept-family" keyword
|
||||
to enforce strict usage of a specific family.
|
||||
|
||||
When performing the DNS resolution, the client side connection is on
|
||||
pause waiting till the end of the resolution.
|
||||
If an IP address can be found, it is stored into <var>. If any kind of
|
||||
@ -19379,8 +19401,9 @@ resolve-prefer <family>
|
||||
|
||||
When DNS resolution is enabled for a server and multiple IP addresses from
|
||||
different families are returned, HAProxy will prefer using an IP address
|
||||
from the family mentioned in the "resolve-prefer" parameter.
|
||||
Available families: "ipv4" and "ipv6"
|
||||
from the family mentioned in the "resolve-prefer" parameter. See also the
|
||||
global "dns-accept-family" keyword to enforce strict usage of a specific
|
||||
family. Available families: "ipv4" and "ipv6".
|
||||
|
||||
Default value: ipv6
|
||||
|
||||
|
@ -92,6 +92,13 @@ extern struct pool_head *resolv_requester_pool;
|
||||
*/
|
||||
#define SRV_MAX_PREF_NET 5
|
||||
|
||||
/* bits describing process-wide acceptable address families for DNS responses */
|
||||
enum {
|
||||
RSLV_ACCEPT_IPV4 = 0x01,
|
||||
RSLV_ACCEPT_IPV6 = 0x02,
|
||||
RSLV_ACCEPT_MASK = RSLV_ACCEPT_IPV4 | RSLV_ACCEPT_IPV6,
|
||||
};
|
||||
|
||||
/* NOTE: big endian structure */
|
||||
struct resolv_query_item {
|
||||
char name[DNS_MAX_NAME_SIZE+1]; /* query name */
|
||||
|
@ -32,6 +32,7 @@ struct list;
|
||||
|
||||
extern struct list sec_resolvers;
|
||||
extern unsigned int resolv_failed_resolutions;
|
||||
extern uint resolv_accept_families;
|
||||
|
||||
struct resolvers *find_resolvers_by_id(const char *id);
|
||||
struct dns_nameserver *find_nameserver_by_resolvers_and_id(struct resolvers *parent, unsigned int id);
|
||||
|
@ -69,6 +69,8 @@ DECLARE_POOL(resolv_requester_pool, "resolv_requester", sizeof(struct resolv_r
|
||||
|
||||
static unsigned int resolution_uuid = 1;
|
||||
unsigned int resolv_failed_resolutions = 0;
|
||||
uint resolv_accept_families = RSLV_ACCEPT_IPV4 | RSLV_ACCEPT_IPV6;
|
||||
|
||||
struct task *process_resolvers(struct task *t, void *context, unsigned int state);
|
||||
static void resolv_free_resolution(struct resolv_resolution *resolution);
|
||||
static void _resolv_unlink_resolution(struct resolv_requester *requester);
|
||||
@ -1632,11 +1634,11 @@ int resolv_get_ip_from_response(struct resolv_response *r_res,
|
||||
unsigned char ip_type;
|
||||
|
||||
record = eb32_entry(eb32, typeof(*record), link);
|
||||
if (record->type == DNS_RTYPE_A) {
|
||||
if (record->type == DNS_RTYPE_A && (resolv_accept_families & RSLV_ACCEPT_IPV4)) {
|
||||
ip_type = AF_INET;
|
||||
ip = &record->data.in4.sin_addr;
|
||||
}
|
||||
else if (record->type == DNS_RTYPE_AAAA) {
|
||||
else if (record->type == DNS_RTYPE_AAAA && (resolv_accept_families & RSLV_ACCEPT_IPV6)) {
|
||||
ip_type = AF_INET6;
|
||||
ip = &record->data.in6.sin6_addr;
|
||||
}
|
||||
@ -2067,9 +2069,12 @@ int resolv_link_resolution(void *requester, int requester_type, int requester_lo
|
||||
hostname_dn = &srv->hostname_dn;
|
||||
hostname_dn_len = srv->hostname_dn_len;
|
||||
resolvers = srv->resolvers;
|
||||
query_type = ((srv->resolv_opts.family_prio == AF_INET)
|
||||
|
||||
query_type = !(resolv_accept_families & RSLV_ACCEPT_IPV6) ? DNS_RTYPE_A :
|
||||
!(resolv_accept_families & RSLV_ACCEPT_IPV4) ? DNS_RTYPE_AAAA :
|
||||
(srv->resolv_opts.family_prio == AF_INET)
|
||||
? DNS_RTYPE_A
|
||||
: DNS_RTYPE_AAAA);
|
||||
: DNS_RTYPE_AAAA;
|
||||
break;
|
||||
|
||||
case OBJ_TYPE_SRVRQ:
|
||||
@ -2101,9 +2106,12 @@ int resolv_link_resolution(void *requester, int requester_type, int requester_lo
|
||||
hostname_dn = &stream->resolv_ctx.hostname_dn;
|
||||
hostname_dn_len = stream->resolv_ctx.hostname_dn_len;
|
||||
resolvers = stream->resolv_ctx.parent->arg.resolv.resolvers;
|
||||
query_type = ((stream->resolv_ctx.parent->arg.resolv.opts->family_prio == AF_INET)
|
||||
|
||||
query_type = !(resolv_accept_families & RSLV_ACCEPT_IPV6) ? DNS_RTYPE_A :
|
||||
!(resolv_accept_families & RSLV_ACCEPT_IPV4) ? DNS_RTYPE_AAAA :
|
||||
(stream->resolv_ctx.parent->arg.resolv.opts->family_prio == AF_INET)
|
||||
? DNS_RTYPE_A
|
||||
: DNS_RTYPE_AAAA);
|
||||
: DNS_RTYPE_AAAA;
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
@ -2333,7 +2341,7 @@ static int resolv_process_responses(struct dns_nameserver *ns)
|
||||
if (!res->try)
|
||||
goto report_res_error;
|
||||
}
|
||||
else {
|
||||
else if ((resolv_accept_families & RSLV_ACCEPT_MASK) == (RSLV_ACCEPT_IPV4 | RSLV_ACCEPT_IPV6)) {
|
||||
/* Fallback from A to AAAA or the opposite and re-send
|
||||
* the resolution immediately. try counter is not
|
||||
* decremented. */
|
||||
@ -2462,9 +2470,11 @@ struct task *process_resolvers(struct task *t, void *context, unsigned int state
|
||||
/* Fallback from A to AAAA or the opposite and re-send
|
||||
* the resolution immediately. try counter is not
|
||||
* decremented. */
|
||||
if (res->prefered_query_type == DNS_RTYPE_A)
|
||||
if (res->prefered_query_type == DNS_RTYPE_A &&
|
||||
(resolv_accept_families & RSLV_ACCEPT_IPV6))
|
||||
res->query_type = DNS_RTYPE_AAAA;
|
||||
else if (res->prefered_query_type == DNS_RTYPE_AAAA)
|
||||
else if (res->prefered_query_type == DNS_RTYPE_AAAA &&
|
||||
(resolv_accept_families & RSLV_ACCEPT_IPV4))
|
||||
res->query_type = DNS_RTYPE_A;
|
||||
else
|
||||
res->try--;
|
||||
@ -3925,6 +3935,48 @@ int cfg_post_parse_resolvers()
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/* config parser for global "dns-accept-family", accepts "ipv4", "ipv6" or both delimited by a comma */
|
||||
static int cfg_parse_dns_accept_family(char **args, int section_type, struct proxy *curpx,
|
||||
const struct proxy *defpx, const char *file, int line,
|
||||
char **err)
|
||||
{
|
||||
char *arg, *comma;
|
||||
int accept_families = 0;
|
||||
|
||||
if (too_many_args(1, args, err, NULL))
|
||||
return -1;
|
||||
|
||||
if (!args[1][0])
|
||||
goto usage;
|
||||
|
||||
for (arg = args[1]; arg && *arg; arg = comma) {
|
||||
comma = strchr(arg, ',');
|
||||
if (comma)
|
||||
*(comma++) = 0;
|
||||
|
||||
if (strcmp(arg, "ipv4") == 0)
|
||||
accept_families |= RSLV_ACCEPT_IPV4;
|
||||
else if (strcmp(arg, "ipv6") == 0)
|
||||
accept_families |= RSLV_ACCEPT_IPV6;
|
||||
else
|
||||
goto usage;
|
||||
}
|
||||
|
||||
resolv_accept_families = accept_families;
|
||||
return 0;
|
||||
usage:
|
||||
memprintf(err, "'%s' expects a comma-delimited list of 'ipv4' and 'ipv6' but got '%s'.", args[0], args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* config keyword parsers */
|
||||
static struct cfg_kw_list cfg_kws = {ILH, {
|
||||
{ CFG_GLOBAL, "dns-accept-family", cfg_parse_dns_accept_family },
|
||||
{ 0, NULL, NULL }
|
||||
}};
|
||||
|
||||
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
||||
|
||||
REGISTER_CONFIG_SECTION("resolvers", cfg_parse_resolvers, cfg_post_parse_resolvers);
|
||||
REGISTER_POST_DEINIT(resolvers_deinit);
|
||||
REGISTER_CONFIG_POSTPARSER("dns runtime resolver", resolvers_finalize_config);
|
||||
|
Loading…
x
Reference in New Issue
Block a user