From 03464f018daa291679f8a4011c95c2e58e76f2d8 Mon Sep 17 00:00:00 2001 From: Grigouze Date: Mon, 26 Mar 2018 23:31:11 +0200 Subject: [PATCH] Support DNSSEC commands in the CLI * ``gandi dnssec create`` will create a DNSSEC key for the domain ``domain.tld``. It taks 4 parameters, ``fqdn`` which is the domaine for which we want to create the key and ``flag`` which is the flag to use for creation (ZSK or KSK) and ``algorithm`` for the choice of the algorithm for the key and the ``public_key`` in a base64 encoded form. * ``gandi dnssec delete ressource`` will remove a DNSSEC key identified by a ressource identificator. * ``gandi dnssec list domain.tld`` will list DNSSEC keys for domain ``domain.tld``. --- gandi/cli/commands/dnssec.py | 52 +++++++++++++++++++++++++ gandi/cli/core/params.py | 24 ++++++++++++ gandi/cli/modules/dnssec.py | 39 +++++++++++++++++++ gandi/cli/tests/commands/test_dnssec.py | 41 +++++++++++++++++++ gandi/cli/tests/fixtures/_domain.py | 22 +++++++++++ gandicli.man.rst | 9 +++++ 6 files changed, 187 insertions(+) create mode 100644 gandi/cli/commands/dnssec.py create mode 100644 gandi/cli/modules/dnssec.py create mode 100644 gandi/cli/tests/commands/test_dnssec.py diff --git a/gandi/cli/commands/dnssec.py b/gandi/cli/commands/dnssec.py new file mode 100644 index 0000000..fd3bd04 --- /dev/null +++ b/gandi/cli/commands/dnssec.py @@ -0,0 +1,52 @@ +""" Domain namespace commands. """ + +import click + +from gandi.cli.core.cli import cli +from gandi.cli.core.params import pass_gandi, DNSSEC_FLAGS, DNSSEC_ALGORITHM + + +@cli.group(name='dnssec') +@pass_gandi +def dnssec(gandi): + """Commands related to dnssec.""" + + +@dnssec.command() +@click.option('--flags', default=None, type=DNSSEC_FLAGS, + help='Flags (ZSK or KSK)') +@click.option('--algorithm', default=None, type=DNSSEC_ALGORITHM, + help='Algorithm') +@click.option('--public_key', default=None, help='Public key (base64-encoded)') +@click.argument('resource') +@pass_gandi +def create(gandi, resource, flags, algorithm, public_key): + """Create DNSSEC key.""" + + result = gandi.dnssec.create(resource, flags, algorithm, public_key) + + return result + + +@dnssec.command() +@click.argument('resource') +@pass_gandi +def list(gandi, resource): + """List DNSSEC keys.""" + keys = gandi.dnssec.list(resource) + gandi.pretty_echo(keys) + + return keys + + +@dnssec.command() +@click.argument('resource', type=click.INT) +@pass_gandi +def delete(gandi, resource): + """Delete DNSSEC key. + """ + + result = gandi.dnssec.delete(resource) + gandi.echo('Delete successful.') + + return result diff --git a/gandi/cli/core/params.py b/gandi/cli/core/params.py index 96942c3..d2eae68 100644 --- a/gandi/cli/core/params.py +++ b/gandi/cli/core/params.py @@ -467,6 +467,28 @@ class DNSRecordsParamType(GandiChoice): return click.Choice.convert(self, value, param, ctx) +class AlgorithmType(click.Choice): + """ Choice parameter to create DNSSEC key """ + + name = 'algorithm' + choices = [1, 2, 3, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16, 253, 254] + + def __init__(self): + """ Initialize choices list. """ + pass + + +class FlagsType(click.Choice): + """ Choice parameter to create DNSSEC key """ + + name = 'flags' + choices = [256, 257] + + def __init__(self): + """ Initialize choices list. """ + pass + + DATACENTER = DatacenterParamType() PAAS_TYPE = PaasTypeParamType() DISK_IMAGE = DiskImageParamType() @@ -487,6 +509,8 @@ WEBACC_NAME = WebAccNameParamType() WEBACC_VHOST_NAME = WebAccVhostParamType() OPER_STEP = OperStepParamType() DNS_RECORDS = DNSRecordsParamType() +DNSSEC_ALGORITHM = AlgorithmType() +DNSSEC_FLAGS = FlagsType() class GandiOption(click.Option): diff --git a/gandi/cli/modules/dnssec.py b/gandi/cli/modules/dnssec.py new file mode 100644 index 0000000..fe541c2 --- /dev/null +++ b/gandi/cli/modules/dnssec.py @@ -0,0 +1,39 @@ +""" Dnssec commands module. """ + +from gandi.cli.core.base import GandiModule + + +class DNSSEC(GandiModule): + + """ Module to handle CLI commands. + + $ gandi dnssec create + $ gandi dnssec list + $ gandi dnssec delete + + """ + + @classmethod + def list(cls, fqdn): + """List operation.""" + return cls.call('domain.dnssec.list', fqdn) + + @classmethod + def create(cls, fqdn, flags, algorithm, public_key): + """Create a dnssec key.""" + fqdn = fqdn.lower() + + params = { + 'flags': flags, + 'algorithm': algorithm, + 'public_key': public_key, + } + + result = cls.call('domain.dnssec.create', fqdn, params) + + return result + + @classmethod + def delete(cls, id): + """Delete this dnss key.""" + return cls.call('domain.dnssec.delete', id) diff --git a/gandi/cli/tests/commands/test_dnssec.py b/gandi/cli/tests/commands/test_dnssec.py new file mode 100644 index 0000000..920f73d --- /dev/null +++ b/gandi/cli/tests/commands/test_dnssec.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +import re +import sys +from datetime import datetime + +from .base import CommandTestCase +from ..compat import mock +from gandi.cli.commands import dnssec + + +class DnssecTestCase(CommandTestCase): + + def test_list(self): + + result = self.invoke_with_exceptions(dnssec.list, ['iheartcli.com']) + + self.maxDiff = None + if sys.version_info[0] == 2: + self.assertEqual(result.output, """[{'algorithm': 5,\n 'date_created': datetime.datetime(2012, 2, 24, 17, 16, 8),\n 'digest': '457c626c008cc70d68133254abc4ee4eb79e4e6c99f9423b60b543fa8a69e6ac',\n 'digest_type': 2,\n 'flags': 257,\n 'id': 125,\n 'keytag': 9301,\n 'public_key': 'AwEAAdYixYvq9eJLRQcxUeYJWaxAGXiP/K1/C7XHbUWGzA8AHCRp81FAmfwcw1FrJ7bMViEegewPDGciQSv5HotPPOynUmkZbgztOeejH/+3Il/cM8SW4Et0i+99S7l9as+FI3AYOhsllDJK1WM9smn0S/9igfpR2dGmCyDU ZfeR1A49\\n'}]\n""") + self.assertEqual(result.exit_code, 0) + + def test_create(self): + result = self.invoke_with_exceptions( + dnssec.create, + ['iheartcli.com', + '--flags', 257, + '--algorithm', 5, + '--public_key', 'AwEAAdYixYvq9eJLRQcxUeYJWaxAGXiP/K1/C7XHbUWGzA8AHCRp81FA' + 'mfwcw1FrJ7bMViEegewPDGciQSv5HotPPOynUmkZbgztOeejH/+3Il/c' + 'M8SW4Et0i+99S7l9as+FI3AYOhsllDJK1WM9smn0S/9igfpR2dGmCyDU ZfeR1A49', + ]) + + self.assertEqual(result.output, """""") + self.assertEqual(result.exit_code, 0) + + def test_delete(self): + + result = self.invoke_with_exceptions(dnssec.delete, ['125']) + + self.assertEqual(result.output, """Delete successful.\n""") + self.assertEqual(result.exit_code, 0) diff --git a/gandi/cli/tests/fixtures/_domain.py b/gandi/cli/tests/fixtures/_domain.py index e80ef03..c8e3981 100644 --- a/gandi/cli/tests/fixtures/_domain.py +++ b/gandi/cli/tests/fixtures/_domain.py @@ -242,3 +242,25 @@ def zone_record_update(zone_id, version, opts, data): def zone_record_set(zone_id, version, data): return + + +def dnssec_delete(key_id): + return {'id': 666, 'step': 'WAIT'} + + +def dnssec_list(domain): + return [{'algorithm': 5, + 'date_created': datetime(2012, 2, 24, 17, 16, 8), + 'digest': '457c626c008cc70d68133254abc4ee4eb79e4e6c99f9423b60b543fa8a69e6ac', + 'digest_type': 2, + 'flags': 257, + 'id': 125, + 'keytag': 9301, + 'public_key': 'AwEAAdYixYvq9eJLRQcxUeYJWaxAGXiP/K1/C7XHbUWGzA8AHCRp81FA' + 'mfwcw1FrJ7bMViEegewPDGciQSv5HotPPOynUmkZbgztOeejH/+3Il/c' + 'M8SW4Et0i+99S7l9as+FI3AYOhsllDJK1WM9smn0S/9igfpR2dGmCyDU ZfeR1A49\n'}] + + + +def dnssec_create(domain, params): + return {'id': 667, 'step': 'WAIT'} diff --git a/gandicli.man.rst b/gandicli.man.rst index 3016dff..03eb42d 100644 --- a/gandicli.man.rst +++ b/gandicli.man.rst @@ -98,6 +98,9 @@ Namespaces: * dns keys recover Recover deleted key for a domain. * dns list Display records for a domain. * dns update Update record entry for a domain. +* dnssec create Create DNSSEC key. +* dnssec delete Delete DNSSEC key. +* dnssec list List DNSSEC keys. * docker Manage docker instances. * domain create Buy a domain. * domain renew Renew a domain. @@ -243,6 +246,12 @@ Details: * ``gandi disk snapshot resource`` will create a snapshot on the fly from a disk. Possible option is ``--name TEXT`` for the name of the snapshot (if not present, will be autogenerated). The operation can be done as background process using the option ``--background`` (or ``--bg``). +* ``gandi dnssec create`` will create a DNSSEC key for the domain ``domain.tld``. It taks 4 parameters, ``fqdn`` which is the domain for which we want to create the key and ``flag`` which is the flag to use for creation (ZSK or KSK) and ``algorithm`` for the choice of the algorithm for the key and the ``public_key`` in a base64 encoded form. + +* ``gandi dnssec delete ressource`` will remove a DNSSEC key identified by a ressource identificator. + +* ``gandi dnssec list domain.tld`` will list DNSSEC keys for domain ``domain.tld``. + * ``gandi docker`` will setup ssh forwarding towards a gandi VM, remotely feeding a docker unix socket. This, for example, can be used for zeroconf access to scripted temporary build VMs. The ``--vm`` option alters the ``dockervm`` configuration parameter and can be used to set the VM used for future docker connections. ``dockervm`` can also be set locally for per-project vms (See ``gandi config``). *NOTE*: passing option parameters to docker require the usage of the POSIX argument parsing ``--`` separator. *NOTE*: a local docker client is required for this command to operate. * ``gandi dns create`` will creating a new DNS record entry for specific domain ``domain.tld``. It takes 4 parameters, ``FQDN`` which is the domain on which to add the record, ``NAME`` which is the record relative name, ``TYPE`` which is the record type, ``VALUE`` which is the record value. Multiple values can be provided for ``VALUE`` parameter. Possible options are ``--ttl INTEGER`` to set record time to live value in seconds.