Puppet Class: bind

Defined in:
manifests/init.pp

Summary

Manage the Bind9 DNS daemon and configuration

Overview

Examples:

Install a local caching resolver


class { 'bind':
  listen_on         => [ '127.0.0.1' ],
  listen_on_v6      => [ '::1' ],
  allow_query       => [ '127.0.0.1', '::1' ],
  allow_query_cache => [ '127.0.0.1', '::1' ],
  allow_recursion   => [ '127.0.0.1', '::1' ],
}

Use custom options to set uncommon options


class { 'bind':
  listen_on      => [ '127.0.0.1' ],
  custom_options => {
    'tcp-idle-timeout' => 600,
    'dnstab'           => [ 'auth', 'resolver', ],
    'rrset-order'      => {
      'type A name "example.com"'    => 'order random',
      'type AAAA name "example.com"' => 'order cyclic',
    },
  },
}

Parameters:

  • confdir (Stdlib::Absolutepath)

    The directory where the main Bind configuration file is located. Example: ‘/etc/bind`.

    Default: operating system specific

  • vardir (Stdlib::Absolutepath)

    The directory where Bind stores other files (e.g. primary zone files or keys managed by Bind). Example: ‘/var/lib/bind`.

    Default: operating system specific

  • cachedir (Stdlib::Absolutepath)

    The directory where Bind stores volatile data (e.g. secondary zone files). Example: ‘/var/cache/bind`.

    Default: operating system specific

  • rndc_keyfile (Stdlib::Absolutepath)

    The file where the secret key for the rndc program is stored.

    Default: operating system specific

  • rndc_program (Stdlib::Absolutepath)

    The full path of the rndc program.

    Default: operating system specific

  • checkzone_program (Stdlib::Absolutepath)

    The full path of the named-checkzone program.

    Default: operating system specific

  • bind_user (String)

    Run the Bind daemon as this user. This parameter is also used to set the owner of some directories and files that the Bind daemon needs to write to.

    Default: operating system specific

  • bind_group (String)

    The group ownership for some Bind related directories and files.

    Default: operating system specific

  • package_name (String)

    The name of the Bind package to install.

    Default: operating system specific

  • service_name (String)

    The name of the Bind service to manage.

    Default: operating system specific

  • listen_on (Bind::AddressMatchList) (defaults to: [])

    An array of strings to define the IPv4 address(es) on which the daemon will listen for queries. The string ‘any` may be used to listen on all available interfaces and addresses. This is also the default if the parameter is unset. The string `none` may be used to disable IPv4.

    Use ‘bind::listen_on` to define more complex configurations.

  • listen_on_v6 (Bind::AddressMatchList) (defaults to: [])

    An array of strings to define the IPv6 address(es) on which the daemon will listen for queries. The string ‘any` may be used to listen on all available interfaces and addresses. This is also the default if the parameter is unset. The string `none` may be used to disable IPv6.

    Use ‘bind::listen_on_v6` to define more complex configurations.

  • ipv4_enable (Boolean) (defaults to: true)

    Should Bind use IPv4. At least one of ‘ipv4_enable` and `ipv6_enable` must be set to true.

  • ipv6_enable (Boolean) (defaults to: true)

    Should Bind use IPv6. At least one of ‘ipv4_enable` and `ipv6_enable` must be set to true.

  • views_enable (Boolean) (defaults to: false)

    Should views be enabled.

  • dnssec_enable (Boolean) (defaults to: true)

    Should DNSSEC be enabled. This parameter is ignored for Bind 9.16.0 or later where DNSSEC is always enabled.

  • dnssec_lookaside (Boolean) (defaults to: false)

    Should DNSSEC Lookaside Validation be enabled. This parameter is ignored for Bind 9.16.0 or later where DNSSEC Lookaside Validation is obsolete.

  • dnssec_validation (Bind::DNSSEC::Validation) (defaults to: 'auto')

    Should DNSSEC Validation be enabled.

  • empty_zones_enable (Boolean) (defaults to: true)

    Should automatic empty zones be enabled.

  • control_channels_enable (Boolean) (defaults to: true)

    Should control channels be enabled. All ‘bind::controls::unix` and `bind::controls::inet` configuration items will be ignored if this is set to `false`. In this case an empty controls statement will be generated and the default control channel will also be disabled. The default control channel will be enabled automatically if this parameter is `true` and no explicit channels are created using the `bind::controls::unix` or `bind::controls::inet` defined type.

  • allow_query (Bind::AddressMatchList) (defaults to: [])

    An array of IP addresses/networks or ACL names that are allowed to query this Bind server.

  • allow_query_cache (Bind::AddressMatchList) (defaults to: [])

    An array of IP addresses/networks or ACL names that are allowed to use the query cache on this Bind server.

  • allow_recursion (Bind::AddressMatchList) (defaults to: [])

    An array of IP addresses/networks or ACL names that are allowed to use this Bind server for recursion. Recursion is automatically turned on if this parameter is not empty.

  • blackhole (Bind::AddressMatchList) (defaults to: [])

    An array of IP addresses/networks that are ignored by the server. Requests from sources matching this list will not be answered.

  • forwarders (Bind::AddressMatchList) (defaults to: [])

    An array of other DNS servers that can be used to forward unresolvable queries to.

  • forward (Bind::Forward) (defaults to: 'first')

    The type of forwarding used. This parameter is only used if forwarders are actually defined. Valid values: ‘first’, ‘only’. Note that automatic empty zones for RFC 6303 are ignored if this parameter is set to ‘only`.

  • root_mirror_enable (Boolean) (defaults to: false)

    Should a mirror for the root domain “.” be installed locally. See RFC 7706 for details.

  • root_hints_enable (Boolean) (defaults to: false)

    Should a local copy of the list of servers that are authoritative for the root domain “.” be included. This is normally not needed since Bind contains an internal list of root nameservers and ‘named` will query the servers in the list until an authoritative response is received. Normally this parameter can be left at default.

  • root_hints_source (String) (defaults to: "puppet:///modules/${module_name}/zones/db.root")

    The source file to use for the root hints. The default is a file provided by this module.

  • localhost_forward_enable (Boolean) (defaults to: true)

    Should the forward zone for localhost be enabled.

  • localhost_forward_source (String) (defaults to: "puppet:///modules/${module_name}/zones/db.localhost")

    The source file to use for the localhost forward zone. The default is a file provided by this module.

  • localhost_reverse_enable (Boolean) (defaults to: true)

    Should the reverse zone for localhost be enabled.

  • localhost_reverse_source (String) (defaults to: "puppet:///modules/${module_name}/zones/db.127")

    The source file to use for the localhost reverse zone. The default is a file provided by this module.

  • filter_aaaa_on_v4 (Bind::Filter_aaaa_on_v4) (defaults to: undef)

    Should AAAA records be omitted from the response if the IPv4 transport is used. If this is set to ‘yes` then it does not apply if the queried zone is DNSSEC-signed. Setting this parameter to `break-dnssec` will also omit DNSSEC related RRs if AAAA records are filtered. Valid options: `no`, `yes`, `break-dnssec`. This parameter is ignored for Bind 9.16.0 or later.

  • window (Integer[0,3600]) (defaults to: 0)

    The size of the rolling window measured in seconds over which the rate limits are calculated.

  • ipv4_prefix_length (Integer[0,32]) (defaults to: 0)

    Define the number of bits that are used to group IPv4 addresses (like a netmask). The rate limits are applied to all requests having the same network prefix.

  • ipv6_prefix_length (Integer[0,128]) (defaults to: 0)

    Define the number of bits that are used to group IPv6 addresses (like a netmask). The rate limits are applied to all requests having the same network prefix.

  • log_only (Boolean) (defaults to: false)

    Do not really limit the queries but only log that it would happen. This can be used to test rate limits before enforcing them.

  • exempt_clients (Array[String]) (defaults to: [])

    An array of IP addresses/networks or ACL names that are never limited.

  • all_per_second (Optional[Integer[0,1000]]) (defaults to: undef)

    Limit the number of total answers per second for an IP address to the given value.

  • errors_per_second (Optional[Integer[0,1000]]) (defaults to: undef)

    Limit the number of total error answers per second for an IP address to the given value.

  • responses_per_second (Optional[Integer[0,1000]]) (defaults to: undef)

    Limit the number of identical responses per second for an IP address to the given value.

  • referrals_per_second (Optional[Integer[0,1000]]) (defaults to: undef)

    Limit the number of referrals per second to the given value.

  • nodata_per_second (Optional[Integer[0,1000]]) (defaults to: undef)

    Limit the number of NODATA responses per second to the given value.

  • nxdomains_per_second (Optional[Integer[0,1000]]) (defaults to: undef)

    Limit the number of NXDOMAIN responses per second to the given value.

  • qps_scale (Optional[Integer[0,1000]]) (defaults to: undef)

    Value to define the query per second scaling when using rate limiting.

  • slip (Optional[Integer[0,10]]) (defaults to: undef)

    Set the rate at which queries over the defined limit are returned with the truncate bit.

  • max_cache_size (Integer) (defaults to: 0)

    The maximum number of bytes to use for the server’s cache. If views are used then the size applies to every view separately. If this value is zero then no limit is configured.

  • min_cache_ttl (Integer) (defaults to: 0)

    The minimum number of seconds for which the server will cache positive answers.

  • max_cache_ttl (Integer) (defaults to: 0)

    The maximum number of seconds for which the server will cache positive answers. If this value is zero then the config parameter will be omitted and the Bind default of 1 week will be used.

  • min_ncache_ttl (Integer) (defaults to: 0)

    The minimum number of seconds for which the server will cache negative answers.

  • max_ncache_ttl (Integer) (defaults to: 0)

    The maximum number of seconds for which the server will cache negative answers. If this value is zero then the config parameter will be omitted and the Bind default of 3 hours will be used.

  • servfail_ttl (Integer) (defaults to: 0)

    The number of seconds that SERVFAIL responses caused by DNSSEC validation errors are cached. Can be set to 0 to disable caching.

  • custom_options (Hash[String,Data]) (defaults to: {})

    Additional config options that are not implemented as class parameters can be set by a hash of custom options. Each key of the hash will be added to the option block of the configuration. For string or numeric values the value will be added as a normal option value. If the value is a hash or an array it will be included as an additional block enclosed in braces.

  • package_ensure (String) (defaults to: 'installed')

    The state of the Bind package. Normally this is set to ‘installed` or a specific version number.

  • service_ensure (Stdlib::Ensure::Service) (defaults to: 'running')

    Whether the Bind service should be running.

  • service_enable (Boolean) (defaults to: true)

    Should the Bind service be enabled.

  • manage_rndc_keyfile (Boolean) (defaults to: true)

    Should the rndc key file be managed by the main class. If this is ‘true` (the default) then the secret key generated by the package installation will be used. Only the file mode and owner are managed without updating the content of the key file. If this is `false` then the key file for the `rndc` tool is not managed by the main class. Use the `bind::key` defined type to manage the key on your own. Caution: changing the key while `named` is running leads to a problem because Puppet can’t reload or restart the service after the key file has been updated because the daemon still uses the old key.

  • report_hostname (Optional[String]) (defaults to: undef)

    The hostname the will be reported by Bind. If this is undefined (default), then Bind will report the local hostname. Set this to a string to hide the hostname and report the given string instead. Use the empty string to disable hostname reporting completely.

  • report_version (Optional[String]) (defaults to: undef)

    The version string that will be reported by Bind. If this is undefined (default), then Bind will report the version that has been installed. Set this to a string to hide the version number and report the given string instead. Use the empty string to disable version reporting completely.

    Use the following command to test: dig @127.0.0.1 version.bind chaos txt

  • querylog_enable (Optional[Boolean]) (defaults to: undef)

    Should the querylog be enabled.

  • trust_anchor_telemetry (Optional[Boolean]) (defaults to: undef)

    Should the trust anchor telemetry transmission be enable. When enabled, once a day the DNSSEC trust anchors in use will be transmitted to the zon owners. This is enabled by default.



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
# File 'manifests/init.pp', line 325

class bind (
  Stdlib::Absolutepath      $confdir,
  Stdlib::Absolutepath      $vardir,
  Stdlib::Absolutepath      $cachedir,
  Stdlib::Absolutepath      $rndc_keyfile,
  Stdlib::Absolutepath      $rndc_program,
  Stdlib::Absolutepath      $checkzone_program,
  String                    $bind_user,
  String                    $bind_group,
  String                    $package_name,
  String                    $service_name,
  Bind::AddressMatchList    $listen_on                = [],
  Bind::AddressMatchList    $listen_on_v6             = [],
  Boolean                   $ipv4_enable              = true,
  Boolean                   $ipv6_enable              = true,
  Boolean                   $views_enable             = false,
  Boolean                   $dnssec_enable            = true,
  Boolean                   $dnssec_lookaside         = false,
  Bind::DNSSEC::Validation  $dnssec_validation        = 'auto',
  Boolean                   $empty_zones_enable       = true,
  Boolean                   $control_channels_enable  = true,
  Bind::AddressMatchList    $allow_query              = [],
  Bind::AddressMatchList    $allow_query_cache        = [],
  Bind::AddressMatchList    $allow_recursion          = [],
  Bind::AddressMatchList    $blackhole                = [],
  Bind::AddressMatchList    $forwarders               = [],
  Bind::Forward             $forward                  = 'first',
  Boolean                   $root_mirror_enable       = false,
  Boolean                   $root_hints_enable        = false,
  String                    $root_hints_source        = "puppet:///modules/${module_name}/zones/db.root",
  Boolean                   $localhost_forward_enable = true,
  String                    $localhost_forward_source = "puppet:///modules/${module_name}/zones/db.localhost",
  Boolean                   $localhost_reverse_enable = true,
  String                    $localhost_reverse_source = "puppet:///modules/${module_name}/zones/db.127",
  Integer                   $max_cache_size           = 0,
  Integer                   $min_cache_ttl            = 0,
  Integer                   $max_cache_ttl            = 0,
  Integer                   $min_ncache_ttl           = 0,
  Integer                   $max_ncache_ttl           = 0,
  Integer                   $servfail_ttl             = 0,
  Hash[String,Data]         $custom_options           = {},
  String                    $package_ensure           = 'installed',
  Stdlib::Ensure::Service   $service_ensure           = 'running',
  Boolean                   $service_enable           = true,
  Boolean                   $manage_rndc_keyfile      = true,
  Bind::Filter_aaaa_on_v4   $filter_aaaa_on_v4        = undef,
  Integer[0,3600]           $window                   = 0,
  Integer[0,32]             $ipv4_prefix_length       = 0,
  Integer[0,128]            $ipv6_prefix_length       = 0,
  Boolean                   $log_only                 = false,
  Array[String]             $exempt_clients           = [],
  Optional[Integer[0,1000]] $all_per_second           = undef,
  Optional[Integer[0,1000]] $errors_per_second        = undef,
  Optional[Integer[0,1000]] $responses_per_second     = undef,
  Optional[Integer[0,1000]] $referrals_per_second     = undef,
  Optional[Integer[0,1000]] $nodata_per_second        = undef,
  Optional[Integer[0,1000]] $nxdomains_per_second     = undef,
  Optional[Integer[0,1000]] $qps_scale                = undef,
  Optional[Integer[0,10]]   $slip                     = undef,
  Optional[String]          $report_hostname          = undef,
  Optional[String]          $report_version           = undef,
  Optional[Boolean]         $querylog_enable          = undef,
  Optional[Boolean]         $trust_anchor_telemetry   = undef,
) {
  $header_message = '// This file is managed by Puppet. DO NOT EDIT.'

  #
  # Version specific options
  #

  $version = ('named_version' in $facts) ? {
    true    => $facts['named_version'],
    default => '4.9.3',
  }

  $_filter_aaaa_on_v4 = (versioncmp($version, '9.16.0') < 0) ? {
    true  => $filter_aaaa_on_v4,
    false => undef,
  }

  $_dnssec_enable = (versioncmp($version, '9.16.0') < 0) ? {
    true    => $dnssec_enable,
    default => undef,
  }

  $_dnssec_lookaside =  (versioncmp($version, '9.16.0') < 0) ? {
    true    => $dnssec_lookaside,
    default => undef,
  }

  $dnssec_policy_available = (versioncmp($version, '9.16.0') >= 0)

  #
  # Package
  #

  package { 'bind':
    ensure => $package_ensure,
    name   => $package_name,
  }

  #
  # Directories
  #

  file { $confdir:
    ensure  => directory,
    owner   => 'root',
    group   => $bind_group,
    mode    => '2755',
    require => Package['bind'],
  }

  file { $vardir:
    ensure  => directory,
    owner   => 'root',
    group   => $bind_group,
    mode    => '0775',
    require => Package['bind'],
  }

  file { "${vardir}/keys":
    ensure => directory,
    owner  => $bind_user,
    group  => $bind_group,
    mode   => '0750',
    before => Bind::Config['named.conf'],
  }

  # Some directories must be writable by the user that named runs as.
  ['primary', 'secondary'].each |$type| {
    file { "${vardir}/${type}":
      ensure => directory,
      owner  => $bind_user,
      group  => $bind_group,
      mode   => '0750',
      before => Bind::Config['named.conf'],
    }

    file { "${vardir}/${type}/README":
      ensure => file,
      owner  => $bind_user,
      group  => $bind_group,
      mode   => '0444',
      source => "puppet:///modules/${module_name}/README.${type}",
    }
  }

  file { $cachedir:
    ensure  => directory,
    owner   => 'root',
    group   => $bind_group,
    mode    => '0775',
    require => Package['bind'],
    before  => Bind::Config['named.conf'],
  }

  #
  # Keys
  #

  file { "${confdir}/keys":
    ensure => directory,
    owner  => 'root',
    group  => $bind_group,
    mode   => '0770',
  }

  concat { 'named.conf.keys':
    path           => "${confdir}/named.conf.keys",
    owner          => 'root',
    group          => $bind_group,
    mode           => '0640',
    ensure_newline => true,
    warn           => $header_message,
    require        => File[$confdir],
    notify         => Service['bind'],
  }

  # Manage keyfile for rndc.
  if $manage_rndc_keyfile {
    bind::key { 'rndc-key':
      keyfile        => $rndc_keyfile,
      manage_content => false,
      require        => File[$confdir],
      notify         => Service['bind'],
    }
  }

  #
  # Logging
  #

  concat { 'named.conf.logging':
    path           => "${confdir}/named.conf.logging",
    owner          => 'root',
    group          => $bind_group,
    mode           => '0640',
    ensure_newline => true,
    warn           => $header_message,
    require        => File[$confdir],
    notify         => Service['bind'],
  }

  concat::fragment { 'named.conf.logging-start':
    target  => 'named.conf.logging',
    order   => '00',
    content => "\nlogging {\n",
  }

  concat::fragment { 'named.conf.logging-end':
    target  => 'named.conf.logging',
    order   => '99',
    content => "};\n",
  }

  #
  # ACLs
  #

  concat { 'named.conf.acls':
    path           => "${confdir}/named.conf.acls",
    owner          => 'root',
    group          => $bind_group,
    mode           => '0640',
    ensure_newline => true,
    warn           => $header_message,
    require        => File[$confdir],
    notify         => Service['bind'],
  }

  #
  # Views
  #

  concat { 'named.conf.views':
    ensure         => bool2str($views_enable, 'present', 'absent'),
    path           => "${confdir}/named.conf.views",
    owner          => 'root',
    group          => $bind_group,
    mode           => '0640',
    ensure_newline => true,
    warn           => $header_message,
    require        => File[$confdir],
    notify         => Service['bind'],
  }

  #
  # DNSSEC policies
  #

  if $dnssec_policy_available {
    concat { 'named.conf.policies':
      path           => "${confdir}/named.conf.policies",
      owner          => 'root',
      group          => $bind_group,
      mode           => '0640',
      ensure_newline => true,
      warn           => $header_message,
      require        => File[$confdir],
      notify         => Service['bind'],
    }
  }

  #
  # Zones
  #

  concat { 'named.conf.zones':
    path           => "${confdir}/named.conf.zones",
    owner          => 'root',
    group          => $bind_group,
    mode           => '0640',
    ensure_newline => true,
    warn           => $header_message,
    require        => File[$confdir],
    notify         => Service['bind'],
  }

  #
  # Options
  #

  $options = {
    'vardir'                 => $vardir,
    'confdir'                => $confdir,
    'cachedir'               => $cachedir,
    'report_hostname'        => $report_hostname,
    'report_version'         => $report_version,
    'servfail_ttl'           => $servfail_ttl,
    'min_cache_ttl'          => $min_cache_ttl,
    'max_cache_ttl'          => $max_cache_ttl,
    'min_ncache_ttl'         => $min_ncache_ttl,
    'max_ncache_ttl'         => $max_ncache_ttl,
    'max_cache_size'         => $max_cache_size,
    'filter_aaaa_on_v4'      => $_filter_aaaa_on_v4,
    'querylog_enable'        => $querylog_enable,
    'dnssec_enable'          => $_dnssec_enable,
    'dnssec_lookaside'       => $_dnssec_lookaside,
    'dnssec_validation'      => $dnssec_validation,
    'trust_anchor_telemetry' => $trust_anchor_telemetry,
    'empty_zones_enable'     => $empty_zones_enable,
  }

  concat { 'named.conf.options':
    path           => "${confdir}/named.conf.options",
    owner          => 'root',
    group          => $bind_group,
    mode           => '0640',
    ensure_newline => true,
    warn           => $header_message,
    require        => File[$confdir],
    notify         => Service['bind'],
  }

  # Order of fragments in Concat['named.conf.options']:
  # 10 options {
  # 11   listen-on { }
  # 12   listen-on port ... { }
  # 13   listen-on-v6 { }
  # 14   listen-on-v6 port ... { }
  # 20   forwarders
  # 21   forward
  # 40   blackhole
  # 75   main
  # 80   rate-limit { }
  # 83   *custom options*
  # 85 }
  # 90 controls {
  # 91   unix ...
  # 92   inet ...
  # 93 }

  concat::fragment { 'named.conf.options-head':
    target  => 'named.conf.options',
    order   => '10',
    content => epp("${module_name}/options.head.epp", { 'cachedir' => $cachedir }),
  }

  concat::fragment { 'named.conf.options-main':
    target  => 'named.conf.options',
    order   => '75',
    content => epp("${module_name}/options.main.epp", $options),
  }

  $limits = {
    'all_per_second'       => $all_per_second,
    'errors_per_second'    => $errors_per_second,
    'responses_per_second' => $responses_per_second,
    'referrals_per_second' => $referrals_per_second,
    'nodata_per_second'    => $nodata_per_second,
    'nxdomains_per_second' => $nxdomains_per_second,
    'qps_scale'            => $qps_scale,
    'slip'                 => $slip,
  }

  # Include rate limit config only if at least one parameter has been set
  $real_limits = $limits.filter |$key, $val| { $val =~ NotUndef }

  unless empty($real_limits) {
    $params = {
      'window'             => $window,
      'ipv4_prefix_length' => $ipv4_prefix_length,
      'ipv6_prefix_length' => $ipv6_prefix_length,
      'log_only'           => $log_only,
      'exempt_clients'     => $exempt_clients,
    }

    concat::fragment { 'named.conf.rate-limit':
      target  => 'named.conf.options',
      order   => '80',
      content => epp("${module_name}/rate-limit.epp", $params + $limits),
    }
  }

  unless empty($custom_options) {
    $_custom_options = bind::gencfg($custom_options, 2)
    concat::fragment { 'named.conf.options-custom':
      target  => 'named.conf.options',
      order   => '83',
      content => $_custom_options,
    }
  }

  concat::fragment { 'named.conf.options-tail':
    target  => 'named.conf.options',
    order   => '85',
    content => "};\n",
  }

  #
  # Listen-on
  #

  unless empty($listen_on) {
    bind::listen_on { 'bind::listen-on':
      address => $listen_on,
    }
  }

  unless empty($listen_on_v6) {
    bind::listen_on_v6 { 'bind::listen-on-v6':
      address => $listen_on_v6,
    }
  }

  #
  # Options
  #

  unless empty($forwarders) {
    bind::aml { 'forwarders':
      address_match_list => $forwarders,
      target             => 'named.conf.options',
      order              => '20',
      omit_empty_list    => true,
      final_empty_line   => false,
    }

    concat::fragment { 'named.conf.options-forward':
      target  => 'named.conf.options',
      order   => '21',
      content => "  forward ${forward};\n\n",
    }
  }

  unless empty($allow_query) {
    bind::aml { 'allow-query':
      address_match_list => $allow_query,
      target             => 'named.conf.options',
      order              => '30',
    }
  }

  unless empty($allow_query_cache) {
    bind::aml { 'allow-query-cache':
      address_match_list => $allow_query_cache,
      target             => 'named.conf.options',
      order              => '31',
    }
  }

  concat::fragment { 'named.conf.options-recursion':
    target  => 'named.conf.options',
    order   => '35',
    content => bool2str(empty($allow_recursion), '  recursion no;', '  recursion yes;'),
  }

  unless empty($allow_recursion) {
    bind::aml { 'allow-recursion':
      address_match_list => $allow_recursion,
      target             => 'named.conf.options',
      order              => '36',
      initial_empty_line => true,
      final_empty_line   => false,
    }
  }

  unless empty($blackhole) {
    bind::aml { 'blackhole':
      address_match_list => $blackhole,
      target             => 'named.conf.options',
      order              => '40',
      initial_empty_line => true,
      final_empty_line   => false,
    }
  }

  #
  # Controls
  #

  if $control_channels_enable {
    @concat::fragment { 'named.conf.controls-head':
      target  => 'named.conf.options',
      order   => '90',
      tag     => ['named.conf.controls',],
      content => "\ncontrols {\n",
    }

    @concat::fragment { 'named.conf.controls-tail':
      target  => 'named.conf.options',
      order   => '93',
      tag     => ['named.conf.controls',],
      content => "};\n",
    }
  }
  else {
    concat::fragment { 'named.conf.controls':
      target  => 'named.conf.options',
      order   => '90',
      content => "\n// Disable controls\ncontrols { };\n",
    }
  }

  #
  # Main Configuration
  #

  $config = {
    'confdir'                 => $confdir,
    'views_enable'            => $views_enable,
    'dnssec_policy_available' => $dnssec_policy_available,
  }

  bind::config { 'named.conf':
    content => epp("${module_name}/named.conf.epp", $config),
  }

  # Ensure named.conf.local exists and has correct permissions but do not
  # manage any future content.

  file { "${confdir}/named.conf.local":
    ensure  => file,
    owner   => 'root',
    group   => $bind_group,
    mode    => '0640',
    replace => false,
    source  => "puppet:///modules/${module_name}/named.conf.local",
    before  => Service['bind'],
  }

  #
  # Misc zone files
  #

  bind::config { 'db.root':
    source => $root_hints_source,
  }

  bind::config { 'db.localhost':
    source => $localhost_forward_source,
  }

  bind::config { 'db.127':
    source => $localhost_reverse_source,
  }

  bind::config { 'db.empty':
    source => "puppet:///modules/${module_name}/zones/db.empty",
  }

  unless $views_enable {
    if $root_hints_enable {
      bind::zone::hint { '.':
        file    => "${confdir}/db.root",
        comment => 'Prime server with knowledge of the root servers',
      }
    }

    if $root_mirror_enable {
      bind::zone::mirror { '.':
        comment => 'Local copy of the root zone (see RFC 7706)',
        order   => '11',
      }
    }

    if $localhost_forward_enable {
      bind::zone::primary { 'localhost':
        file  => "${confdir}/db.localhost",
        order => '15',
      }
    }

    if $localhost_reverse_enable {
      bind::zone::primary { '127.in-addr.arpa':
        file  => "${confdir}/db.127",
        order => '15',
      }
    }
  }

  #
  # Service
  #

  if ($ipv4_enable and $ipv6_enable) {
    $network_options = []
  }
  elsif $ipv4_enable {
    $network_options = ['-4']
  }
  elsif $ipv6_enable {
    $network_options = ['-6']
  }
  else {
    fail('One of ipv4_enable or ipv6_enable must be true')
  }

  # Start daemon as $bind_user if configured
  $user_options = $bind_user ? {
    undef   => [],
    default => ['-u', $bind_user,],
  }

  $daemon_options = join($user_options + $network_options, ' ')

  $options_file = "${facts['os']['name']}-${facts['os']['release']['major']}" ? {
    'Debian-10' => '/etc/default/bind9',
    default     => '/etc/default/named',
  }

  file_line { 'named-options':
    path    => $options_file,
    line    => "OPTIONS=\"${daemon_options}\"",
    match   => '^OPTIONS=',
    require => Package['bind'],
    notify  => Service['bind'],
  }

  service { 'bind':
    ensure  => $service_ensure,
    enable  => $service_enable,
    name    => $service_name,
    restart => "${rndc_program} reconfig",
  }
}