ui - fix example, subpath behind reverse proxy and add socket proxy rights for swarm
This commit is contained in:
parent
0c1883472d
commit
dd7d1a2c78
@ -16,7 +16,7 @@ services:
|
|||||||
- ./letsencrypt:/etc/letsencrypt
|
- ./letsencrypt:/etc/letsencrypt
|
||||||
- autoconf:/etc/nginx
|
- autoconf:/etc/nginx
|
||||||
environment:
|
environment:
|
||||||
- SERVER_NAME=admin.example.com # replace with your domain
|
- SERVER_NAME=admin.example.com # replace with your domain
|
||||||
- MULTISITE=yes
|
- MULTISITE=yes
|
||||||
- AUTO_LETS_ENCRYPT=yes
|
- AUTO_LETS_ENCRYPT=yes
|
||||||
- REDIRECT_HTTP_TO_HTTPS=yes
|
- REDIRECT_HTTP_TO_HTTPS=yes
|
||||||
@ -24,11 +24,12 @@ services:
|
|||||||
- USE_CLIENT_CACHE=yes
|
- USE_CLIENT_CACHE=yes
|
||||||
- USE_GZIP=yes
|
- USE_GZIP=yes
|
||||||
- USE_API=yes
|
- USE_API=yes
|
||||||
- API_URI=/ChangeMeToSomethingHardToGuess # change it to something hard to guess + must match API_URI for myui service
|
- API_URI=/ChangeMeToSomethingHardToGuess # change it to something hard to guess + must match API_URI for myui service
|
||||||
- admin.example.com_SERVE_FILES=no
|
- admin.example.com_SERVE_FILES=no
|
||||||
- admin.example.com_USE_REVERSE_PROXY=yes
|
- admin.example.com_USE_REVERSE_PROXY=yes
|
||||||
- admin.example.com_REVERSE_PROXY_URL=/admin/ # change it to something hard to guess
|
- admin.example.com_REVERSE_PROXY_URL=/admin/ # change it to something hard to guess
|
||||||
- admin.example.com_REVERSE_PROXY_HOST=http://myui:5000/
|
- admin.example.com_REVERSE_PROXY_HOST=http://myui:5000/
|
||||||
|
- admin.example.com_REVERSE_PROXY_HEADERS=X-Script-Name /admin # must match REVERSE_PROXY_URL
|
||||||
- admin.example.com_USE_MODSECURITY=no
|
- admin.example.com_USE_MODSECURITY=no
|
||||||
labels:
|
labels:
|
||||||
- "bunkerized-nginx.UI"
|
- "bunkerized-nginx.UI"
|
||||||
@ -41,11 +42,11 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- autoconf:/etc/nginx
|
- autoconf:/etc/nginx
|
||||||
environment:
|
environment:
|
||||||
- ABSOLUTE_URI=https://admin.example.com/admin/ # change it to your full URI
|
- ABSOLUTE_URI=https://admin.example.com/admin/ # change it to your full URI
|
||||||
- DOCKER_HOST=tcp://myuiproxy:2375
|
- DOCKER_HOST=tcp://myuiproxy:2375
|
||||||
- API_URI=/ChangeMeToSomethingHardToGuess
|
- API_URI=/ChangeMeToSomethingHardToGuess
|
||||||
- ADMIN_USERNAME=admin # change it to something hard to guess
|
- ADMIN_USERNAME=admin # change it to something hard to guess
|
||||||
- ADMIN_PASSWORD=changeme # change it to a good password
|
- ADMIN_PASSWORD=changeme # change it to a good password
|
||||||
|
|
||||||
myuiproxy:
|
myuiproxy:
|
||||||
image: tecnativa/docker-socket-proxy
|
image: tecnativa/docker-socket-proxy
|
||||||
@ -54,6 +55,8 @@ services:
|
|||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
environment:
|
environment:
|
||||||
- CONTAINERS=1
|
- CONTAINERS=1
|
||||||
|
- SWARM=1
|
||||||
|
- SERVICES=1
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
autoconf:
|
autoconf:
|
||||||
|
|||||||
@ -7,12 +7,14 @@ from flask_wtf.csrf import CSRFProtect, CSRFError
|
|||||||
from src.Instances import Instances
|
from src.Instances import Instances
|
||||||
from src.User import User
|
from src.User import User
|
||||||
from src.Config import Config
|
from src.Config import Config
|
||||||
|
from src.ReverseProxied import ReverseProxied
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
import os, json, re, copy, traceback
|
import os, json, re, copy, traceback
|
||||||
|
|
||||||
# Flask app
|
# Flask app
|
||||||
app = Flask(__name__, static_url_path="/", static_folder="static", template_folder="templates")
|
app = Flask(__name__, static_url_path="/", static_folder="static", template_folder="templates")
|
||||||
|
app.wsgi_app = ReverseProxied(app.wsgi_app)
|
||||||
|
|
||||||
# Set variables and instantiate objects
|
# Set variables and instantiate objects
|
||||||
vars = utils.get_variables()
|
vars = utils.get_variables()
|
||||||
@ -50,7 +52,7 @@ def login() :
|
|||||||
if request.method == "POST" and "username" in request.form and "password" in request.form :
|
if request.method == "POST" and "username" in request.form and "password" in request.form :
|
||||||
if app.config["USER"].get_id() == request.form["username"] and app.config["USER"].check_password(request.form["password"]) :
|
if app.config["USER"].get_id() == request.form["username"] and app.config["USER"].check_password(request.form["password"]) :
|
||||||
login_user(app.config["USER"])
|
login_user(app.config["USER"])
|
||||||
return redirect("/")
|
return redirect(app.config["ABSOLUTE_URI"])
|
||||||
else :
|
else :
|
||||||
fail = True
|
fail = True
|
||||||
if fail :
|
if fail :
|
||||||
@ -61,7 +63,7 @@ def login() :
|
|||||||
@login_required
|
@login_required
|
||||||
def logout() :
|
def logout() :
|
||||||
logout_user()
|
logout_user()
|
||||||
return redirect("/login")
|
return redirect(app.config["ABSOLUTE_URI"] + "/login")
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
@app.route('/home')
|
@app.route('/home')
|
||||||
|
|||||||
208
ui/src/Config.py
208
ui/src/Config.py
@ -2,119 +2,119 @@ import json, uuid, glob, copy, re, subprocess, os
|
|||||||
|
|
||||||
class Config :
|
class Config :
|
||||||
|
|
||||||
def __init__(self) :
|
def __init__(self) :
|
||||||
with open("/opt/bunkerized-nginx/settings.json", "r") as f :
|
with open("/opt/bunkerized-nginx/settings.json", "r") as f :
|
||||||
self.__settings = json.loads(f.read())
|
self.__settings = json.loads(f.read())
|
||||||
|
|
||||||
def __env_to_dict(self, filename) :
|
def __env_to_dict(self, filename) :
|
||||||
if not os.path.isfile(filename) :
|
if not os.path.isfile(filename) :
|
||||||
return {}
|
return {}
|
||||||
with open(filename, "r") as f :
|
with open(filename, "r") as f :
|
||||||
env = f.read()
|
env = f.read()
|
||||||
data = {}
|
data = {}
|
||||||
for line in env.split("\n") :
|
for line in env.split("\n") :
|
||||||
var = line.split("=")[0]
|
var = line.split("=")[0]
|
||||||
val = line.replace(var + "=", "", 1)
|
val = line.replace(var + "=", "", 1)
|
||||||
data[var] = val
|
data[var] = val
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def __dict_to_env(self, filename, variables) :
|
def __dict_to_env(self, filename, variables) :
|
||||||
env = ""
|
env = ""
|
||||||
for k, v in variables.items() :
|
for k, v in variables.items() :
|
||||||
env += k + "=" + v + "\n"
|
env += k + "=" + v + "\n"
|
||||||
with open(filename, "w") as f :
|
with open(filename, "w") as f :
|
||||||
f.write(env)
|
f.write(env)
|
||||||
|
|
||||||
def __gen_conf(self, global_conf, services_conf) :
|
def __gen_conf(self, global_conf, services_conf) :
|
||||||
conf = copy.deepcopy(global_conf)
|
conf = copy.deepcopy(global_conf)
|
||||||
if not "SERVER_NAME" in conf :
|
if not "SERVER_NAME" in conf :
|
||||||
conf["SERVER_NAME"] = ""
|
conf["SERVER_NAME"] = ""
|
||||||
servers = conf["SERVER_NAME"].split(" ")
|
servers = conf["SERVER_NAME"].split(" ")
|
||||||
if conf["SERVER_NAME"] == "" :
|
if conf["SERVER_NAME"] == "" :
|
||||||
servers = []
|
servers = []
|
||||||
for service in services_conf :
|
for service in services_conf :
|
||||||
first_server = service["SERVER_NAME"].split(" ")[0]
|
first_server = service["SERVER_NAME"].split(" ")[0]
|
||||||
if not first_server in servers :
|
if not first_server in servers :
|
||||||
servers.append(first_server)
|
servers.append(first_server)
|
||||||
for k, v in service.items() :
|
for k, v in service.items() :
|
||||||
conf[first_server + "_" + k] = v
|
conf[first_server + "_" + k] = v
|
||||||
conf["SERVER_NAME"] = " ".join(servers)
|
conf["SERVER_NAME"] = " ".join(servers)
|
||||||
env_file = "/tmp/" + str(uuid.uuid4()) + ".env"
|
env_file = "/tmp/" + str(uuid.uuid4()) + ".env"
|
||||||
self.__dict_to_env(env_file, conf)
|
self.__dict_to_env(env_file, conf)
|
||||||
proc = subprocess.run(["/opt/bunkerized-nginx/gen/main.py", "--settings", "/opt/bunkerized-nginx/settings.json", "--templates", "/opt/bunkerized-nginx/confs", "--output", "/etc/nginx", "--variables", env_file], capture_output=True)
|
proc = subprocess.run(["/opt/bunkerized-nginx/gen/main.py", "--settings", "/opt/bunkerized-nginx/settings.json", "--templates", "/opt/bunkerized-nginx/confs", "--output", "/etc/nginx", "--variables", env_file], capture_output=True)
|
||||||
stderr = proc.stderr.decode("ascii")
|
stderr = proc.stderr.decode("ascii")
|
||||||
stdout = proc.stdout.decode("ascii")
|
stdout = proc.stdout.decode("ascii")
|
||||||
if stderr != "" or proc.returncode != 0 :
|
if stderr != "" or proc.returncode != 0 :
|
||||||
raise Exception("Error from generator (return code = " + str(proc.returncode) + ") : " + stderr + "\n" + stdout)
|
raise Exception("Error from generator (return code = " + str(proc.returncode) + ") : " + stderr + "\n" + stdout)
|
||||||
|
|
||||||
def get_settings(self) :
|
def get_settings(self) :
|
||||||
return self.__settings
|
return self.__settings
|
||||||
|
|
||||||
def get_config(self) :
|
def get_config(self) :
|
||||||
return self.__env_to_dict("/etc/nginx/global.env")
|
return self.__env_to_dict("/etc/nginx/global.env")
|
||||||
|
|
||||||
def get_services(self) :
|
def get_services(self) :
|
||||||
services = []
|
services = []
|
||||||
for filename in glob.iglob("/etc/nginx/**/site.env") :
|
for filename in glob.iglob("/etc/nginx/**/site.env") :
|
||||||
env = self.__env_to_dict(filename)
|
env = self.__env_to_dict(filename)
|
||||||
services.append(env)
|
services.append(env)
|
||||||
no_multisite = self.__env_to_dict("/etc/nginx/site.env")
|
no_multisite = self.__env_to_dict("/etc/nginx/site.env")
|
||||||
if len(no_multisite) > 0 :
|
if len(no_multisite) > 0 :
|
||||||
services.append(no_multisite)
|
services.append(no_multisite)
|
||||||
return services
|
return services
|
||||||
|
|
||||||
def check_variables(self, variables) :
|
def check_variables(self, variables) :
|
||||||
for k, v in variables.items() :
|
for k, v in variables.items() :
|
||||||
check = False
|
check = False
|
||||||
for category in self.__settings :
|
for category in self.__settings :
|
||||||
for param in self.__settings[category]["params"] :
|
for param in self.__settings[category]["params"] :
|
||||||
multiple = False
|
multiple = False
|
||||||
if param["type"] != "multiple" :
|
if param["type"] != "multiple" :
|
||||||
real_params = [param]
|
real_params = [param]
|
||||||
else :
|
else :
|
||||||
real_params = param["params"]
|
real_params = param["params"]
|
||||||
multiple = True
|
multiple = True
|
||||||
for real_param in real_params :
|
for real_param in real_params :
|
||||||
if (((not multiple and k == real_param["env"]) or
|
if (((not multiple and k == real_param["env"]) or
|
||||||
(multiple and re.search("^" + real_param["env"] + "_" + "[0-9]+$", k))) and
|
(multiple and re.search("^" + real_param["env"] + "_" + "[0-9]+$", k))) and
|
||||||
real_param["context"] == "multisite" and
|
real_param["context"] == "multisite" and
|
||||||
re.search(real_param["regex"], v)) :
|
re.search(real_param["regex"], v)) :
|
||||||
check = True
|
check = True
|
||||||
if not check :
|
if not check :
|
||||||
raise Exception("Variable " + k + " is not valid.")
|
raise Exception("Variable " + k + " is not valid.")
|
||||||
|
|
||||||
def new_service(self, variables) :
|
def new_service(self, variables) :
|
||||||
global_env = self.__env_to_dict("/etc/nginx/global.env")
|
global_env = self.__env_to_dict("/etc/nginx/global.env")
|
||||||
services = self.get_services()
|
services = self.get_services()
|
||||||
for service in services :
|
for service in services :
|
||||||
if service["SERVER_NAME"] == variables["SERVER_NAME"] or service["SERVER_NAME"] in variables["SERVER_NAME"].split(" ") :
|
if service["SERVER_NAME"] == variables["SERVER_NAME"] or service["SERVER_NAME"] in variables["SERVER_NAME"].split(" ") :
|
||||||
raise Exception("Service " + service["SERVER_NAME"] + " already exists.")
|
raise Exception("Service " + service["SERVER_NAME"] + " already exists.")
|
||||||
services.append(variables)
|
services.append(variables)
|
||||||
self.__gen_conf(global_env, services)
|
self.__gen_conf(global_env, services)
|
||||||
return "Configuration for " + variables["SERVER_NAME"] + " has been generated."
|
return "Configuration for " + variables["SERVER_NAME"] + " has been generated."
|
||||||
|
|
||||||
def edit_service(self, old_server_name, variables) :
|
def edit_service(self, old_server_name, variables) :
|
||||||
self.delete_service(old_server_name)
|
self.delete_service(old_server_name)
|
||||||
self.new_service(variables)
|
self.new_service(variables)
|
||||||
return "Configuration for " + old_server_name + " has been edited."
|
return "Configuration for " + old_server_name + " has been edited."
|
||||||
|
|
||||||
|
|
||||||
def delete_service(self, server_name) :
|
def delete_service(self, server_name) :
|
||||||
global_env = self.__env_to_dict("/etc/nginx/global.env")
|
global_env = self.__env_to_dict("/etc/nginx/global.env")
|
||||||
services = self.get_services()
|
services = self.get_services()
|
||||||
new_services = []
|
new_services = []
|
||||||
found = False
|
found = False
|
||||||
for service in services :
|
for service in services :
|
||||||
if service["SERVER_NAME"].split(" ")[0] == server_name :
|
if service["SERVER_NAME"].split(" ")[0] == server_name :
|
||||||
found = True
|
found = True
|
||||||
else :
|
else :
|
||||||
new_services.append(service)
|
new_services.append(service)
|
||||||
if not found :
|
if not found :
|
||||||
raise Exception("Can't delete missing " + server_name + " configuration.")
|
raise Exception("Can't delete missing " + server_name + " configuration.")
|
||||||
new_servers = global_env["SERVER_NAME"].split(" ")
|
new_servers = global_env["SERVER_NAME"].split(" ")
|
||||||
if server_name in new_servers :
|
if server_name in new_servers :
|
||||||
new_servers.remove(server_name)
|
new_servers.remove(server_name)
|
||||||
global_env["SERVER_NAME"] = " ".join(new_servers)
|
global_env["SERVER_NAME"] = " ".join(new_servers)
|
||||||
self.__gen_conf(global_env, new_services)
|
self.__gen_conf(global_env, new_services)
|
||||||
return "Configuration for " + server_name + " has been deleted."
|
return "Configuration for " + server_name + " has been deleted."
|
||||||
|
|
||||||
|
|||||||
@ -47,17 +47,16 @@ class Instances :
|
|||||||
|
|
||||||
# Docker instances (containers or services)
|
# Docker instances (containers or services)
|
||||||
if self.__docker != None :
|
if self.__docker != None :
|
||||||
if self.__docker.swarm == None :
|
for instance in self.__docker.containers.list(all=True, filters={"label" : "bunkerized-nginx.UI"}) :
|
||||||
for instance in self.__docker.containers.list(all=True, filters={"label" : "bunkerized-nginx.UI"}) :
|
id = instance.id
|
||||||
id = instance.id
|
name = instance.name
|
||||||
name = instance.name
|
type = "container"
|
||||||
type = "container"
|
status = "down"
|
||||||
status = "down"
|
if instance.status == "running" :
|
||||||
if instance.status == "running" :
|
status = "up"
|
||||||
status = "up"
|
instances.append(self.__instance(id, name, type, status, instance))
|
||||||
instances.append(self.__instance(id, name, type, status, instance))
|
if self.__docker.swarm != None :
|
||||||
else :
|
for instance in self.__docker.services.list(filters={"label" : "bunkerized-nginx.UI"}) :
|
||||||
for instance in self.__docker.services.list(all=True, filters={"label" : "bunkerized-nginx.UI"}) :
|
|
||||||
id = instance.id
|
id = instance.id
|
||||||
name = instance.name
|
name = instance.name
|
||||||
type = "service"
|
type = "service"
|
||||||
|
|||||||
17
ui/src/ReverseProxied.py
Normal file
17
ui/src/ReverseProxied.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
class ReverseProxied(object):
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
def __call__(self, environ, start_response):
|
||||||
|
script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
|
||||||
|
if script_name:
|
||||||
|
environ['SCRIPT_NAME'] = script_name
|
||||||
|
path_info = environ['PATH_INFO']
|
||||||
|
if path_info.startswith(script_name):
|
||||||
|
environ['PATH_INFO'] = path_info[len(script_name):]
|
||||||
|
|
||||||
|
scheme = environ.get('HTTP_X_SCHEME', '')
|
||||||
|
if scheme:
|
||||||
|
environ['wsgi.url_scheme'] = scheme
|
||||||
|
return self.app(environ, start_response)
|
||||||
@ -5,9 +5,9 @@ class User(flask_login.UserMixin) :
|
|||||||
def __init__(self, id, password) :
|
def __init__(self, id, password) :
|
||||||
self.__id = id
|
self.__id = id
|
||||||
self.__password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt())
|
self.__password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt())
|
||||||
|
|
||||||
def get_id(self) :
|
def get_id(self) :
|
||||||
return self.__id
|
return self.__id
|
||||||
|
|
||||||
def check_password(self, password) :
|
def check_password(self, password) :
|
||||||
return bcrypt.checkpw(password.encode("utf-8"), self.__password)
|
return bcrypt.checkpw(password.encode("utf-8"), self.__password)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user