From 5d9c6182c9055e2961ce0f0048c2856d134879c5 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Thu, 16 Nov 2017 14:53:47 +0100 Subject: [PATCH] API: Make the /cryptokeys endpoint use CryptoKey objects Add bits and algorithm to the CryptoKey object --- docs/http-api/cryptokeyitem.rst | 2 ++ docs/http-api/endpoint-cryptokeys.rst | 16 ++++++-------- pdns/ws-auth.cc | 29 ++++++++++++++++--------- regression-tests.api/test_Zones.py | 2 ++ regression-tests.api/test_cryptokeys.py | 4 ++-- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/docs/http-api/cryptokeyitem.rst b/docs/http-api/cryptokeyitem.rst index 4090ab6e6..80951d10a 100644 --- a/docs/http-api/cryptokeyitem.rst +++ b/docs/http-api/cryptokeyitem.rst @@ -15,3 +15,5 @@ CryptoKey :param string dnskey: The DNSKEY record for this key :param [string] ds: An array of DS records for this key :param string privatekey: The private key in ISC format + :param string algorithm: The key's algorithm + :param int bit: The bitsize of this key diff --git a/docs/http-api/endpoint-cryptokeys.rst b/docs/http-api/endpoint-cryptokeys.rst index ce35115b2..021abb430 100644 --- a/docs/http-api/endpoint-cryptokeys.rst +++ b/docs/http-api/endpoint-cryptokeys.rst @@ -14,21 +14,18 @@ These endpoints allow for the manipulation of DNSSEC crypto material. .. http:post:: /api/v1/servers/:server_id/zones/:zone_id/cryptokeys - This method adds a new key to a zone. - The key can either be generated or imported by supplying the ``content`` parameter. + This method adds a new key to a zone, POST data should be a :json:object:`CryptoKey`. + But not all fields needs to be present: - if ``content``, ``bits`` and ``algo`` are null, a key will be generated based + The key can either be generated or imported by supplying the ``privatekey`` parameter. + + if ``privatekey``, ``bits`` and ``algorithm`` are null, a key will be generated based on the :ref:`setting-default-ksk-algorithm` and :ref:`setting-default-ksk-size` settings for a KSK and the :ref:`setting-default-zsk-algorithm` and :ref:`setting-default-zsk-size` options for a ZSK. :param server_id: The name of the server :param zone_id: The id value of the :json:object:`Zone` - :reqjson string content: The private key to use (The format used is compatible with BIND and NSD/LDNS) - :reqjson string keytype: Either "ksk" or "zsk" - :reqjson bool active: If not set the key will not be active by default - :reqjson int bits: Number of bits in the key (if ``content`` is not set) - :reqjson int,string algo: The DNSSEC algorithm (if ``content`` is not set), see :ref:`dnssec-supported-algos` :statuscode 201: Everything was fine, returns all public data as a :json:object:`CryptoKey`. :statuscode 422: Returned when something is wrong with the content of the request. Contains an error message @@ -44,7 +41,8 @@ These endpoints allow for the manipulation of DNSSEC crypto material. .. http:put:: /api/v1/servers/:server_id/zones/:zone_name/cryptokeys/:cryptokey_id - This method (de)activates a key from ``zone_name`` specified by ``cryptokey_id``. + This method changes a key from ``zone_name`` specified by ``cryptokey_id``. + At this time, only changing the ``active`` parameter is supported. :param string server_id: The name of the server :param string zone_id: The id value of the :json:object:`Zone` diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 460952f6c..b5dc9c4a6 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -892,7 +892,9 @@ static void apiZoneCryptokeysGET(DNSName zonename, int inquireKeyId, HttpRespons { "active", value.second.active }, { "keytype", keyType }, { "flags", (uint16_t)value.first.d_flags }, - { "dnskey", value.first.getDNSKEY().getZoneRepresentation() } + { "dnskey", value.first.getDNSKEY().getZoneRepresentation() }, + { "algorithm", DNSSECKeeper::algorithm2name(value.first.d_algorithm) }, + { "bits", value.first.getKey()->getBits() } }; if (value.second.keyType == DNSSECKeeper::KSK || value.second.keyType == DNSSECKeeper::CSK) { @@ -942,10 +944,10 @@ static void apiZoneCryptokeysDELETE(DNSName zonename, int inquireKeyId, HttpRequ * This method adds a key to a zone by generate it or content parameter. * Parameter: * { - * "content" : "key The format used is compatible with BIND and NSD/LDNS" + * "privatekey" : "key The format used is compatible with BIND and NSD/LDNS" * "keytype" : "ksk|zsk" * "active" : "true|false" - * "algo" : "key generation algorithm "name|number" as default" https://doc.powerdns.com/md/authoritative/dnssec/#supported-algorithms + * "algorithm" : "key generation algorithm name as default" https://doc.powerdns.com/md/authoritative/dnssec/#supported-algorithms * "bits" : number of bits * } * @@ -954,7 +956,7 @@ static void apiZoneCryptokeysDELETE(DNSName zonename, int inquireKeyId, HttpRequ * The server returns 422 Unprocessable Entity {"error" : "Invalid keytype 'keytype'"} * Case 2: 'bits' must be a positive integer value. * The server returns 422 Unprocessable Entity {"error" : "'bits' must be a positive integer value."} - * Case 3: The "algo" isn't supported + * Case 3: The "algorithm" isn't supported * The server returns 422 Unprocessable Entity {"error" : "Unknown algorithm: 'algo'"} * Case 4: Algorithm <= 10 and no bits were passed * The server returns 422 Unprocessable Entity {"error" : "Creating an algorithm algo key requires the size (in bits) to be passed"} @@ -976,7 +978,13 @@ static void apiZoneCryptokeysDELETE(DNSName zonename, int inquireKeyId, HttpRequ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpResponse *resp, DNSSECKeeper *dk) { auto document = req->json(); - auto content = document["content"]; + string privatekey_fieldname = "privatekey"; + auto privatekey = document["privatekey"]; + if (privatekey.is_null()) { + // Fallback to the old "content" behaviour + privatekey = document["content"]; + privatekey_fieldname = "content"; + } bool active = boolFromJson(document, "active", false); bool keyOrZone; @@ -990,7 +998,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon int64_t insertedId = -1; - if (content.is_null()) { + if (privatekey.is_null()) { int bits = keyOrZone ? ::arg().asNum("default-ksk-size") : ::arg().asNum("default-zsk-size"); auto docbits = document["bits"]; if (!docbits.is_null()) { @@ -1001,7 +1009,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon } } int algorithm = DNSSECKeeper::shorthand2algorithm(keyOrZone ? ::arg()["default-ksk-algorithm"] : ::arg()["default-zsk-algorithm"]); - auto providedAlgo = document["algo"]; + auto providedAlgo = document["algorithm"]; if (providedAlgo.is_string()) { algorithm = DNSSECKeeper::shorthand2algorithm(providedAlgo.string_value()); if (algorithm == -1) @@ -1021,13 +1029,14 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon } if (insertedId < 0) throw ApiException("Adding key failed, perhaps DNSSEC not enabled in configuration?"); - } else if (document["bits"].is_null() && document["algo"].is_null()) { - auto keyData = stringFromJson(document, "content"); + } else if (document["bits"].is_null() && document["algorithm"].is_null()) { + auto keyData = stringFromJson(document, privatekey_fieldname); DNSKEYRecordContent dkrc; DNSSECPrivateKey dpk; try { shared_ptr dke(DNSCryptoKeyEngine::makeFromISCString(dkrc, keyData)); dpk.d_algorithm = dkrc.d_algorithm; + // TODO remove in 4.2.0 if(dpk.d_algorithm == 7) dpk.d_algorithm = 5; @@ -1050,7 +1059,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon if (insertedId < 0) throw ApiException("Adding key failed, perhaps DNSSEC not enabled in configuration?"); } else { - throw ApiException("Either you submit just the 'content' field or you leave 'content' empty and submit the other fields."); + throw ApiException("Either you submit just the 'privatekey' field or you leave 'privatekey' empty and submit the other fields."); } apiZoneCryptokeysGET(zonename, insertedId, resp, dk); resp->status = 201; diff --git a/regression-tests.api/test_Zones.py b/regression-tests.api/test_Zones.py index b261b9810..02896cd0c 100644 --- a/regression-tests.api/test_Zones.py +++ b/regression-tests.api/test_Zones.py @@ -1653,6 +1653,8 @@ class AuthZoneKeys(ApiTestCase, AuthZonesHelperMixin): del key0['dnskey'] del key0['ds'] expected = { + u'algorithm': u'ECDSAP256SHA256', + u'bits': 256, u'active': True, u'type': u'Cryptokey', u'keytype': u'csk', diff --git a/regression-tests.api/test_cryptokeys.py b/regression-tests.api/test_cryptokeys.py index cf249bead..cccb3c783 100644 --- a/regression-tests.api/test_cryptokeys.py +++ b/regression-tests.api/test_cryptokeys.py @@ -72,7 +72,7 @@ class Cryptokeys(ApiTestCase): payload = { 'keytype': type, 'active' : active, - 'algo' : algo + 'algorithm' : algo } if bits > 0: payload['bits'] = bits @@ -264,4 +264,4 @@ class Cryptokeys(ApiTestCase): out = subprocess.check_output(["../pdns/pdnsutil", "--config-dir=.", "show-zone", self.zone]) self.assertIn("Active", out) except subprocess.CalledProcessError as e: - self.fail("pdnsutil show-zone failed: " + e.output) \ No newline at end of file + self.fail("pdnsutil show-zone failed: " + e.output) -- 2.40.0