integration - continue work on storageless config for k8s and swarm

This commit is contained in:
florian 2021-09-03 22:40:37 +02:00
parent e55dff8128
commit 062fa3e78a
No known key found for this signature in database
GPG Key ID: 3D80806F12602A7C
12 changed files with 100 additions and 107 deletions

View File

@ -16,6 +16,7 @@ chmod ugo+x /opt/bunkerized-nginx/entrypoint/* /opt/bunkerized-nginx/scripts/*
chmod ugo+x /opt/bunkerized-nginx/gen/main.py chmod ugo+x /opt/bunkerized-nginx/gen/main.py
chmod ugo+x /opt/bunkerized-nginx/jobs/main.py chmod ugo+x /opt/bunkerized-nginx/jobs/main.py
chmod ugo+x /opt/bunkerized-nginx/jobs/reload.py chmod ugo+x /opt/bunkerized-nginx/jobs/reload.py
chmod ugo+x /opt/bunkerized-nginx/jobs/certbot-*.sh
chmod 770 /opt/bunkerized-nginx chmod 770 /opt/bunkerized-nginx
chmod 440 /opt/bunkerized-nginx/settings.json chmod 440 /opt/bunkerized-nginx/settings.json

View File

@ -6,6 +6,15 @@ import Controller
from logger import log from logger import log
CONFIGS = {
"conf": "/etc/nginx",
"letsencrypt": "/etc/letsencrypt",
"http": "/http-confs",
"server": "/server-confs",
"modsec": "/modsec-confs",
"modsec-crs": "/modsec-crs-confs"
}
class Config : class Config :
def __init__(self, type, api_uri, http_port="8080") : def __init__(self, type, api_uri, http_port="8080") :
@ -64,12 +73,32 @@ class Config :
instance.kill("SIGHUP") instance.kill("SIGHUP")
except : except :
ret = False ret = False
elif self.__type == Controller.Type.SWARM : elif self.__type == Controller.Type.SWARM or self.__type == Controller.Type.KUBERNETES :
ret = self.__api_call(instances, "/reload")
elif self.__type == Controller.Type.KUBERNETES :
ret = self.__api_call(instances, "/reload") ret = self.__api_call(instances, "/reload")
return ret return ret
def send(self, instances) :
ret = True
if self.__type == Controller.Type.DOCKER :
return ret
elif self.__type == Controller.Type.SWARM or self.__type == Controller.Type.KUBERNERTES :
fail = False
for name, path in CONFIGS.items() :
file = self.__tarball(path)
if not self.__api_call(instances, "/" + name, file=file) :
log("config", "ERROR", "can't send config " + name + " to instance(s)")
fail = True
file.close()
if fail :
ret = False
return ret
def __tarball(path) :
file = io.BytesIO()
with tarfile.open(mode="w:gz", fileobj=file) as tar :
tar.add(path, arcname=".")
return file
def __ping(self, instances) : def __ping(self, instances) :
return self.__api_call(instances, "/ping") return self.__api_call(instances, "/ping")
@ -120,7 +149,7 @@ class Config :
log("config", "ERROR", "exception while waiting for bunkerized-nginx instances : " + traceback.format_exc()) log("config", "ERROR", "exception while waiting for bunkerized-nginx instances : " + traceback.format_exc())
return False return False
def __api_call(self, instances, path) : def __api_call(self, instances, path, file=None) :
ret = True ret = True
nb = 0 nb = 0
urls = [] urls = []
@ -146,7 +175,10 @@ class Config :
for url in urls : for url in urls :
req = None req = None
try : try :
req = requests.post(url) if file == None :
req = requests.post(url)
else :
req = requests.post(url, {'file': file})
except : except :
pass pass
if req and req.status_code == 200 and req.text == "ok" : if req and req.status_code == 200 and req.text == "ok" :

View File

@ -51,3 +51,10 @@ class Controller(ABC) :
except : except :
ret = False ret = False
return ret return ret
def _send(self, instances) :
try :
ret = self._config.send(instances)
except :
ret = False
return ret

View File

@ -96,9 +96,9 @@ class IngressController(Controller.Controller) :
def process_events(self, current_env) : def process_events(self, current_env) :
self.__old_env = current_env self.__old_env = current_env
t_pod = Thread(target=self.__watch_pod) t_pod = Thread(target=self.__watch, args=("pod",))
t_ingress = Thread(target=self.__watch_ingress) t_ingress = Thread(target=self.__watch, args=("ingress",))
t_service = Thread(target=self.__watch_service) t_service = Thread(target=self.__watch, args=("service",))
t_pod.start() t_pod.start()
t_ingress.start() t_ingress.start()
t_service.start() t_service.start()
@ -106,63 +106,38 @@ class IngressController(Controller.Controller) :
t_ingress.join() t_ingress.join()
t_service.join() t_service.join()
def __watch_pod(self) : def __watch(self, type) :
w = watch.Watch() w = watch.Watch()
for event in w.stream(self.__api.list_pod_for_all_namespaces, label_selector="bunkerized-nginx") : what = None
if type == "pod" :
what = self.__api.list_pod_for_all_namespaces
elif type == "ingress" :
what = self.__extensions_api.list_ingress_for_all_namespaces
elif type == "service" :
what = self.__api.list_service_for_all_namespaces
for event in w.stream(what, label_selector="bunkerized-nginx") :
self.lock.acquire() self.lock.acquire()
new_env = self.get_env() new_env = self.get_env()
if new_env != self.__old_env : if new_env != self.__old_env :
try : try :
if self.gen_conf(new_env) : if not self.gen_conf(new_env) :
self.__old_env = new_env.copy() raise Exception("can't generate configuration")
log("CONTROLLER", "INFO", "successfully generated new configuration") if not self.send() :
if self.reload() : raise Exception("can't send configuration")
log("controller", "INFO", "successful reload") if not self.reload() :
else : raise Exception("can't reload configuration")
log("controller", "ERROR", "failed reload") self.__old_env = new_env.copy()
except : log("CONTROLLER", "INFO", "successfully loaded new configuration")
log("controller", "ERROR", "exception while receiving event") except Exception as e :
self.lock.release() log("controller", "ERROR", "error while computing new event : " + str(e))
def __watch_ingress(self) :
w = watch.Watch()
for event in w.stream(self.__extensions_api.list_ingress_for_all_namespaces, label_selector="bunkerized-nginx") :
self.lock.acquire()
new_env = self.get_env()
if new_env != self.__old_env :
try :
if self.gen_conf(new_env) :
self.__old_env = new_env.copy()
log("CONTROLLER", "INFO", "successfully generated new configuration")
if self.reload() :
log("controller", "INFO", "successful reload")
else :
log("controller", "ERROR", "failed reload")
except :
log("controller", "ERROR", "exception while receiving event")
self.lock.release()
def __watch_service(self) :
w = watch.Watch()
for event in w.stream(self.__api.list_service_for_all_namespaces, label_selector="bunkerized-nginx") :
self.lock.acquire()
new_env = self.get_env()
if new_env != self.__old_env :
try :
if self.gen_conf(new_env) :
self.__old_env = new_env.copy()
log("CONTROLLER", "INFO", "successfully generated new configuration")
if self.reload() :
log("controller", "INFO", "successful reload")
else :
log("controller", "ERROR", "failed reload")
except :
log("controller", "ERROR", "exception while receiving event")
self.lock.release() self.lock.release()
def reload(self) : def reload(self) :
return self._reload(self.__get_services(autoconf=True)) return self._reload(self.__get_services(autoconf=True))
def send(self) :
return self._send(self.__get_services(autoconf=True))
def wait(self) : def wait(self) :
self.lock.acquire() self.lock.acquire()
try : try :

View File

@ -46,23 +46,24 @@ class SwarmController(Controller.Controller) :
if new_env != old_env : if new_env != old_env :
self.lock.acquire() self.lock.acquire()
try : try :
log("controller", "INFO", "generating new configuration") if not self.gen_conf(new_env) :
if self.gen_conf(new_env) : raise Exception("can't generate configuration")
old_env = new_env.copy() if not self.send() :
log("controller", "INFO", "successfully generated new configuration") raise Exception("can't send configuration")
if self.reload() : if not self.reload() :
log("controller", "INFO", "successful reload") raise Exception("can't reload configuration")
else : self.__old_env = new_env.copy()
log("controller", "ERROR", "failed reload") log("CONTROLLER", "INFO", "successfully loaded new configuration")
else : except Exception as e :
log("controller", "ERROR", "can't generate new configuration") log("controller", "ERROR", "error while computing new event : " + str(e))
except :
log("controller", "ERROR", "exception while receiving event")
self.lock.release() self.lock.release()
def reload(self) : def reload(self) :
return self._reload(self.__get_instances()) return self._reload(self.__get_instances())
def send(self) :
return self._send(self.__get_instances())
def wait(self) : def wait(self) :
self.lock.acquire() self.lock.acquire()
try : try :

View File

@ -820,6 +820,7 @@ do_and_check_cmd chmod 750 /opt/bunkerized-nginx/entrypoint/*
do_and_check_cmd chmod 750 /opt/bunkerized-nginx/gen/main.py do_and_check_cmd chmod 750 /opt/bunkerized-nginx/gen/main.py
do_and_check_cmd chmod 750 /opt/bunkerized-nginx/jobs/main.py do_and_check_cmd chmod 750 /opt/bunkerized-nginx/jobs/main.py
do_and_check_cmd chmod 750 /opt/bunkerized-nginx/jobs/reload.py do_and_check_cmd chmod 750 /opt/bunkerized-nginx/jobs/reload.py
do_and_check_cmd chmod 750 /opt/bunkerized-nginx/jobs/certbot-*.sh
# Set permissions for /usr/local/bin/bunkerized-nginx # Set permissions for /usr/local/bin/bunkerized-nginx
do_and_check_cmd chown root:root /usr/local/bin/bunkerized-nginx do_and_check_cmd chown root:root /usr/local/bin/bunkerized-nginx
do_and_check_cmd chmod 750 /usr/local/bin/bunkerized-nginx do_and_check_cmd chmod 750 /usr/local/bin/bunkerized-nginx

View File

@ -37,31 +37,10 @@ spec:
- name: MULTISITE - name: MULTISITE
value: "yes" value: "yes"
volumeMounts: volumeMounts:
- name: confs
mountPath: /etc/nginx
readOnly: true
- name: letsencrypt
mountPath: /etc/letsencrypt
readOnly: true
- name: acme-challenge
mountPath: /acme-challenge
readOnly: true
- name: www - name: www
mountPath: /www mountPath: /www
readOnly: true readOnly: true
volumes: volumes:
- name: confs
hostPath:
path: /shared/confs
type: Directory
- name: letsencrypt
hostPath:
path: /shared/letsencrypt
type: Directory
- name: acme-challenge
hostPath:
path: /shared/acme-challenge
type: Directory
- name: www - name: www
hostPath: hostPath:
path: /shared/www path: /shared/www
@ -108,22 +87,10 @@ spec:
- name: API_URI - name: API_URI
value: "/ChangeMeToSomethingHardToGuess" value: "/ChangeMeToSomethingHardToGuess"
volumeMounts: volumeMounts:
- name: confs
mountPath: /etc/nginx
- name: letsencrypt - name: letsencrypt
mountPath: /etc/letsencrypt mountPath: /etc/letsencrypt
- name: acme-challenge
mountPath: /acme-challenge
volumes: volumes:
- name: confs
hostPath:
path: /shared/confs
type: Directory
- name: letsencrypt - name: letsencrypt
hostPath: hostPath:
path: /shared/letsencrypt path: /shared/letsencrypt
type: Directory type: Directory
- name: acme-challenge
hostPath:
path: /shared/acme-challenge
type: Directory

View File

@ -14,10 +14,7 @@ services:
mode: host mode: host
protocol: tcp protocol: tcp
volumes: volumes:
- /shared/confs:/etc/nginx:ro
- /shared/www:/www:ro - /shared/www:/www:ro
- /shared/letsencrypt:/etc/letsencrypt:ro
- /shared/acme-challenge:/acme-challenge:ro
environment: environment:
- SWARM_MODE=yes - SWARM_MODE=yes
- USE_API=yes - USE_API=yes
@ -41,9 +38,7 @@ services:
image: bunkerity/bunkerized-nginx-autoconf image: bunkerity/bunkerized-nginx-autoconf
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
- /shared/confs:/etc/nginx
- /shared/letsencrypt:/etc/letsencrypt - /shared/letsencrypt:/etc/letsencrypt
- /shared/acme-challenge:/acme-challenge
environment: environment:
- SWARM_MODE=yes - SWARM_MODE=yes
- API_URI=/ChangeMeToSomethingHardToGuess # must match API_URI from nginx - API_URI=/ChangeMeToSomethingHardToGuess # must match API_URI from nginx

View File

@ -6,7 +6,7 @@ class CertbotNew(Job) :
def __init__(self, redis_host=None, copy_cache=False, domain="", email="", staging=False) : def __init__(self, redis_host=None, copy_cache=False, domain="", email="", staging=False) :
name = "certbot-new" name = "certbot-new"
data = ["certbot", "certonly", "--webroot", "-w", "/opt/bunkerized-nginx/acme-challenge", "-n", "-d", domain, "--email", email, "--agree-tos"] data = ["certbot", "certonly", "--manual", "--preferred-challenges=http", "--manual-auth-hook", "/opt/bunkerized-nginx/jobs/certbot-auth.sh", "--manual-cleanup-hook", "/opt/bunkerized-nginx/jobs/certbot-cleanup.sh", "-n", "-d", domain, "--email", email, "--agree-tos"]
if staging : if staging :
data.append("--staging") data.append("--staging")
type = "exec" type = "exec"

3
jobs/certbot-auth.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
echo $CERTBOT_VALIDATION > /opt/bunkerized-nginx/acme-challenge/.well-known/acme-challenge/$CERTBOT_TOKEN

3
jobs/certbot-cleanup.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
rm -f /opt/bunkerized-nginx/acme-challenge/.well-known/acme-challenge/$CERTBOT_TOKEN

View File

@ -41,6 +41,13 @@ api_list["^/letsencrypt$"] = function ()
return M.extract_file("/tmp/letsencrypt.tar.gz", "/etc/letsencrypt/") return M.extract_file("/tmp/letsencrypt.tar.gz", "/etc/letsencrypt/")
end end
api_list["^/acme$"] = function ()
if not M.save_file("/tmp/acme.tar.gz") then
return false
end
return M.extract_file("/tmp/acme.tar.gz", "/acme-challenge")
end
api_list["^/http$"] = function () api_list["^/http$"] = function ()
if not M.save_file("/tmp/http.tar.gz") then if not M.save_file("/tmp/http.tar.gz") then
return false return false
@ -75,7 +82,7 @@ function M.save_file (name)
return false return false
end end
form:set_timeout(1000) form:set_timeout(1000)
file = io.open(name, "a") file = io.open(name, "w")
while true do while true do
local typ, res, err = form:read() local typ, res, err = form:read()
if not typ then if not typ then
@ -89,6 +96,7 @@ function M.save_file (name)
file:write(res) file:write(res)
end end
end end
file:flush()
file:close() file:close()
return true return true
end end