Commit 6c2db2b5 authored by Vitezslav Kriz's avatar Vitezslav Kriz Committed by Petr Špaček

keyfile argument distiguish managed and unmanaged mode

Arguments --keyfile, -k for managed mode
and
--keyfile-ro, -K for unmanaged (readonly) mode.

Automatic setting based on the file permission is removed because it was
confusing and could easily lead to state where automatic update does not
happen because of unexpected file permissions.

Check if folder is writeable was moved into Lua code.

Default unmanaged keyfile path can be specified at compile
time with option KEYFILE_DEFAULT. This default
configuration can be disabled in configuration file with
trust_anchors.keyfile_default = nil.
parent 1b36d2d4
......@@ -23,6 +23,7 @@ ROOTHINTS ?= $(ETCDIR)/root.hints
COVERAGE_STAGE ?= gcov
COVERAGE_STATSDIR ?= $(CURDIR)/coverage.stats
TOPSRCDIR := $(CURDIR)
KEYFILE_DEFAULT ?=
# Tools
CC ?= cc
......
......@@ -58,6 +58,8 @@ The root anchors bootstrap may fail for various reasons, in this case you need t
You've just enabled DNSSEC!
.. note:: Bootstrapping and automatic update need write access to keyfile direcory. If you want to manage root anchors manually you should use ``trust_anchors.add_file('root.keys', true)``.
CLI interface
=============
......
......@@ -56,6 +56,7 @@ daemon-install: kresd-install bindings-install
ifneq ($(SED),)
$(SED) -e "s/@VERSION@/$(VERSION)/" -e "s/@DATE@/$(date)/" \
-e "s|@MODULEDIR@|$(MODULEDIR)|" \
-e "s|@KEYFILE_DEFAULT@|$(KEYFILE_DEFAULT)|" \
doc/kresd.8.in > doc/kresd.8
$(INSTALL) -d -m 0755 $(DESTDIR)$(MANDIR)/man8/
$(INSTALL) -m 0644 doc/kresd.8 $(DESTDIR)$(MANDIR)/man8/
......@@ -65,7 +66,7 @@ daemon-clean: kresd-clean
daemon/lua/zonefile.lua
daemon/lua/trust_anchors.lua: daemon/lua/trust_anchors.lua.in
@$(call quiet,SED,$<) -e "s|@ETCDIR@|$(ETCDIR)|g" $< > $@
@$(call quiet,SED,$<) -e "s|@ETCDIR@|$(ETCDIR)|g;s|@KEYFILE_DEFAULT@|$(KEYFILE_DEFAULT)|g" $< > $@
LIBZSCANNER_COMMENTS := \
$(shell pkg-config libzscanner --atleast-version=2.4.2 && echo true || echo false)
......
......@@ -812,9 +812,8 @@ int engine_ipc(struct engine *engine, const char *expr)
}
}
static int engine_loadconf(struct engine *engine, const char *config_path)
int engine_load_sandbox(struct engine *engine)
{
int ret = 0;
/* Init environment */
static const char sandbox_bytecode[] = {
#include "daemon/lua/sandbox.inc"
......@@ -824,22 +823,13 @@ static int engine_loadconf(struct engine *engine, const char *config_path)
lua_pop(engine->L, 1);
return kr_error(ENOEXEC);
}
/* Load config file */
if (config_path) {
if (strcmp(config_path, "-") == 0) {
return ret; /* No config and no defaults. */
}
ret = l_dosandboxfile(engine->L, config_path);
}
if (ret == 0) {
/* Load defaults */
static const char config_bytecode[] = {
#include "daemon/lua/config.inc"
};
ret = l_dobytecode(engine->L, config_bytecode, sizeof(config_bytecode), "config");
}
return kr_ok();
}
/* Evaluate */
int engine_loadconf(struct engine *engine, const char *config_path)
{
assert(config_path != NULL);
int ret = l_dosandboxfile(engine->L, config_path);
if (ret != 0) {
fprintf(stderr, "%s\n", lua_tostring(engine->L, -1));
lua_pop(engine->L, 1);
......@@ -847,14 +837,22 @@ static int engine_loadconf(struct engine *engine, const char *config_path)
return ret;
}
int engine_start(struct engine *engine, const char *config_path)
int engine_load_defaults(struct engine *engine)
{
/* Load configuration. */
int ret = engine_loadconf(engine, config_path);
/* Load defaults */
static const char config_bytecode[] = {
#include "daemon/lua/config.inc"
};
int ret = l_dobytecode(engine->L, config_bytecode, sizeof(config_bytecode), "config");
if (ret != 0) {
return ret;
fprintf(stderr, "%s\n", lua_tostring(engine->L, -1));
lua_pop(engine->L, 1);
}
return ret;
}
int engine_start(struct engine *engine)
{
/* Clean up stack and restart GC */
lua_settop(engine->L, 0);
lua_gc(engine->L, LUA_GCCOLLECT, 0);
......
......@@ -83,12 +83,13 @@ int engine_pcall(struct lua_State *L, int argc);
int engine_ipc(struct engine *engine, const char *expr);
/** Start the lua engine and execute the config.
*
* @note Special path "-" means that even default config won't be done
* (like listening on localhost).
*/
int engine_start(struct engine *engine, const char *config_path);
int engine_load_sandbox(struct engine *engine);
int engine_loadconf(struct engine *engine, const char *config_path);
int engine_load_defaults(struct engine *engine);
/** Start the lua engine and execute the config. */
int engine_start(struct engine *engine);
void engine_stop(struct engine *engine);
int engine_register(struct engine *engine, const char *name, const char *precedence, const char* ref);
int engine_unregister(struct engine *engine, const char *name);
......
......@@ -18,3 +18,13 @@ end
if kres.context().root_hints.nsset.root == nil then
_hint_root_file()
end
if not trust_anchors.keysets['\0'] and trust_anchors.keyfile_default then
if io.open(trust_anchors.keyfile_default, 'r') then
trust_anchors.config(trust_anchors.keyfile_default, true)
else
panic("cannot open default trust anchor file:'%s'",
trust_anchors.keyfile_default
)
end
end
......@@ -367,9 +367,16 @@ update = function (keyset, new_keys, is_initial)
end
local add_file = function (path, unmanaged)
-- Bootstrap if requested and keyfile doesn't exist
if not unmanaged then
if not io.open(path .. '.lock', 'w') then
error("[ ta ] ERROR: write access needed to keyfile dir '"..path.."'")
end
os.remove(path .. ".lock")
end
-- Bootstrap if requested and keyfile doesn't exist
if not unmanaged and not io.open(path, 'r') then
log("[ ta ] keyfile '%s': doesn't exist, bootstrapping", path);
local tas, msg = bootstrap(trust_anchors.bootstrap_url, trust_anchors.bootstrap_ca)
if not tas then
msg = msg .. '\n'
......@@ -394,14 +401,15 @@ local add_file = function (path, unmanaged)
end
-- Parse the file and check its sanity
local keyset, err = keyset_read(path)
if not unmanaged then keyset.filename = path end
local keyset, err = keyset_read(path)
if not keyset then
panic("[ ta ] ERROR: failed to read anchors from '%s' (%s)", path, err)
end
if not unmanaged then keyset.filename = path end
if not keyset[1] then
panic("[ ta ] ERROR: failed to read anchors from '%s'", path)
end
if not unmanaged then keyset.filename = path end
local owner = keyset[1].owner
for _, ta in ipairs(keyset) do
if ta.owner ~= owner then
......@@ -446,6 +454,8 @@ trust_anchors = {
bootstrap_url = 'https://data.iana.org/root-anchors/root-anchors.xml',
bootstrap_ca = '@ETCDIR@/icann-ca.pem',
-- change empty string to nil
keyfile_default = ('@KEYFILE_DEFAULT@' ~= '' and '@KEYFILE_DEFAULT@') or nil,
-- Load keys from a file, 5011-managed by default.
-- If managed and the file doesn't exist, try bootstrapping the root into it.
......
......@@ -39,6 +39,7 @@
#include "daemon/engine.h"
#include "daemon/bindings.h"
#include "daemon/tls.h"
#include "lib/dnssec/ta.h"
/* We can fork early on Linux 3.9+ and do SO_REUSEPORT for better performance. */
#if defined(UV_VERSION_HEX) && defined(SO_REUSEPORT) && defined(__linux__)
......@@ -347,7 +348,8 @@ static void help(int argc, char *argv[])
" -S, --fd=[fd] Listen on given fd (handed out by supervisor).\n"
" -T, --tlsfd=[fd] Listen using TLS on given fd (handed out by supervisor).\n"
" -c, --config=[path] Config file path (relative to [rundir]) (default: config).\n"
" -k, --keyfile=[path] File containing trust anchors (DS or DNSKEY).\n"
" -k, --keyfile=[path] File with root domain trust anchors (DS or DNSKEY), automatically updated.\n"
" -K, --keyfile-ro=[path] File with read-only root domain trust anchors, for use with an external updater.\n"
" -m, --moduledir=[path] Override the default module path (" MODULEDIR ").\n"
" -f, --forks=N Start N forks sharing the configuration.\n"
" -q, --quiet Quiet output, no prompt in interactive mode.\n"
......@@ -422,79 +424,21 @@ static void free_sd_socket_names(char **socket_names, int count)
}
#endif
static int init_keyfile(struct engine *engine, const char *keyfile)
static int init_keyfile(struct engine *engine, const char *keyfile, bool keyfile_unmanaged)
{
auto_free char *dirname_storage = strdup(keyfile);
if (!dirname_storage) {
return kr_error(ENOMEM);
}
/* Resolve absolute path to the keyfile directory */
auto_free char *keyfile_dir = malloc(PATH_MAX);
if (!keyfile_dir) {
return kr_error(ENOMEM);
}
if (realpath(dirname(dirname_storage), keyfile_dir) == NULL) {
kr_log_error("[ ta ]: keyfile '%s' directory: %s\n", keyfile, strerror(errno));
return kr_error(ENOTDIR);
}
auto_free char *basename_storage = strdup(keyfile);
if (!basename_storage) {
return kr_error(ENOMEM);
}
char *_filename = basename(basename_storage);
int dirlen = strlen(keyfile_dir);
int namelen = strlen(_filename);
if (dirlen + 1 + namelen >= PATH_MAX) {
kr_log_error("[ ta ]: keyfile '%s' PATH_MAX exceeded\n",
keyfile);
return kr_error(ENAMETOOLONG);
}
keyfile_dir[dirlen++] = '/';
keyfile_dir[dirlen] = '\0';
auto_free char *keyfile_path = malloc(dirlen + namelen + 1);
if (!keyfile_path) {
return kr_error(ENOMEM);
}
memcpy(keyfile_path, keyfile_dir, dirlen);
memcpy(keyfile_path + dirlen, _filename, namelen + 1);
int unmanaged = 0;
/* Note: config has been executed, so access() is OK,
* as we've dropped privileges already if configured. */
if (access(keyfile_path, F_OK) != 0) {
kr_log_info("[ ta ] keyfile '%s': doesn't exist, bootstrapping\n", keyfile_path);
if (access(keyfile_dir, W_OK) != 0) {
kr_log_error("[ ta ] keyfile '%s': write access to '%s' needed\n", keyfile_path, keyfile_dir);
return kr_error(EPERM);
}
} else if (access(keyfile_path, R_OK) == 0) {
if ((access(keyfile_path, W_OK) != 0) || (access(keyfile_dir, W_OK) != 0)) {
kr_log_error("[ ta ] keyfile '%s': not writeable, starting in unmanaged mode\n", keyfile_path);
unmanaged = 1;
}
} else {
kr_log_error("[ ta ] keyfile '%s': %s\n", keyfile_path, strerror(errno));
return kr_error(EPERM);
}
auto_free char *cmd = afmt("trust_anchors.config('%s',%s)", keyfile_path, unmanaged?"true":"nil");
auto_free char *cmd = afmt("trust_anchors.config('%s',%s)",
keyfile, keyfile_unmanaged ? "true" : "nil");
if (!cmd) {
kr_log_error("[system] not enough memory\n");
return kr_error(ENOMEM);
}
int lua_ret = engine_cmd(engine->L, cmd, false);
if (lua_ret != 0) {
if (lua_gettop(engine->L) > 0) {
kr_log_error("%s", lua_tostring(engine->L, -1));
lua_settop(engine->L, 0);
kr_log_error("%s\n", lua_tostring(engine->L, -1));
} else {
kr_log_error("[ ta ] keyfile '%s': failed to load (%s)\n",
keyfile_path, lua_strerror(lua_ret));
keyfile, lua_strerror(lua_ret));
}
return kr_error(EIO);
}
......@@ -515,6 +459,7 @@ int main(int argc, char **argv)
array_t(int) tls_fd_set;
array_init(tls_fd_set);
char *keyfile = NULL;
int keyfile_unmanaged = 0;
char *moduledir = MODULEDIR;
const char *config = NULL;
int control_fd = -1;
......@@ -528,6 +473,7 @@ int main(int argc, char **argv)
{"tlsfd", required_argument, 0, 'T'},
{"config", required_argument, 0, 'c'},
{"keyfile",required_argument, 0, 'k'},
{"keyfile-ro",required_argument, 0, 'K'},
{"forks",required_argument, 0, 'f'},
{"moduledir", required_argument, 0, 'm'},
{"verbose", no_argument, 0, 'v'},
......@@ -536,7 +482,7 @@ int main(int argc, char **argv)
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
while ((c = getopt_long(argc, argv, "a:t:S:T:c:f:m:k:vqVh", opts, &li)) != -1) {
while ((c = getopt_long(argc, argv, "a:t:S:T:c:f:m:K:k:vqVh", opts, &li)) != -1) {
switch (c)
{
case 'a':
......@@ -563,7 +509,13 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
break;
case 'K':
keyfile_unmanaged = 1;
case 'k':
if (keyfile != NULL) {
kr_log_error("[system] error only one of '--keyfile' and '--keyfile-ro' allowed\n");
return EXIT_FAILURE;
}
keyfile = optarg;
break;
case 'm':
......@@ -748,14 +700,18 @@ int main(int argc, char **argv)
worker->loop = loop;
loop->data = worker;
ret = engine_start(&engine, config);
ret = engine_load_sandbox(&engine);
if (ret == 0 && config != NULL && strcmp(config, "-") !=0) {
ret = engine_loadconf(&engine, config);
}
if (ret != 0) {
ret = EXIT_FAILURE;
goto cleanup;
}
if (keyfile) {
ret = init_keyfile(&engine, keyfile);
ret = init_keyfile(&engine, keyfile, keyfile_unmanaged);
if (ret != 0) {
kr_log_error("[system] failed to initialized keyfile: %s\n", kr_strerror(ret));
ret = EXIT_FAILURE;
......@@ -763,6 +719,21 @@ int main(int argc, char **argv)
}
}
if (config == NULL || strcmp(config, "-") !=0) {
ret = engine_load_defaults(&engine);
}
if (ret != 0) {
ret = EXIT_FAILURE;
goto cleanup;
}
ret = engine_start(&engine);
if (ret != 0) {
ret = EXIT_FAILURE;
goto cleanup;
}
/* Run the event loop */
ret = run_worker(loop, &engine, &ipc_set, fork_id == 0, control_fd);
if (ret != 0) {
......
......@@ -197,10 +197,12 @@ All paths are prefixed with ``PREFIX`` variable by default if not specified othe
"daemon", "``SBINDIR``", "``$(PREFIX)/sbin``", ""
"configuration", "``ETCDIR``", "``$(PREFIX)/etc/kresd``", "Configuration file, templates."
"modules", "``MODULEDIR``", "``$(LIBDIR)/kdns_modules``", "Runtime directory for loading dynamic modules [#]_."
"trust anchor file", "``KEYFILE_DEFAULT``", "*(none)*", "Path to read-only trust anchor file, which is used as fallback when no other file is specified. [#]_"
"work directory", "", "the current directory", "Run directory for daemon. (Only relevant during run time, not e.g. during installation.)"
.. [#] The ``libkres.pc`` is installed in ``$(LIBDIR)/pkgconfig``.
.. [#] The default moduledir can be changed with `-m` option to `kresd` daemon or by calling `moduledir()` function from lua.
.. [#] If no other trust anchor is specified by user, the compiled-in path ``KEYFILE_DEFAULT`` must contain a valid trust anchor. This is typically used by distributions which provide DNSSEC root trust anchors as part of distribution package. Users can disable the built-in trust anchor by adding ``trust_anchors.keyfile_default = nil`` to their configuration.
.. note:: Each module is self-contained and may install additional bundled files within ``$(MODULEDIR)/$(modulename)``. These files should be read-only, non-executable.
......
......@@ -24,6 +24,8 @@
.IR config ]
.RB [ \-k | \-\-keyfile
.IR keyfile ]
.RB [ \-K | \-\-keyfile-ro
.IR keyfile ]
.RB [ \-m | \-\-moduledir
.IR path ]
.RB [ \-f | \-\-forks
......@@ -121,10 +123,20 @@ file at the default location (\fIconfig\fR). The syntax is
described in \fIdaemon/README.md\fR.
.TP
.B \-k\fI keyfile\fR, \fB\-\-keyfile=\fI<keyfile>
Use given for keeping root trust anchors. If the file doesn't exist, it will be
automatically boostrapped from IANA and warning for you will be issued to check it
before trusting it. The file contains DNSKEY/DS records in presentation format,
and is compatible with Unbound or BIND9 root key files.
(Recommended!) Automatically managed root trust anchors file.
Root trust anchors in this file are managed using standard RFC 5011 (Automated Updates of DNS Security Trust Anchors).
Kresd needs write access to the directory containing the keyfile.
If the file does not exist, it will be automatically boostrapped from IANA using HTTPS protocol
and warning that you need to to check the key before trusting it will be issued.
The file contains DNSKEY/DS records in presentation format,
and is compatible with Unbound and BIND 9 root key files.
.TP
.B \-K\fI keyfile\fR, \fB\-\-keyfile-ro=\fI<keyfile>
(Discouraged) Static root trust anchors file. The file is not updated by kresd. Use of this option is discouraged because it will break your installation when the trust anchor key changes!
Default: "@KEYFILE_DEFAULT@" (can be empty if your distribution did not provide one)
.TP
.B \-m\fI path\fR, \fB\-\-moduledir=\fI<path>
Override the directory that is searched for modules. Default: @MODULEDIR@
......
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