1
0
mirror of https://github.com/oskar456/dzonegit.git synced 2024-05-11 05:55:41 +00:00

13 Commits
v0.1 ... v0.4

Author SHA1 Message Date
Ondřej Caletka
08e2ea93fb version 0.4 2018-08-15 14:04:05 +02:00
Ondřej Caletka
45046429d1 Update docs 2018-08-15 14:03:15 +02:00
Ondřej Caletka
293bf930a1 Better multi-call support
Also supports calling by secondary argument (kind-of busybox
stylekind-of). Drop support for multicall setuptools wrapper – when
installed using setuptools, the user should call explicit function
instead.
2018-08-15 13:32:17 +02:00
Ondřej Caletka
c9026ff21b Revert "Drop dzonegit multi-call executable"
This reverts commit 9a521350d3.
Since there is no Python dependency, it may be actually handy to call
the script as is.
2018-08-15 13:17:33 +02:00
Ondřej Caletka
b608c25372 Revert "Add Experimental filters for smudging/cleaning the SOA serial"
This reverts commit 331df2a4ec.
Increasing serial during the cleaning phase does not work as expected.
Maybe the way is to smudge serial to current unix time during checkout
on the server.
2018-08-15 13:08:08 +02:00
Ondřej Caletka
27ae5ff210 version 0.3 2018-08-10 15:49:29 +02:00
Ondřej Caletka
331df2a4ec Add Experimental filters for smudging/cleaning the SOA serial 2018-08-10 15:48:02 +02:00
Ondřej Caletka
1f79f52b1a Allow wildcards in zone blacklists and whitelists 2018-08-10 12:51:21 +02:00
Ondřej Caletka
9a521350d3 Drop dzonegit multi-call executable 2018-08-10 12:30:51 +02:00
Ondřej Caletka
5988fd005e version 0.2 2018-07-23 14:05:25 +02:00
Ondřej Caletka
7e6376ffb2 Add template wildcard matches 2018-07-23 14:04:46 +02:00
Ondřej Caletka
c3a181be14 Fix Knot DNS template example 2018-07-19 16:38:48 +02:00
Ondřej Caletka
6f23c066bc Add Travis CI config 2018-07-19 10:50:06 +02:00
5 changed files with 129 additions and 44 deletions

13
.travis.yml Normal file
View File

@@ -0,0 +1,13 @@
before_install:
- sudo apt-get install -y bind9utils
language: python
python:
- "3.5"
- "3.6"
- "nightly"
install:
- pip install -e .
- pip install pytest
script:
- pytest
sudo: false

View File

@@ -17,36 +17,47 @@ Main features
- enforce updating serial number when zone content is changed
- both ``pre-commit`` and ``pre-receive``/``update`` hooks to enforce similar checks in the remote repository
- ``post-receive`` hook to checkout the working copy from a bare repository, generate config snippets for various DNS server software and reload them
- only Python standard library is used
- only Python 3.5+ standard library is used
Requirements
------------
- Python 3.5+
- `named-compilezone(8)`_ (part of BIND9 package)
- `named-compilezone(8)`_ (part of `bind9utils` package)
- git
Instalation and usage
---------------------
Simple instalation (especially for workstations)
------------------------------------------------
- install required dependencies
- install ``dzonegit`` package using your favourite tool (``virtualenvwrapper``,
``venv``, ``pipenv``, etc.)
Since there is no other Python dependency than the standard library, you can
simply download the `dzonegit.py` file, make it executable and rename/symlink
it to an appropriate hook location inside the Git repository
`.git/hooks/pre-commit`. This is especially handy for the end users not
experienced with Python packaging ecosystem.
Full instalation and usage
--------------------------
- install all the requirements
- install ``dzonegit`` Python package using your
favourite tool (``virtualenvwrapper``, ``venv``, ``pipenv``, etc.)
- in the local repository, create a symlink for the ``pre-commit`` hook:
``$ ln -s $(which dzonegit-pre-commit) /path/to/repo/.git/hooks/pre-commit``
- on the server, install some git repository management software, preferrably Gitolite_
- on the server, install either ``pre-receive`` or ``update`` hook (both do the same) as
well as ``post-receive`` hook. See `Gitolite documentation on how to add custom hooks`_
- on the server set up the configuration options for each repository
- on the server, install some git repository management software,
preferably Gitolite_
- on the server, install either ``pre-receive`` or ``update`` hook
(both do the same) as well as the ``post-receive`` hook. See `Gitolite
documentation on how to add custom hooks`_
- on the server, set up the configuration options for each repository
Configuration options
---------------------
All configuration options are stored in `git-config(1)`_ in section named ``dzonegit``.
All boolean options default to *False*.
All configuration options are stored in `git-config(1)`_ in the section
named ``dzonegit``. All boolean options default to *False*.
*dzonegit.ignorewhitespaceerrors*
@@ -76,7 +87,7 @@ All boolean options default to *False*.
can be provided by appending single digit from 1 to 9 to this option.
*dzonegit.zonereloadcmd*
A command to run for each zone, whose zone file has been modified. Zone
A command to run for each zone, where zone file has been modified. Zone
name is automatically appended as the last argument. Should do something
like ``rndc reload``. More commands can be provided by appending single digit
from 1 to 9 to this option.
@@ -84,20 +95,27 @@ All boolean options default to *False*.
*dzonegit.zoneblacklist*
Path to a text file containing list of zone names without trailing dots,
one per line. If zone is found on the blacklist, it is ignored when
``post-receive`` hook generates configuration.
``post-receive`` hook generates configuration. Wildcards can be used as
well, see `JSON template`_ below.
*dzonegit.zonewhitelist*
Path to a text file containing list of zone names without trailing dots,
one per line. If not empty and zone is not found on the whitelist,
it is ignored when ``post-receive`` hook generates configuration.
it is ignored when ``post-receive`` hook generates configuration. Wildcards
can be used as well, see `JSON template`_ below.
JSON template
-------------
The DNS server configuration snippets are generated using a simple JSON-based
template. All keys are optional but please make sure the file is a valid
JSON file. It is possible to define a zone-specific options, for instance for
changing DNSSEC parameters per zone.
template. All keys are optional but please make sure the file is a valid JSON
file. It is possible to define a zone-specific options, for instance for
changing DNSSEC parameters per zone. Those zone-specific options allow usage of
wildcards; if an exact match of zone name is not found, the leftmost label is
substituted with `*`. If still no match is found, the leftmost label is dropped
and the second one is again substituted with `*`. In the end, a single `*` is
checked. Only if even this key is not found, the value of *defaultvar* is used
as the zone-specific option.
Valid keys are:
@@ -112,19 +130,22 @@ Valid keys are:
*defaultvar*
A string that would template variable ``$zonevar`` expand to if there is not
a zone-specific variable defined.
a zone-specific variable defined, nor any wildcard matched.
*zonevars*
An object mapping zone names (without the final dot) to a zone-specific
variable to which template variable ``$zonevar`` would expand to.
variable to which template variable ``$zonevar`` would expand to. Using
wildcards is possible by replacing the leftmost label with `*`. Ultimately,
a key with label `*` will match every single zone (making *defaultvar*
option litte bit pointless)
In the template strings, these placeholders are supported:
``$datetime``
Current timestamp
Current date and time in human readable format
``$zonename``
Zone name, without trailing dot
Zone name, without the trailing dot
``$zonefile``
Full path to the zone file
@@ -138,12 +159,14 @@ Example JSON template for Knot DNS
.. code-block:: json
{
"header": "# Managed by dzonegit, do not edit.\n",
"header": "# Managed by dzonegit, do not edit.\nzone:",
"footer": "",
"item": " - zone: \"$zonename\"\n file: \"$zonefile\"\n $zonevar\n",
"item": " - domain: \"$zonename\"\n file: \"$zonefile\"\n $zonevar\n",
"defaultvar": "template: default",
"zonevars": {
"example.com": "template: signed"
"example.com": "template: signed",
"*.cz": "template: czdomains",
"*.in-addr.arpa": "template: ipv4reverse"
}
}

View File

@@ -268,6 +268,23 @@ def replace_serial(path, oldserial, newserial):
path.write_text(updated)
def get_zone_wildcards(name):
""" A generator of wildcards out of a zone name.
For a DNS name, returns series of:
- the name itself
- the name with first label substitued as *
- the name with first label dropped and second substittuted as *
- ...
- single *
"""
yield name
labels = name.split(".")
while labels:
labels[0] = "*"
yield ".".join(labels)
labels.pop(0)
def template_config(checkoutpath, template, blacklist=set(), whitelist=set()):
""" Recursively find all *.zone files and template config file using
a simple JSON based template like this:
@@ -278,7 +295,9 @@ def template_config(checkoutpath, template, blacklist=set(), whitelist=set()):
"item": " - zone: \"$zonename\"\n file: \"$zonefile\"\n $zonevar\n",
"defaultvar": "template: default",
"zonevars": {
"example.com": "template: signed"
"example.com": "template: signed",
"*.com": "template: dotcom",
"*": "template: uberdefault"
}
}
@@ -301,13 +320,16 @@ def template_config(checkoutpath, template, blacklist=set(), whitelist=set()):
out.append(headertpl.substitute(mapping))
for f in sorted(Path(checkoutpath).glob("**/*.zone")):
zonename = get_zone_name(f, f.read_bytes())
if whitelist and zonename not in whitelist:
if whitelist and not any(
n in whitelist
for n in get_zone_wildcards(zonename)
):
print(
"WARNING: Ignoring zone {} - not whitelisted for "
"this repository.".format(zonename),
)
continue
if zonename in blacklist:
if any(n in blacklist for n in get_zone_wildcards(zonename)):
print(
"WARNING: Ignoring zone {} - blacklisted for "
"this repository.".format(zonename),
@@ -323,7 +345,12 @@ def template_config(checkoutpath, template, blacklist=set(), whitelist=set()):
)
continue
zones[zonename] = f.relative_to(checkoutpath)
zonevar = zonevars[zonename] if zonename in zonevars else defaultvar
for name in get_zone_wildcards(zonename):
if name in zonevars:
zonevar = zonevars[name]
break
else:
zonevar = defaultvar
out.append(itemtpl.substitute(
mapping, zonename=zonename,
zonefile=str(f), zonevar=zonevar,
@@ -468,17 +495,25 @@ def post_receive(stdin=sys.stdin):
subprocess.run(cmd)
def get_action(argv=sys.argv):
name = Path(argv[0]).name
if "pre-commit" in name:
return pre_commit
if "update" in name:
return update
if "pre-receive" in name:
return pre_receive
if "post-receive" in name:
return post_receive
def main():
name = Path(sys.argv[0]).name
print(name)
if name == "pre-commit":
pre_commit()
elif name == "update":
update()
elif name == "pre-receive":
pre_receive()
elif name == "post-receive":
post_receive()
action = get_action()
if action is None and len(sys.argv) > 1:
sys.argv.pop(0)
action = get_action()
if action:
action()
else:
sys.exit("No valid command found")

View File

@@ -5,7 +5,7 @@ readme = Path(__file__).with_name("README.rst").read_text()
setup(
name="dzonegit",
version="0.1",
version="0.4",
description="Git hooks to manage a repository of DNS zones",
long_description=readme,
long_description_content_type="text/x-rst",
@@ -19,7 +19,6 @@ setup(
tests_require=["pytest"],
entry_points={
"console_scripts": [
"dzonegit = dzonegit:main",
"dzonegit-pre-commit = dzonegit:pre_commit",
"dzonegit-pre-receive = dzonegit:pre_receive",
"dzonegit-post-receive = dzonegit:post_receive",

View File

@@ -298,12 +298,14 @@ def test_template_config(git_dir):
"item": " - zone: \"$zonename\"\n file: \"$zonefile\"\n $zonevar\n",
"defaultvar": "template: default",
"zonevars": {
"example.com": "template: signed"
"example.com": "template: signed",
"*": "template: dummy"
}
}"""
output = dzonegit.template_config(str(git_dir), template)
assert output.startswith("# Managed by dzonegit")
assert " - zone: \"dummy\"\n file: \"" in output
assert " template: dummy" in output
assert output.endswith("# This is the end")
output = dzonegit.template_config(
str(git_dir),
@@ -311,9 +313,22 @@ def test_template_config(git_dir):
whitelist=set("a"),
)
assert " - zone: \"dummy\"\n file: \"" not in output
output = dzonegit.template_config(
str(git_dir),
template,
blacklist=set("*"),
)
assert " - zone: \"dummy\"\n file: \"" not in output
def test_load_set_file(git_dir):
git_dir.join("dummy").write("dummy\n\n # Comment")
s = dzonegit.load_set_file("dummy")
assert s == {"dummy"}
def test_get_zone_wildcards():
assert list(dzonegit.get_zone_wildcards("a.long.zone.name")) == [
"a.long.zone.name", "*.long.zone.name",
"*.zone.name", "*.name", "*",
]