tsig-keygen.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/env python
  2. "Pure Python TSIG key generator with multiple output formats."
  3. import os, base64, argparse, jinja2
  4. algorithm_bytes = dict(("hmac-sha{}".format(bits), bits // 8) for bits in (256, 384, 512))
  5. algorithm_choices = tuple(sorted(algorithm_bytes))
  6. templates = {}
  7. templates["bind9"] = '''
  8. key {{ args.name }} {
  9. algorithm {{ args.algorithm }};
  10. secret "{{ args.key }}";
  11. };
  12. {% if args.servers %}
  13. {%- for server in args.servers -%}
  14. server {{ server }} { keys { {{ args.name }}; }; };
  15. {% endfor -%}
  16. {% for zone in args.zones %}
  17. zone "{{ zone }}" {
  18. file "{{args.directory }}/{{ zone }}";
  19. type slave;
  20. masters { {%- for server in args.servers %} {{ server }}; {% endfor %} };
  21. };
  22. {% endfor %}
  23. {% endif %}
  24. '''
  25. templates["nsd"] = '''
  26. key:
  27. name: "{{ args.name }}"
  28. algorithm: {{ args.algorithm }}
  29. secret: "{{ args.key }}"
  30. {% if args.servers -%}
  31. pattern:
  32. name: "secondary.{{ args.name }}"
  33. zonefile: "{{ args.directory }}/%s"
  34. {%- for server in args.servers %}
  35. allow-notify: {{ server }} {{ args.name }}
  36. request-xfr: {{ server }} {{ args.name }}
  37. {%- endfor %}
  38. {%- for zone in args.zones %}
  39. zone:
  40. name: "{{ zone }}"
  41. include-pattern: "secondary.{{ args.name }}"
  42. {%- endfor %}
  43. {% endif %}
  44. '''
  45. templates["knot"] = '''
  46. ### WARNING: KNOT CONFIGURATION NOT YET TESTED ###
  47. key:
  48. - id: {{ args.name }}
  49. algorithm: {{ args.algorithm }}
  50. secret: {{ args.key }}
  51. {% if args.servers -%}
  52. remote:
  53. - id: {{ args.name }}
  54. key: {{ args.name }}
  55. {%- for server in args.servers %}
  56. address: {{ server }}@53
  57. {%- endfor %}
  58. acl:
  59. - id: notify_from_{{ args.name }}
  60. action: notify
  61. key: {{ args.name }}
  62. {%- for server in args.servers %}
  63. address: {{ server }}@53
  64. {%- endfor %}
  65. {# #}
  66. {%- for zone in args.zones %}
  67. zone:
  68. - domain: {{ zone }}
  69. storage: {{ args.directory }}/{{ zone }}
  70. master: {{ args.name }}
  71. acl: notify_from_{{ args.name }}
  72. {% endfor %}
  73. {% endif %}
  74. '''
  75. ap = argparse.ArgumentParser(description = __doc__)
  76. ap.add_argument("-a", "--algorithm", choices = algorithm_choices, default = algorithm_choices[0],
  77. help = "TSIG algorithm (default: " + algorithm_choices[0] + ")")
  78. ap.add_argument("-d", "--directory", default = "secondary", help = "where to store secondary zone files")
  79. ap.add_argument("-f", "--format", choices = tuple(templates), default = "nsd", help = "output format (default: nsd)")
  80. ap.add_argument("-k", "--key", help = "TSIG shared secret (default: generate new secret)")
  81. ap.add_argument("-n", "--name", default = "tsig.example.org", help = "DNS name for TSIG shared secret")
  82. ap.add_argument("-o", "--output", default = "-", type = argparse.FileType("w"), help = "output file")
  83. ap.add_argument("-s", "--servers", nargs = "+", default = [], metavar = "SERVER", help = "address(es) of primary nameserver(s)")
  84. ap.add_argument("-z", "--zones", nargs = "+", default = [], metavar = "ZONE", help = "zone(s) to xfr from specified nameserver(s)")
  85. args = ap.parse_args()
  86. if args.key is None:
  87. args.key = base64.b64encode(os.urandom(algorithm_bytes[args.algorithm])).decode("ascii")
  88. args.output.write(jinja2.Template(templates[args.format]).render(args = args))