From: David VaĊĦek Date: Mon, 22 May 2023 21:05:21 +0000 (+0200) Subject: backup: implement QUIC server key and certificate backup and restore X-Git-Tag: v3.4.dev~112^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=47d64c3fd412d9bb49b2ee99121d52ce896a0963;p=thirdparty%2Fknot-dns.git backup: implement QUIC server key and certificate backup and restore --- diff --git a/doc/operation.rst b/doc/operation.rst index 6cfbcfc689..379b18a324 100644 --- a/doc/operation.rst +++ b/doc/operation.rst @@ -1073,6 +1073,14 @@ if the backup was created for only a subset of zones. to 3.1, it's necessary to use the ``-f`` option. Since this option also turns off some verification checks, it shouldn't be used in other cases. +.. NOTE:: + For QUIC, only the auto-generated key is restored. The ``zone-restore`` + command doesn't restore a user-defined QUIC key and certificate so as to + avoid possible configuration management conflicts and they must be restored + from the backup (its subdirectory ``quic``) manually. In all cases, + restart of the Knot server after the restore is necessary for the restored + QUIC key/certificate to take effect. + Limitations ----------- diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c index b991707578..e62fc02f4e 100644 --- a/src/knot/ctl/commands.c +++ b/src/knot/ctl/commands.c @@ -630,9 +630,21 @@ static int zones_apply_backup(ctl_args_t *args, bool restore_mode) return KNOT_CTL_EZONE; } + zone_backup_ctx_t *ctx = latest_backup_ctx(args); + + /* QUIC - server key and cert backup. */ + ret = backup_quic(ctx); + if (ret != KNOT_EOK) { + log_ctl_error("control, QUIC %s error (%s)", + restore_mode ? "restore" : "backup", + knot_strerror(ret)); + send_error(args, knot_strerror(ret)); + ret = KNOT_EOK; + goto done; + } + /* Global catalog zones backup. */ if (args->data[KNOT_CTL_IDX_ZONE] == NULL) { - zone_backup_ctx_t *ctx = latest_backup_ctx(args); ctx->backup_global = true; ret = global_backup(ctx, &args->server->catalog, NULL); if (ret != KNOT_EOK) { diff --git a/src/knot/zone/backup.c b/src/knot/zone/backup.c index 169bc8a44b..b47b74ddd5 100644 --- a/src/knot/zone/backup.c +++ b/src/knot/zone/backup.c @@ -34,6 +34,7 @@ #include "knot/dnssec/kasp/kasp_zone.h" #include "knot/dnssec/kasp/keystore.h" #include "knot/journal/journal_metadata.h" +#include "knot/server/server.h" #include "knot/zone/backup_dir.h" #include "knot/zone/zonefile.h" #include "libdnssec/error.h" @@ -468,3 +469,104 @@ int global_backup(zone_backup_ctx_t *ctx, catalog_t *catalog, } return ret; } + +static int backup_quic_file(zone_backup_ctx_t *ctx, char *file, char *subdir, + const char *desc, bool required, bool *success) +{ + char *backup_quic_dir = NULL, *backup_orig = NULL, *backup; + int ret; + + backup_quic_dir = dir_file(ctx->backup_dir, subdir); + ABORT_IF_ENOMEM(backup_quic_dir); + backup_orig = backup = dir_file(backup_quic_dir, file); + ABORT_IF_ENOMEM(backup); + + BACKUP_SWAP(ctx, backup, file); + ret = backup_file(backup, file); + if (ret == KNOT_EOK) { + *success = true; + } else if (!required && ret == KNOT_ENOENT) { + ret = KNOT_EOK; + } else { + log_ctl_error("control, QUIC %s file %s failed (%s)", desc, + ctx->restore_mode ? "restore" : "backup", + knot_strerror(ret)); + } +done: + free(backup_orig); + free(backup_quic_dir); + return ret; +} + +#define DONE_ON_ERROR if (ret != KNOT_EOK) { \ + goto done; \ + } + +int backup_quic(zone_backup_ctx_t *ctx) +{ + if (!ctx->backup_quic) { + return KNOT_EOK; + } + + const char *str_auto = "auto-generated key"; + const char *str_key = "configured key"; + const char *str_cert = "certificate"; + + bool log_auto = false; + bool log_key = false; + bool log_cert = false; + int ret; + + conf_val_t liquic_val = conf_get(conf(), C_SRV, C_LISTEN_QUIC); + bool quic_on = (conf_val_count(&liquic_val) > 0); + + char *cert_file = conf_tls(conf(), C_CERT_FILE); + char *key_file = conf_tls(conf(), C_KEY_FILE); + bool user_keys = (key_file != NULL); + + char *kasp_dir = conf_db(conf(), C_KASP_DB); + char *auto_file = abs_path(DFLT_QUIC_KEY_FILE, kasp_dir); + ABORT_IF_ENOMEM(auto_file); + free(kasp_dir); + + // Backup/restore of auto-generated key is required if it's in active use, + // otherwise use it if the file is found (no fail if missing). + ret = backup_quic_file(ctx, auto_file, "keys", str_auto, + quic_on && !user_keys, &log_auto); + DONE_ON_ERROR; + + // If QUIC isn't configured, backup of configured key and cert is possible, + // but it isn't required (no fail if missing). + if (user_keys && !ctx->restore_mode) { + char *quic_subdir = "quic"; + ret = backup_quic_file(ctx, key_file, quic_subdir, str_key, + quic_on, &log_key); + DONE_ON_ERROR; + + ret = backup_quic_file(ctx, cert_file, quic_subdir, str_cert, + quic_on, &log_cert); + DONE_ON_ERROR; + } + + if (log_auto || log_key) { + log_ctl_info("control, QUIC %s%s%s%s%s %s '%s'", + log_auto ? str_auto : "", + (log_auto && log_key) ? ", " : "", + log_key ? str_key : "", + log_cert ? " and " : "", + log_cert ? str_cert : "", + ctx->restore_mode ? "restored from" : "backed up to", + ctx->backup_dir); + } + +done: + free(auto_file); + free(key_file); + free(cert_file); + + if (ret != KNOT_EOK) { + ctx->failed = true; + } + + return ret; +} diff --git a/src/knot/zone/backup.h b/src/knot/zone/backup.h index 35088b3e94..301bb11266 100644 --- a/src/knot/zone/backup.h +++ b/src/knot/zone/backup.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2023 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -73,3 +73,5 @@ void zone_backups_init(zone_backup_ctxs_t *ctxs); void zone_backups_deinit(zone_backup_ctxs_t *ctxs); void zone_backups_add(zone_backup_ctxs_t *ctxs, zone_backup_ctx_t *ctx); void zone_backups_rem(zone_backup_ctx_t *ctx); + +int backup_quic(zone_backup_ctx_t *ctx);