Commit adf5051a authored by Ondřej Surý's avatar Ondřej Surý

Merge branch '1144-dns-over-https-server' into 'main'

Resolve "Encrypted DNS - RFC 8484, DNS over HTTPS, DOH (also DoT comments)"

Closes #1144

See merge request !4644
parents 677cc946 91718fe4
Pipeline #62874 passed with stages
in 1 minute and 15 seconds
......@@ -70,11 +70,6 @@ stages:
- linux
- amd64
.linux-i386: &linux_i386
tags:
- linux
- i386
.linux-stress-amd64: &linux_stress_amd64
tags:
- amd64
......@@ -133,10 +128,6 @@ stages:
image: "$CI_REGISTRY_IMAGE:debian-sid-amd64"
<<: *linux_amd64
.debian-sid-i386: &debian_sid_i386_image
image: "$CI_REGISTRY_IMAGE:debian-sid-i386"
<<: *linux_i386
# openSUSE Tumbleweed
.tumbleweed-latest-amd64: &tumbleweed_latest_amd64_image
......@@ -285,6 +276,7 @@ stages:
"with-openssl=C:/OpenSSL"
"with-libxml2=C:/libxml2"
"with-libuv=C:/libuv"
"with-nghttp2=C:/nghttp2"
"without-python"
"with-system-tests"
x64'
......@@ -836,30 +828,6 @@ unit:gcc:tarball:
- schedules
- tags
# Jobs for regular GCC builds on Debian "sid" (i386)
gcc:sid:i386:
variables:
CC: gcc
CFLAGS: "${CFLAGS_COMMON}"
EXTRA_CONFIGURE: "--with-libidn2"
<<: *debian_sid_i386_image
<<: *build_job
system:gcc:sid:i386:
<<: *debian_sid_i386_image
<<: *system_test_job
needs:
- job: gcc:sid:i386
artifacts: true
unit:gcc:sid:i386:
<<: *debian_sid_i386_image
<<: *unit_test_job
needs:
- job: gcc:sid:i386
artifacts: true
# Jobs for debug GCC builds on openSUSE Tumbleweed (amd64)
gcc:tumbleweed:amd64:
......
5576. [experimental] Initial server-side implementation of DNS-over-HTTPS
(DoH). Support for both TLS-encrypted and unencrypted
HTTP/2 connections has been added to the network manager
and integrated into named. (Note: there is currently no
client-side support for DNS-over-HTTPS; this will be
added to dig in a future release.) [GL #1144]
5575. [bug] When migrating to dnssec-policy, BIND considered keys
with the "Inactive" and/or "Delete" timing metadata as
possible active keys. This has been fixed. [GL #2406]
......
......@@ -367,3 +367,25 @@ 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.
-----------------------------------------------------------------------------
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
......@@ -94,6 +94,8 @@ options {\n\
# pid-file \"" NAMED_LOCALSTATEDIR "/run/named/named.pid\"; \n\
port 53;\n\
tls-port 853;\n\
http-port 80;\n\
https-port 443;\n\
prefetch 2 9;\n\
recursing-file \"named.recursing\";\n\
recursive-clients 1000;\n\
......
......@@ -73,6 +73,8 @@ EXTERN const char *named_g_configargs INIT(PACKAGE_CONFIGARGS);
EXTERN const char *named_g_builder INIT(PACKAGE_BUILDER);
EXTERN in_port_t named_g_port INIT(0);
EXTERN in_port_t named_g_tlsport INIT(0);
EXTERN in_port_t named_g_httpsport INIT(0);
EXTERN in_port_t named_g_httpport INIT(0);
EXTERN isc_dscp_t named_g_dscp INIT(-1);
EXTERN named_server_t *named_g_server INIT(NULL);
......
......@@ -705,7 +705,7 @@ parse_T_opt(char *option) {
static void
parse_port(char *arg) {
enum { DNSPORT, TLSPORT } ptype = DNSPORT;
enum { DNSPORT, TLSPORT, HTTPSPORT, HTTPPORT } ptype = DNSPORT;
char *value = arg;
int port;
......@@ -714,6 +714,12 @@ parse_port(char *arg) {
} else if (strncmp(arg, "tls=", 4) == 0) {
value = arg + 4;
ptype = TLSPORT;
} else if (strncmp(arg, "https=", 6) == 0) {
value = arg + 6;
ptype = HTTPSPORT;
} else if (strncmp(arg, "http=", 5) == 0) {
value = arg + 6;
ptype = HTTPPORT;
}
port = parse_int(value, "port");
......@@ -728,6 +734,12 @@ parse_port(char *arg) {
case TLSPORT:
named_g_tlsport = port;
break;
case HTTPSPORT:
named_g_httpsport = port;
break;
case HTTPPORT:
named_g_httpport = port;
break;
default:
INSIST(0);
ISC_UNREACHABLE();
......
......@@ -86,6 +86,15 @@ DYNDB
dyndb string quoted_string {
unspecified-text };
HTTP
^^^^
::
http string {
endpoints { quoted_string; ... };
};
KEY
^^^
......@@ -264,6 +273,8 @@ OPTIONS
glue-cache boolean;// deprecated
heartbeat-interval integer;
hostname ( quoted_string | none );
http-port integer;
https-port integer;
inline-signing boolean;
interface-interval duration;
ipv4only-contact string;
......@@ -275,10 +286,12 @@ OPTIONS
key-directory quoted_string;
lame-ttl duration;
listen-on [ port integer ] [ dscp
integer ] [ tls string ] {
integer ] [ tls string ] [ http
string ] {
address_match_element; ... };
listen-on-v6 [ port integer ] [ dscp
integer ] [ tls string ] {
integer ] [ tls string ] [ http
string ] {
address_match_element; ... };
lmdb-mapsize sizeval;
lock-file ( quoted_string | none );
......
......@@ -115,7 +115,11 @@ Options
``portnum``; if not not specified, the default is port 53. If
``value`` is of the form ``tls=<portnum>``, the server will
listen for TLS queries on ``portnum``; the default is 853.
If ``value`` is of the form ``https=<portnum>``, the server will
listen for HTTPS queries on ``portnum``; the default is 443.
If ``value`` is of the form ``http=<portnum>``, the server will
listen for HTTP queries on ``portnum``; the default is 80.
``-s``
This option writes memory usage statistics to ``stdout`` on exit.
......
......@@ -398,13 +398,19 @@ static void
named_server_reload(isc_task_t *task, isc_event_t *event);
static isc_result_t
ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
uint16_t family, ns_listenelt_t **target);
listenelt_http(const cfg_obj_t *http, bool tls, const char *key,
const char *cert, in_port_t port, isc_mem_t *mctx,
ns_listenelt_t **target);
static isc_result_t
listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
ns_listenelt_t **target);
static isc_result_t
ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
uint16_t family, ns_listenlist_t **target);
listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
ns_listenlist_t **target);
static isc_result_t
configure_forward(const cfg_obj_t *config, dns_view_t *view,
......@@ -8573,6 +8579,16 @@ load_configuration(const char *filename, named_server_t *server,
maps[i++] = named_g_defaults;
maps[i] = NULL;
obj = NULL;
result = named_config_get(maps, "http-port", &obj);
INSIST(result == ISC_R_SUCCESS);
named_g_httpport = (in_port_t)cfg_obj_asuint32(obj);
obj = NULL;
result = named_config_get(maps, "https-port", &obj);
INSIST(result == ISC_R_SUCCESS);
named_g_httpsport = (in_port_t)cfg_obj_asuint32(obj);
/*
* If bind.keys exists, load it. If "dnssec-validation auto"
* is turned on, the root key found there will be used as a
......@@ -8989,7 +9005,7 @@ load_configuration(const char *filename, named_server_t *server,
}
if (clistenon != NULL) {
/* check return code? */
(void)ns_listenlist_fromconfig(
(void)listenlist_fromconfig(
clistenon, config, named_g_aclconfctx,
named_g_mctx, AF_INET, &listenon);
} else {
......@@ -9017,7 +9033,7 @@ load_configuration(const char *filename, named_server_t *server,
}
if (clistenon != NULL) {
/* check return code? */
(void)ns_listenlist_fromconfig(
(void)listenlist_fromconfig(
clistenon, config, named_g_aclconfctx,
named_g_mctx, AF_INET6, &listenon);
} else {
......@@ -10985,9 +11001,9 @@ named_server_togglequerylog(named_server_t *server, isc_lex_t *lex) {
}
static isc_result_t
ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
uint16_t family, ns_listenlist_t **target) {
listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
ns_listenlist_t **target) {
isc_result_t result;
const cfg_listelt_t *element;
ns_listenlist_t *dlist = NULL;
......@@ -11004,8 +11020,8 @@ ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
{
ns_listenelt_t *delt = NULL;
const cfg_obj_t *listener = cfg_listelt_value(element);
result = ns_listenelt_fromconfig(listener, config, actx, mctx,
family, &delt);
result = listenelt_fromconfig(listener, config, actx, mctx,
family, &delt);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
......@@ -11019,66 +11035,114 @@ cleanup:
return (result);
}
static const cfg_obj_t *
find_maplist(const cfg_obj_t *config, const char *listname, const char *name) {
isc_result_t result;
const cfg_obj_t *maplist = NULL;
const cfg_listelt_t *elt = NULL;
REQUIRE(config != NULL);
REQUIRE(name != NULL);
result = cfg_map_get(config, listname, &maplist);
if (result != ISC_R_SUCCESS) {
return (NULL);
}
for (elt = cfg_list_first(maplist); elt != NULL;
elt = cfg_list_next(elt)) {
const cfg_obj_t *map = cfg_listelt_value(elt);
if (strcasecmp(cfg_obj_asstring(cfg_map_getname(map)), name) ==
0) {
return (map);
}
}
return (NULL);
}
/*
* Create a listen list from the corresponding configuration
* data structure.
*/
static isc_result_t
ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
uint16_t family, ns_listenelt_t **target) {
listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
ns_listenelt_t **target) {
isc_result_t result;
const cfg_obj_t *tlsobj, *portobj, *dscpobj;
in_port_t port;
const cfg_obj_t *tlsobj = NULL, *httpobj = NULL;
const cfg_obj_t *portobj = NULL, *dscpobj = NULL;
const cfg_obj_t *http_server = NULL;
in_port_t port = 0;
isc_dscp_t dscp = -1;
const char *key = NULL, *cert = NULL;
bool tls = false;
bool do_tls = false, http = false;
ns_listenelt_t *delt = NULL;
REQUIRE(target != NULL && *target == NULL);
/* XXXWPK TODO be more verbose on failures. */
tlsobj = cfg_tuple_get(listener, "tls");
if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) {
if (!strcmp(cfg_obj_asstring(tlsobj), "ephemeral")) {
tls = true;
} else {
const cfg_obj_t *tlsconfigs = NULL;
const cfg_listelt_t *element;
(void)cfg_map_get(config, "tls", &tlsconfigs);
for (element = cfg_list_first(tlsconfigs);
element != NULL; element = cfg_list_next(element))
{
cfg_obj_t *tconfig = cfg_listelt_value(element);
const cfg_obj_t *name =
cfg_map_getname(tconfig);
if (!strcmp(cfg_obj_asstring(name),
cfg_obj_asstring(tlsobj))) {
tls = true;
const cfg_obj_t *keyo = NULL,
*certo = NULL;
(void)cfg_map_get(tconfig, "key-file",
&keyo);
if (keyo == NULL) {
return (ISC_R_FAILURE);
}
(void)cfg_map_get(tconfig, "cert-file",
&certo);
if (certo == NULL) {
return (ISC_R_FAILURE);
}
key = cfg_obj_asstring(keyo);
cert = cfg_obj_asstring(certo);
break;
}
const char *tlsname = cfg_obj_asstring(tlsobj);
if (strcmp(tlsname, "ephemeral") != 0) {
const cfg_obj_t *keyobj = NULL, *certobj = NULL;
const cfg_obj_t *tlsmap = NULL;
tlsmap = find_maplist(config, "tls", tlsname);
if (tlsmap == NULL) {
return (ISC_R_FAILURE);
}
CHECK(cfg_map_get(tlsmap, "key-file", &keyobj));
key = cfg_obj_asstring(keyobj);
CHECK(cfg_map_get(tlsmap, "cert-file", &certobj));
cert = cfg_obj_asstring(certobj);
}
if (!tls) {
do_tls = true;
}
httpobj = cfg_tuple_get(listener, "http");
if (httpobj != NULL && cfg_obj_isstring(httpobj)) {
const char *httpname = cfg_obj_asstring(httpobj);
http_server = find_maplist(config, "http", httpname);
if (http_server == NULL) {
cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR,
"http '%s' is not defined",
cfg_obj_asstring(httpobj));
return (ISC_R_FAILURE);
}
http = true;
}
portobj = cfg_tuple_get(listener, "port");
if (!cfg_obj_isuint32(portobj)) {
if (tls) {
if (http && do_tls) {
if (named_g_httpsport != 0) {
port = named_g_httpsport;
} else {
result = named_config_getport(
config, "https-port", &port);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
} else if (http && !do_tls) {
if (named_g_httpport != 0) {
port = named_g_port;
} else {
result = named_config_getport(
config, "http-port", &port);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
} else if (do_tls) {
if (named_g_tlsport != 0) {
port = named_g_tlsport;
} else {
......@@ -11103,6 +11167,7 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
if (cfg_obj_asuint32(portobj) >= UINT16_MAX) {
cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR,
"port value '%u' is out of range",
cfg_obj_asuint32(portobj));
return (ISC_R_RANGE);
}
......@@ -11122,10 +11187,13 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj);
}
result = ns_listenelt_create(mctx, port, dscp, NULL, tls, key, cert,
&delt);
if (result != ISC_R_SUCCESS) {
return (result);
if (http) {
INSIST(http_server != NULL);
CHECK(listenelt_http(http_server, do_tls, key, cert, port, mctx,
&delt));
} else {
CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls, key,
cert, &delt));
}
result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config,
......@@ -11136,7 +11204,55 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
return (result);
}
*target = delt;
return (ISC_R_SUCCESS);
cleanup:
return (result);
}
static isc_result_t
listenelt_http(const cfg_obj_t *http, bool tls, const char *key,
const char *cert, in_port_t port, isc_mem_t *mctx,
ns_listenelt_t **target) {
isc_result_t result = ISC_R_SUCCESS;
ns_listenelt_t *delt = NULL;
char **endpoints = NULL;
const cfg_obj_t *eplist = NULL;
const cfg_listelt_t *elt = NULL;
size_t len, i = 0;
REQUIRE(target != NULL && *target == NULL);
REQUIRE((key == NULL) == (cert == NULL));
if (port == 0) {
port = tls ? named_g_httpsport : named_g_httpport;
}
CHECK(cfg_map_get(http, "endpoints", &eplist));
len = cfg_list_length(eplist, false);
endpoints = isc_mem_allocate(mctx, sizeof(endpoints[0]) * len);
for (elt = cfg_list_first(eplist); elt != NULL;
elt = cfg_list_next(elt)) {
const cfg_obj_t *ep = cfg_listelt_value(elt);
const char *path = cfg_obj_asstring(ep);
endpoints[i++] = isc_mem_strdup(mctx, path);
}
INSIST(i == len);
result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, tls,
key, cert, endpoints, len, &delt);
if (result != ISC_R_SUCCESS) {
if (delt != NULL) {
ns_listenelt_destroy(delt);
}
return (result);
}
*target = delt;
cleanup:
return (result);
}
isc_result_t
......
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
tls local-tls {
key-file "key.pem";
cert-file "cert.pem";
};
http local-http-server {
endpoints { "/dns-query"; };
};
options {
listen-on { 10.53.0.1; };
http-port 80;
https-port 443;
listen-on port 443 tls local-tls http local-http-server { 10.53.0.1; };
listen-on port 8080 http local-http-server { 10.53.0.1; };
};
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
tls local-tls {
key-file "key.pem";
cert-file "cert.pem";
};
options {
listen-on port 853 tls local-tls { 10.53.0.1; };
};
......@@ -668,6 +668,8 @@ copy_setports() {
atsign="@"
sed -e "s/${atsign}PORT${atsign}/${PORT}/g" \
-e "s/${atsign}TLSPORT${atsign}/${TLSPORT}/g" \
-e "s/${atsign}HTTPPORT${atsign}/${HTTPSPORT}/g" \
-e "s/${atsign}HTTPSPORT${atsign}/${HTTPSPORT}/g" \
-e "s/${atsign}EXTRAPORT1${atsign}/${EXTRAPORT1}/g" \
-e "s/${atsign}EXTRAPORT2${atsign}/${EXTRAPORT2}/g" \
-e "s/${atsign}EXTRAPORT3${atsign}/${EXTRAPORT3}/g" \
......
......@@ -82,6 +82,8 @@ done
echo "export PORT=$(get_port "$baseport")"
echo "export TLSPORT=$(get_port)"
echo "export HTTPPORT=$(get_port)"
echo "export HTTPSPORT=$(get_port)"
echo "export EXTRAPORT1=$(get_port)"
echo "export EXTRAPORT2=$(get_port)"
echo "export EXTRAPORT3=$(get_port)"
......
......@@ -149,7 +149,7 @@ stop_servers() {
echostart "S:$systest:$(date_with_args)"
echoinfo "T:$systest:1:A"
echoinfo "A:$systest:System test $systest"
echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${EXTRAPORT1},${EXTRAPORT2},${EXTRAPORT3},${EXTRAPORT4},${EXTRAPORT5},${EXTRAPORT6},${EXTRAPORT7},${EXTRAPORT8},${CONTROLPORT}"
echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${HTTPPORT},${HTTPSPORT},${EXTRAPORT1},${EXTRAPORT2},${EXTRAPORT3},${EXTRAPORT4},${EXTRAPORT5},${EXTRAPORT6},${EXTRAPORT7},${EXTRAPORT8},${CONTROLPORT}"
$PERL ${srcdir}/testsock.pl -p "$PORT" || {
echowarn "I:$systest:Network interface aliases not set up. Skipping test."
......
......@@ -138,7 +138,8 @@ const FileData installFiles[] =
{"libdns.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"libirs.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"libeay32.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"libuv.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"nghttp2.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"uv.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
#ifdef HAVE_LIBXML2
{"libxml2.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
#endif
......
</
......@@ -124,7 +124,7 @@ AS_IF([test "$enable_static" != "no" && test "$enable_developer" != "yes"],
#
# Set the default CFLAGS and CPPFLAGS
#
STD_CFLAGS="-Wall -Wextra -Wwrite-strings -Wcast-qual -Wpointer-arith -Wno-missing-field-initializers -Wformat -Wshadow"
STD_CFLAGS="-Wall -Wextra -Wwrite-strings -Wpointer-arith -Wno-missing-field-initializers -Wformat -Wshadow"