Have you ever been in a situation where you need to write multiple configuration files manually? I have been managing multiple router configurations. The routers were following the same pattern, but each one of them had to have different opened / filtered ports, Network Address Translation rules, different routes, etc.
This could be resolved by using a configuration file template and some scripting to push variable data into the template. While jinja templates and python scripts may be a fairly good solution, I was still struggling with two questions:
- Does a simpler and more powerful templating language for re-writing configurations exist out there?
- Is there a simple command line tool for templating?
As I could not find satisfactory answers to my questions, I decided to start this project called conftl - Configuration Templating Language. Let me give some examples how to use conftl.
Example 1: Open Network Ports on Firewall
I do not want to bother you with all the router details, so I will show only simple Linux iptables firewall example.
The template file firewall.tmpl is created with the following content:
# ... other iptables rules ...
{{for dport in dports:}}
-A INPUT -i eth0 -p tcp --dport {{=dport}} -m state --state NEW,ESTABLISHED -j ACCEPT
{{pass}}
# ... other iptables rules ...
What do we have in this template? We see that we can embed python code, wrapped with
{{...}}
So what this means? - everyone who has some idea of the Python syntax (and the Python syntax is a really clean one), could embed Python constructs into the template. Possibilities are endless: conditional expressions if - else, while and for loops, assigning values to variables, even function definitions are possible in template.
What else we see in there? The construct
{{=dport}}
is somehow weird. This is how we print a variable value to the output configuration file.
The other funny thing is the special keyword
{{pass}}
which indicates where our for loop block ends.
Generally we just templated one Python for loop, which prints to the output config file one iptables rule per each port in dports. Now we need a way to render the template and specify which network ports to open. This can be done using the command line utility 'render':
$ render -i firewall.tmpl -c "dports=[22, 80, 8080, 443]"
-i firewall.tmpl where to get the template from
-c "dports=[...]" gives the list with the ports to open
The output will be printed to the screen for debug:
# ... other iptables rules ...
-A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp --dport 8080 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
# ... other iptables rules ...
In real situation you would want to give an output file also:
$ render -i firewall.tmpl -o firewall.conf -c "dports=[22, 80, 8080, 443]"
-o firewall.conf where to write the configuration file
Example 2: Nginx Proxy
Imagine you need to dynamically configure your reverse proxy. The example template would be:
# ... other nginx configurations
<%for loc in locations:%>
<%server_port = locations[loc]%>
location /<%=loc%> {
rewrite ^/<%=loc%>(.*) /$1 break;
proxy_pass http://<%=server_port%>;
}
<%pass%>
# ... other nginx configurations
Here you do not see the
{{...}}
pattern. Nginx configuration file uses
{}
brackets for opening/closing the blocks, so we may use different delimiters
"<% %>"
for better visibility.
In this example we will not give the values of the 'locations' variable at the command line. I will create another text file containing the json context. Context means assigning values to the template variables. Lets create the file 'nginx.ctx':
{"locations": {"blog": "192.168.43.15:8081",
"mail": "192.168.43.15:8082"}}
After running the command line tool:
$ render -i nginx.tmpl -j nginx.ctx -d "<% %>"
-j tells render where to get json context from
-d "<% %>" instructs render for the delimiters we are currently using
we will receive the output:
# ... other nginx configurations
location /blog {
rewrite ^/blog(.*) /$1 break;
proxy_pass http://192.168.43.15:8081;
}
location /mail {
rewrite ^/mail(.*) /$1 break;
proxy_pass http://192.168.43.15:8082;
}
# ... other nginx configurations
Installation, Advanced Usage, More Examples
I presented two examples just for taste what conftl is. More information and advanced usage cases may be found at the project repository:
https://github.com/ttt-fifo/conftl
Conclusion
I was searching for a simple, but powerful solution to template my configuration files. I find delightful the possibility to embed Python code into the template. I also find it convenient to have a command line tool for rendering templates. I would be happy if other people also like this approach and start using conftl
Love to hear your opinion and comments.
Top comments (3)
I'm not sure I understand how is this different from jinja2 and why you are not using Ansible to manage your configuration files?
Hi Maxime,
Thanks for the comment.
Jinja is awesome, no doubt - and Ansible is using jinja for templating.
The difference from jinja is that you can have pure python embedded in templating, not some other syntax.
For example this is possible in template:
If you are interested - there is much more information in the README:
github.com/ttt-fifo/conftl
Second part of your question:
Ansible is more advanced system, which can connect remote servers, doing full configuration management.
As I said above: for configuration templating Ansible uses Jinja.
conftl is doing just the templating, without the other Ansible features.
My next project would be to connect conftl with Ansible - maybe by developing an action plugin, we will see...
Thank you for the comment!
Ok! Thanks for the explanation.
This jinja alternative looks quite promising, I'm interested in seeing it evolves.
Kudos 👍