Possible to set default DNSSEC algorithm?

kdub

Active Member
Apr 19, 2019
29
8
3
California
cPanel Access Level
Root Administrator
Hello. Running a VPS with CENTOS 6.10 kvm, WHM v78.0.21, using PowerDNS 4.1.5 as master.

In cPanel account under Zone Editor enabling DNSSEC always generates using algo 8 RSA/SHA-256 2,048 bits and only digest type can be changed through the gui.

cpanel-dnssec.PNG

I'm setting up a slave DNS with NS1 and they require ECDSA (algorithm 13, ECDSAP256SHA256).

Interestingly, powerDNS documentation claims default algo since 4.0 is 13.

I tried uncommenting "default-ksk-algorithm=ecdsa256" in /etc/pdns.conf and restarting powerDNS with no effect to cPanel. However, pdnsutil secure-zone DOMAIN does default to algo 13 and a browser refresh in cpanel zone editor reflects algo 13.

Is there another setting somewhere in cPanel that controls the algo used when enabling DNSSEC via the zone editor? Thanks.
 

kdub

Active Member
Apr 19, 2019
29
8
3
California
cPanel Access Level
Root Administrator
TL;DR; doesn't matter what you set in pdns.conf, cpanel will always default to the hard-coded options for ddnsec which for nsec are nsec3, narrow, no opt_out, 7 iterations. and for algo are 8, RSA/SHA-256, ksk-2048, zsk-1024. Only way to change this is edit the cPanel code directly and risk losing it with updates. For now, if you need to use other DNSSEC options you'll have to do it all through the command line. Only downside is if you or clients toggle DNSSEC off and back on in the zone editor, it resets the key to the hard-coded defaults.

Anyone aware of a feature request for this or if it's in the pipe already?

So I dug deep into the cpanel code. Looks like the algo (and other options) is set by the API call from the zone editor page when toggling dnssec on for the domain. Unfortunately, at this point there is not an algo arg included in the API call, only the domain arg. The reason the algo defaults to 8 every time is because cpanel defaults to a hard-coded 8 when no algo is specified. So it looks like there's still some work to be done to implement this feature. Now that 13 is powerDNS default and DNS providers are moving towards algo 13 I expect cpanel to follow suit.

To be honest, in the zone editor, it wouldn't be difficult to add an algo dropdown next to the enable button allowing the user to select the desired algo. Ideally it would be populated with the algos supported by the installed version of powerDNS found via "pdnsutil list-algorithms". Really there should also be ability to set all options supported for nsec and ksk/zsk size. And cpanel should pull the default values for all of these from the pdns.conf rather than completely ignoring them in favor of hard-coded values. Another option, probably even easier for most users, would be to add a section in the cpanel config so these values can be specified through the whm interface globally with a per account override ability.

I might try implementing at least part of this. Problem is the changes would likely be erased with every cpanel update. If anyone wants to take a stab at it here's the relevant existing code starting from the zone editor html and backing out to the pdnsutil command line call. I made some comments in the code so you can follow what's happening:

Code:
#/usr/local/cpanel/base/frontend/paper_lantern/zone_editor/views/ lines 23-31
# toggling DNSSEC on/off calls dnssec.toggle_status() in /usr/local/cpanel/base/frontend/paper_lantern/zone_editor/index.cmb.js

            <div class="col-xs-12">
                <label class="toggle-control-label" for="toggle_dnssec_status">[% locale.maketext("Status") %]</label>
                <toggle-switch
                    id="toggle_dnssec_status"
                    ng-model="dnssec.enabled"
                    enabled-label="[% locale.maketext('Enabled') %]"
                    disabled-label="[% locale.maketext('Disabled') %]"
                    on-toggle="dnssec.toggle_status()"></toggle-switch>
            </div>
Code:
# /usr/local/cpanel/base/frontend/paper_lantern/zone_editor/index.cmb.js lines 2718-2724
# dnssec.toggle_status calls DNSSEC.enable(dnssec.domain) in /usr/local/cpanel/base/frontend/paper_lantern/zone_editor/services/dnssec.js

                    dnssec.toggle_status = function() {
                        if (dnssec.enabled) {
                            dnssec.show_disable_warning = true;
                            return;
                        }

                        return DNSSEC.enable(dnssec.domain)
Code:
# /usr/local/cpanel/base/frontend/paper_lantern/zone_editor/services/dnssec.js lines 27-55
# API call to enable dnssec with domain as sole arg handled by /usr/local/cpanel/Cpanel/API/DNSSEC.pm

            function set_state(domain, status) {
                var apiCall = new APIREQUEST.Class();
                if (status) {
                    apiCall.initialize("DNSSEC", "enable_dnssec");
                } else {
                    apiCall.initialize("DNSSEC", "disable_dnssec");
                }
                apiCall.addArgument("domain", domain);

                return $q.when(API.promise(apiCall.getRunArguments()))
                    .then(function(response) {
                        response = response.parsedResponse;
                        if (response.status) {

                            // The API call can succeed but you can still get an error message
                            // so check for that message and return it
                            if (response.meta.DNSSEC && response.meta.DNSSEC.failed) {
                                return $q.reject(response.meta.DNSSEC.failed[domain]);
                            }
                            return true;
                        } else {
                            return $q.reject(response.error);
                        }
                    });
            }

            api.enable = function(domain) {
                return set_state(domain, true);
            };
Code:
# /usr/local/cpanel/Cpanel/API/DNSSEC.pm lines 29-52
# dnssec args are assembled and passed to dnssec admin binary for handling. This is where $algo_config and $nsec_config are set using args passed from the API call. Note that the only arg actually being provided is the domain name, others are null
# DNSSEC admin bin calls several functions in /usr/local/cpanel/Cpanel/NameServer/Conf/PowerDNS.pm to handle the rest

sub enable_dnssec {
    my ( $args, $result ) = @_;

    if ( !main::hasfeature($feature) ) {
        $result->error_feature($feature);
        return;
    }

    my @domains     = $args->get_length_required_multiple('domain');
    my $nsec_config = {
        'use_nsec3'        => $args->get('use_nsec3'),
        'nsec3_opt_out'    => $args->get('nsec3_opt_out'),
        'nsec3_iterations' => $args->get('nsec3_iterations'),
        'nsec3_narrow'     => $args->get('nsec3_narrow'),
        'nsec3_salt'       => $args->get('nsec3_salt'),
    };

    my $algo_config = {
        'algo_num'  => $args->get('algo_num'),
        'key_setup' => $args->get('key_setup'),
        'ksk_size'  => $args->get('ksk_size'),
        'zsk_size'  => $args->get('zsk_size'),
    };

    my $enabled = _call_adminbin( 'ENABLE_DNSSEC', [ $nsec_config, $algo_config, \@domains ] );

    $result->metadata(
        'DNSSEC',
        $enabled
    );

    return 1;
}
Code:
# /usr/local/cpanel/Cpanel/NameServer/Conf/PowerDNS.pm
# presumably adminBin sets the default algo and nsec configs using the args provided if available (none ever are)  otherwise setting hard-coded defaults. lines 376-397

sub algo_config_defaults {
    my ( $self, $algo_config ) = @_;
    my $new_algo_config = {
        algo_num  => $algo_config->{algo_num} // 8,
        key_setup => $algo_config->{key_setup} // 'auto',
        ksk_size  => $algo_config->{ksk_size} // 'auto',
        zsk_size  => $algo_config->{zsk_size} // 'auto',
    };
    return $new_algo_config;
}

sub nsec_config_defaults {
    my ( $self, $nsec_config ) = @_;
    my $new_nsec_config = {
        use_nsec3        => $nsec_config->{use_nsec3} // 1,
        nsec3_opt_out    => $nsec_config->{nsec3_opt_out} // 0,
        nsec3_iterations => $nsec_config->{nsec3_iterations} // 7,
        nsec3_narrow     => $nsec_config->{nsec3_narrow} // 1,
        nsec3_salt       => $nsec_config->{nsec3_salt} // Cpanel::Rand::Get::getranddata( 16, [ 0 .. 9, 'a' .. 'f' ] ),
    };
    return $new_nsec_config;
}

# presumably then calls validate_algo_config() line 306 and validate_nsec3_config() line 343 (not pasted here because it's just too much) which validates the configs using _supported_algorithms() (lines 408-460) to fill the missing details using the hard-coded values.

sub _supported_algorithms {
    return {
        '5' => {
            'desc'      => 'RSA/SHA-1',
            'tag'       => 'RSASHA1',
            'ksk_size'  => 2048,
            'zsk_size'  => 1024,
            'key_setup' => 'classic',
        },
        '6' => {
            'desc'      => 'DSA-NSEC3-SHA1',
            'tag'       => 'DSA-NSEC3-SHA1',
            'ksk_size'  => 2048,
            'zsk_size'  => 1024,
            'key_setup' => 'classic',
        },
        '7' => {
            'desc'      => 'RSASHA1-NSEC3-SHA1',
            'tag'       => 'RSASHA1-NSEC3-SHA1',
            'ksk_size'  => 2048,
            'zsk_size'  => 1024,
            'key_setup' => 'classic',
        },
        '8' => {
            'desc'      => 'RSA/SHA-256',
            'tag'       => 'RSASHA256',
            'ksk_size'  => 2048,
            'zsk_size'  => 1024,
            'key_setup' => 'classic',
        },
        '10' => {
            'desc'      => 'RSA/SHA-512',
            'tag'       => 'RSASHA512',
            'ksk_size'  => 2048,
            'zsk_size'  => 1024,
            'key_setup' => 'classic',
        },
        '13' => {
            'desc'      => 'ECDSA Curve P-256 with SHA-256',
            'tag'       => 'ECDSAP256SHA256',
            'ksk_size'  => 256,
            'zsk_size'  => 256,
            'key_setup' => 'simple',
        },
        '14' => {
            'desc'      => 'ECDSA Curve P-384 with SHA-384',
            'tag'       => 'ECDSAP384SHA384',
            'ksk_size'  => 384,
            'zsk_size'  => 384,
            'key_setup' => 'simple',
        }
    };
}

# presumably adminBin then calls secure_zone() (lines 88-111) which calls _add_ksk(). if key_type is 'classic' (for algos <13) _add_zsk() is called as well. In powerDNS 4.0+ using algo 13+ generates a single key used for both ksk and zsk. Lower algos require separate key generation. I believe 'pdnsutil secure-zone' does this automatically but cpanel relies on 'pdnsutil add-zone-key' instead which requires manually generating both if the algo <13.

sub secure_zone {
    my ( $self, $algo_config, $domain ) = @_;

    return { 'success' => 0, 'error' => 'DNSSEC is already enabled' }
      if scalar keys %{ $self->ds_records($domain) };

    my $run = _add_ksk( $algo_config, $domain );
    return $run if !$run->{'success'};

    if ( $algo_config->{'key_setup'} eq 'classic' ) {
        my $run = _add_zsk( $algo_config, $domain );
        if ( !$run->{'success'} ) {
            $self->unsecure_zone($domain);
            return $run;
        }
    }

    # PowerDNS docs recommend running this after securing a zone,
    # as it fixes the 'ordername' and 'auth' fields.
    #
    # This is not strictly required on newly secured zones, but since
    # it becomes a noop if no changes are needed, its safe to do.
    return Cpanel::NameServer::Utils::PowerDNS::run_pdnsutil( { 'args' => [ 'rectify-zone', $domain ] } );
}

# pretty clear what happens here, _add_ksk() & _add_zsk() call run_pdnsutil() line 52 in /usr/local/cpanel/Cpanel/NameServer/Utils/powerDNS.pm with args to handle the command line action

sub _add_ksk {
    my ( $algo_config, $domain ) = @_;
    return Cpanel::NameServer::Utils::PowerDNS::run_pdnsutil( { 'args' => [ 'add-zone-key', $domain, 'ksk', $algo_config->{'ksk_size'}, 'active', $algo_config->{'tag'} ] } );
}

sub _add_zsk {
    my ( $algo_config, $domain ) = @_;
    return Cpanel::NameServer::Utils::PowerDNS::run_pdnsutil( { 'args' => [ 'add-zone-key', $domain, 'zsk', $algo_config->{'zsk_size'}, 'active', $algo_config->{'tag'} ] } );
}
 
Last edited:

cPanelMichael

Administrator
Staff member
Apr 11, 2011
47,880
2,258
463
Hello @kdub,

The modification you are looking for isn't supported at this time, as it aligns with the fact that DNSSEC is currently incompatible with DNS Clustering using PowerDNS with cPanel & WHM. You can add feedback and vote for the feature request on the link below:

DNSSEC support in Clustering

Let me know if this is the information you are looking for.

Thank you.
 
  • Like
Reactions: Jean Boudreau

kdub

Active Member
Apr 19, 2019
29
8
3
California
cPanel Access Level
Root Administrator
@cPanelMichael Thanks for your response. I'm aware of the feature request for DNSSECC clustering support. I'm just looking to use a thirdparty DNS like NS1 or cloudflare to set up a hidden master, not so much the cpanel clustering.

Rather than waste time modifying all the cpanel code I posted above, I managed to implement the missing DNSSEC functionality using a pre action hook when DNSSEC is toggled on, to set all of the DNSSEC options based on pdns.conf. Which is tied to this topic Intercept and modify data with hook I posted shortly after starting this one. Providing all the options basically overrides cpanel's hard coded defaults.

If I can find the time, I'd like to write a plugin to allow global defaults to be set in WHM, and per account overrides to be set in cpanel. If you have a plugin template or example of something similar in scope or function that I could use to jump-start my development, I would be grateful. Thanks.
 
  • Like
Reactions: cPanelMichael