You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

init.py 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #! /usr/bin/env python3
  2. import argparse
  3. import docker
  4. import sys
  5. import re
  6. def get_zones(container):
  7. out = container.exec_run(['pdnsutil', 'list-all-zones'])
  8. if out is not None:
  9. out = out.decode('UTF-8') # bytres to string
  10. zones = out.split('\n') # convert to list, one zone per line
  11. zones = list(filter(None, zones)) # remove any empty lines
  12. zones = zones[0:len(zones) - 1] # remove last line 'All zonecount: xx'
  13. return zones
  14. return None
  15. def add_zone(container, zone):
  16. out = container.exec_run(['pdnsutil', 'create-zone', zone])
  17. if out is not None:
  18. out = out.decode('UTF-8')
  19. if out != 'Creating empty zone \'%s\'\n' % (zone): # might to strict, but as we can not get return code
  20. return False
  21. out = container.exec_run(['pdnsutil', 'set-kind', zone, 'master'])
  22. out = out.decode('UTF-8')
  23. return out == '';
  24. return False
  25. def get_tsig_keys(container):
  26. out = container.exec_run(['pdnsutil', 'list-tsig-keys'])
  27. if out is not None:
  28. out = out.decode('UTF-8') # bytres to string
  29. keys = out.split('\n') # convert to list, one key per line
  30. keys = list(filter(None, keys)) # remove any empty lines
  31. keys = {"%s|%s" % (key[0], key[1]): key for key in [key.split(' ') for key in keys]}
  32. return keys
  33. return None
  34. def add_tsig_key(container, key_name, key_algo):
  35. out = container.exec_run(['pdnsutil', 'generate-tsig-key', key_name, key_algo])
  36. if out is not None:
  37. out = out.decode('UTF-8')
  38. match = re.search('Create new TSIG key %s %s (.+)' % (re.escape(key_name), re.escape(key_algo)), out)
  39. if match:
  40. key = match.group(1)
  41. return key
  42. else:
  43. return None
  44. return None
  45. def add_tsig_key_to_zone(container, zone, key_name):
  46. out = container.exec_run(['pdnsutil', 'set-meta', zone, 'TSIG-ALLOW-DNSUPDATE', key_name])
  47. if out is not None:
  48. out = out.decode('UTF-8')
  49. return out == 'Set \'%s\' meta TSIG-ALLOW-DNSUPDATE = %s\n' % (zone, key_name) # might to strict, but as we can not get return code
  50. return False
  51. def main():
  52. parser = argparse.ArgumentParser(description='Init Powerdns server')
  53. parser.add_argument('--dhcp-zone', dest='dhcp_zone', default=None, help='DHCP zone to create (eg: dhcp.city-a.example.com)')
  54. parser.add_argument('--site-zone', dest='site_zone', default=None, help='Site zone to create (eg: city-a.example.com)')
  55. parser.add_argument('--rev-zone', dest='rev_zone', default=None, help='Reverse zone to create (eg: 100.15.10), arpa suffix will be added automatically')
  56. parser.add_argument('--tsig-key-name', dest='tsig_key_name', default='dhcpupdate', help='TSIG key name to add (eg: dnsupdate)')
  57. parser.add_argument('--tsig-key-algo', dest='tsig_key_algo', default='hmac-md5', help='TSIG key algorithm to use (eg: hmac-md5)')
  58. parser.add_argument('--container-pdns', dest='container_pdns', default='pdns-pdns', help='Name of pdns auth docker container (eg: pdns-pdns)')
  59. args = parser.parse_args()
  60. if args.dhcp_zone is None and args.site_zone is None:
  61. print('At least one of dhcp-zone or site-zone is required')
  62. return 64
  63. if args.rev_zone is not None and not args.rev_zone.endswith('.in-addr.arpa'):
  64. args.rev_zone = args.rev_zone + '.in-addr.arpa'
  65. print('Getting docker client instance...')
  66. try:
  67. client = docker.DockerClient(version='auto')
  68. client.ping()
  69. except Exception as e:
  70. print('Failed to ping docker server: %s' % (e))
  71. return 1
  72. print('Getting docker container %s...' % (args.container_pdns))
  73. try:
  74. container = client.containers.get(args.container_pdns)
  75. except Exception as e:
  76. print('Can not find container %s' % (e))
  77. return 2
  78. if container.status != 'running':
  79. print('Container is not running: %s' % (container.status))
  80. return 3
  81. print('Checking existing zones...')
  82. zones = get_zones(container)
  83. if zones is None:
  84. print('Failed to get existing zones: Unknown error')
  85. return 4
  86. print('Checking existing TSIG keys...')
  87. keys = get_tsig_keys(container)
  88. if keys is None:
  89. print('Failed to get existing TSIG keys: Unknown error')
  90. return 5
  91. if '%s.|%s.' % (args.tsig_key_name, args.tsig_key_algo) in keys:
  92. print('Not adding TSIG key %s %s: already exists' % (args.tsig_key_name, args.tsig_key_algo))
  93. tsig_key_secret = keys['%s.|%s.' % (args.tsig_key_name, args.tsig_key_algo)][2]
  94. else:
  95. print('Adding TSIG key %s %s...' % (args.tsig_key_name, args.tsig_key_algo))
  96. tsig_key_secret = add_tsig_key(container, args.tsig_key_name, args.tsig_key_algo)
  97. if tsig_key_secret is None:
  98. print('Failed to add TSIG key: Unknown error')
  99. return 6
  100. for zone in [args.dhcp_zone, args.site_zone, args.rev_zone]:
  101. if zone is not None:
  102. if zone in zones:
  103. print('Not adding zone %s: already exists' % (zone))
  104. else:
  105. print('Adding zone...')
  106. if not add_zone(container, zone):
  107. print('Failed to add zone: Unknown error')
  108. return 7
  109. print('Adding TSIG %s to zone %s...' % (args.tsig_key_name, zone))
  110. if not add_tsig_key_to_zone(container, zone, args.tsig_key_name):
  111. print('Failed to add TSIG key to zone: Unknown error')
  112. return 8
  113. print('DHCP_TSIG_KEY_NAME=%s' % (args.tsig_key_name))
  114. print('DHCP_TSIG_KEY_ALGO=%s' % (args.tsig_key_algo))
  115. print('DHCP_TSIG_KEY_SECRET=%s' % (tsig_key_secret))
  116. return 0
  117. if __name__ == '__main__':
  118. sys.exit(main())