]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
backup: implement QUIC server key and certificate backup and restore
authorDavid Vašek <david.vasek@nic.cz>
Mon, 22 May 2023 21:05:21 +0000 (23:05 +0200)
committerDavid Vašek <david.vasek@nic.cz>
Tue, 13 Jun 2023 09:10:20 +0000 (11:10 +0200)
doc/operation.rst
src/knot/ctl/commands.c
src/knot/zone/backup.c
src/knot/zone/backup.h

index 6cfbcfc689d4be14289deb8fe8273d18f49c8f01..379b18a324ed294d5e1c75fcf9622c086dfe26c1 100644 (file)
@@ -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
 -----------
 
index b991707578f2d28679ce4873531b14bbc3fb1096..e62fc02f4e718275c2da3413e9676f12a52f27e2 100644 (file)
@@ -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) {
index 169bc8a44bea4d6913e32645c60dd47b4fe05191..b47b74ddd5abfd47459ef2904b49849660ce3074 100644 (file)
@@ -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;
+}
index 35088b3e94b7b0a6b2b157283751ccdd0eb4a314..301bb11266599487f45a03c63e17ad47e097bc07 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     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);