From 235c9fdb2a26249a2095f596451a84e8b7930a5d Mon Sep 17 00:00:00 2001 From: mdivecky Date: Wed, 25 Oct 2023 19:06:28 +0200 Subject: [PATCH] basic config generator from prompt --- .gitignore | 5 ++ README.md | 1 + clusters.json.example | 19 +++++++ n-gen.py | 109 ++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 + templates/nginx-site.conf | 34 ++++++++++++ 6 files changed, 170 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 clusters.json.example create mode 100755 n-gen.py create mode 100644 requirements.txt create mode 100644 templates/nginx-site.conf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..74622dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.venv +*.pyc +__pycache__ +.vscode +clusters.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6119057 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Nginx configurator (patent for that name is pending...) \ No newline at end of file diff --git a/clusters.json.example b/clusters.json.example new file mode 100644 index 0000000..9bb55ad --- /dev/null +++ b/clusters.json.example @@ -0,0 +1,19 @@ +{ + "clusters":[ + { + "name":"dummy1", + "nodes": [ + "10.0.0.1", + "10.0.0.2", + "10.0.0.3" + ] + }, + { + "name":"dummy2", + "nodes": [ + "127.0.0.1", + "127.0.0.2" + ] + } + ] + } \ No newline at end of file diff --git a/n-gen.py b/n-gen.py new file mode 100755 index 0000000..f910d40 --- /dev/null +++ b/n-gen.py @@ -0,0 +1,109 @@ +import json +import pyinputplus as pyip + +# Get clusters from json config +with open("clusters.json") as json_file: + CLUSTERS = json.load(json_file)["clusters"] + +# Setup Jinja2 +from jinja2 import Environment, PackageLoader, select_autoescape +jin = Environment(loader=PackageLoader("n-gen"), autoescape=select_autoescape()) + +# ID of next config - TBD +CONF_ID = 1 + +def get_domains(): + new_domain = True + domains = [] + while new_domain: + domain = pyip.inputStr("Enter a full domain name: ") + domains.append(domain) + next_domain = pyip.inputYesNo("Do you want to add another domain? (y/n) ") + if next_domain == "no": + new_domain = False + return domains + + +def get_upstreams(clusters): + print("\nNow, we will select upstream server(s).") + if ( + pyip.inputYesNo( + "Is the service located on existing upstream cluster (like Swarm)? (y/n) " + ) + == "yes" + ): + cluster_list = [d["name"] for d in clusters] + sel_cluster_name = pyip.inputMenu(cluster_list, lettered=True, blank=True) + cluster = [ + element for element in clusters if element["name"] == sel_cluster_name + ][0] + print("Selected cluster " + cluster["name"] + " with nodes:") + for node in cluster["nodes"]: + print(node) + return cluster["nodes"] + else: + new_upstream = True + upstreams = [] + while new_upstream: + upstream = pyip.inputStr("Enter IPv4 address of one upstream server: ") + upstreams.append(upstream) + next_upstream = pyip.inputYesNo( + "Do you want to add another upstream server? (y/n) " + ) + if next_upstream == "no": + new_upstream = False + return upstreams + + +def get_port(): + return pyip.inputInt( + "\nEnter a port number for the upstream servers: ", min=81, max=65534 + ) + + +def get_proto(): + print("\nEnter the upstream protocol (between service and reverse proxy)") + return pyip.inputMenu(["http://", "https://"], lettered=True) + + +def input_check(domains, upstreams, port, proto): + print("\n-----------------------------------------------") + print("You have entered following service information:") + print("Domains:") + for domain in domains: + print("\t" + domain) + + print("Upstream servers with proto and port:") + for upstream in upstreams: + print("\t" + proto + upstream + ":" + str(port)) + + if pyip.inputYesNo("Is this information correct? (y/n) ") == "yes": + return True + else: + print("Sorry to hear that, please start again. Exiting") + exit() + + +def fill_template(id, domains, upstreams, port, proto): + template = jin.get_template("nginx-site.conf") + return template.render( + id=id, domains=domains, upstreams=upstreams, port=port, proto=proto + ) + + +def main(): + print("This script will generate nginx configuration and for new service.\n") + domains = get_domains() + upstreams = get_upstreams(CLUSTERS) + port = get_port() + proto = get_proto() + input_check(domains, upstreams, port, proto) + print(fill_template(CONF_ID, domains, upstreams, port, proto)) + + +# def test(): +# print(fill_template("1110", ['nolog.cz', 'www.nolog.cz'], ['10.0.0.1', '10.0.0.2'], 80, 'https://')) + +if __name__ == "__main__": + main() + # test() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..487d0c1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pyinputplus +Jinja2 \ No newline at end of file diff --git a/templates/nginx-site.conf b/templates/nginx-site.conf new file mode 100644 index 0000000..2451854 --- /dev/null +++ b/templates/nginx-site.conf @@ -0,0 +1,34 @@ +# ID: {{ id }} +# Service configured by nxa.py + +upstream up_{{ id }} { +{%- for upstream in upstreams %} + server {{ upstream }}:{{ port }}; +{%- endfor %} +} + +server { + server_name{% for domain in domains %} {{ domain }}{% endfor %}; # AUTOSSL > {{ id }} + + listen 80; + listen [::]:80; + + # ssl + include /etc/autossl/gen/{{ id }}.conf; + + # logging + include include/logging-nolog.conf; # Change to "logging-debug" if needed + + # gzip compression + include include/gzip.conf; + + # security headers + include include/security-headers.conf; + + # reverse proxy + location / { + proxy_pass {{ proto }}up_{{ id }}; + include include/proxy-headers.conf; + } + +} \ No newline at end of file