Merge branch 'staging' into dev

This commit is contained in:
Théophile Diot 2024-01-12 16:03:02 +00:00 committed by GitHub
commit 494e111c51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 222 additions and 146 deletions

View file

@ -15,9 +15,11 @@
- [BUGFIX] Remove certbot renew delay causing errors on k8s
- [BUGFIX] Fix missing custom modsec files when BW instances change
- [BUGFIX] Fix inconsistency on config changes when using Redis
- [BUGFIX] Fix web UI not working when using / URL
- [FEATURE] Add Anonymous reporting feature
- [FEATURE] Add support for fallback Referrer-Policies
- [FEATURE] Add profile page to web ui and the possibility to activate the 2FA
- [FEATURE] Add 2FA support to web UI
- [FEATURE] Add username and password management to web UI
- [FEATURE] Add setting REVERSE_PROXY_INCLUDES to manually add "include" directives in the reverse proxies
- [FEATURE] Add support for Redis Sentinel
- [FEATURE] Add support for tls in Ingress definition

View file

@ -1,7 +1,7 @@
{
"name": "autoconf-configs",
"kinds": ["autoconf"],
"delay": 60,
"delay": 180,
"timeout": 60,
"tests": [
{

View file

@ -1,7 +1,7 @@
{
"name": "docker-configs",
"kinds": ["docker"],
"delay": 30,
"delay": 120,
"timeout": 60,
"tests": [
{

View file

@ -2,7 +2,7 @@
"name": "kubernetes-ingress",
"kinds": ["kubernetes"],
"timeout": 60,
"delay": 60,
"delay": 120,
"tests": [
{
"type": "string",

View file

@ -1,26 +1,29 @@
{
"name": "kubernetes-ingress",
"name": "kubernetes-tls",
"kinds": ["kubernetes"],
"timeout": 60,
"delay": 60,
"delay": 300,
"tests": [
{
"type": "string",
"url": "https://app1.example.com",
"string": "hello",
"tls": "app1.example.com,app2.example.com"
"tls": "app1.example.com,app2.example.com",
"tls_edit": false
},
{
"type": "string",
"url": "https://app2.example.com",
"string": "hello",
"tls": "app1.example.com,app2.example.com"
"tls": "app1.example.com,app2.example.com",
"tls_edit": false
},
{
"type": "string",
"url": "https://app3.example.com",
"string": "hello",
"tls": "app3.example.com"
"tls": "app3.example.com",
"tls_edit": false
}
]
}

View file

@ -1,7 +1,8 @@
{
"name": "swarm-configs",
"kinds": ["swarm"],
"timeout": 120,
"timeout": 60,
"delay": 120,
"tests": [
{
"type": "string",

View file

@ -1,3 +1,14 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-bunkerweb
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
@ -247,14 +258,3 @@ spec:
protocol: TCP
port: 6379
targetPort: 6379
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-bunkerweb
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi

View file

@ -100,10 +100,15 @@ class DockerController(Controller):
first=not self._loaded,
)
def __process_event(self, event):
return "Actor" in event and "Attributes" in event["Actor"] and ("bunkerweb.INSTANCE" in event["Actor"]["Attributes"] or "bunkerweb.SERVER_NAME" in event["Actor"]["Attributes"])
def process_events(self):
self._set_autoconf_load_db()
for _ in self.__client.events(decode=True, filters={"type": "container"}):
for event in self.__client.events(decode=True, filters={"type": "container"}):
try:
if not self.__process_event(event):
continue
self._update_settings()
self._instances = self.get_instances()
self._services = self.get_services()

View file

@ -146,15 +146,16 @@ class IngressController(Controller):
for host in tls.hosts:
for service in services:
if host in service["SERVER_NAME"].split(" "):
secret_tls = self.__corev1.list_secret_for_all_namespaces(
secrets_tls = self.__corev1.list_secret_for_all_namespaces(
watch=False,
field_selector=f"metadata.name={tls.secret_name},metadata.namespace={namespace}",
).items
if not secret_tls:
if len(secrets_tls) == 0:
self._logger.warning(
f"Ignoring tls setting for {host} : secret {tls.secret_name} not found.",
)
break
secret_tls = secrets_tls[0]
if not secret_tls.data:
self._logger.warning(
f"Ignoring tls setting for {host} : secret {tls.secret_name} contains no data.",
@ -227,6 +228,22 @@ class IngressController(Controller):
configs[config_type][f"{config_site}{config_name}"] = config_data
return configs
def __process_event(self, event):
obj = event["object"]
metadata = obj.metadata if obj else None
annotations = metadata.annotations if metadata else None
if not obj:
return False
if obj.kind == "Pod":
return annotations and "bunkerweb.io/INSTANCE" in annotations
if obj.kind == "Ingress":
return True
if obj.kind == "ConfigMap":
return annotations and "bunkerweb.io/CONFIG_TYPE" in annotations
if obj.kind == "Service":
return True
return False
def __watch(self, watch_type):
w = watch.Watch()
what = None
@ -245,9 +262,13 @@ class IngressController(Controller):
locked = False
error = False
try:
for _ in w.stream(what):
for event in w.stream(what):
self.__internal_lock.acquire()
locked = True
if not self.__process_event(event):
self.__internal_lock.release()
locked = False
continue
self._update_settings()
self._instances = self.get_instances()
self._services = self.get_services()

View file

@ -16,14 +16,20 @@ class SwarmController(Controller):
super().__init__("swarm")
self.__client = DockerClient(base_url=docker_host)
self.__internal_lock = Lock()
self.__swarm_instances = []
self.__swarm_services = []
self.__swarm_configs = []
def _get_controller_instances(self) -> List[Service]:
self.__swarm_instances = []
return self.__client.services.list(filters={"label": "bunkerweb.INSTANCE"})
def _get_controller_services(self) -> List[Service]:
self.__swarm_services = []
return self.__client.services.list(filters={"label": "bunkerweb.SERVER_NAME"})
def _to_instances(self, controller_instance) -> List[dict]:
self.__swarm_instances.append(controller_instance.id)
instances = []
instance_env = {}
for env in controller_instance.attrs["Spec"]["TaskTemplate"]["ContainerSpec"]["Env"]:
@ -46,6 +52,7 @@ class SwarmController(Controller):
return instances
def _to_services(self, controller_service) -> List[dict]:
self.__swarm_services.append(controller_service.id)
service = {}
for variable, value in controller_service.attrs["Spec"]["Labels"].items():
if not variable.startswith("bunkerweb."):
@ -80,6 +87,7 @@ class SwarmController(Controller):
return services
def get_configs(self) -> Dict[str, Dict[str, Any]]:
self.__swarm_configs = []
configs = {}
for config_type in self._supported_config_types:
configs[config_type] = {}
@ -103,6 +111,7 @@ class SwarmController(Controller):
continue
config_site = f"{config.attrs['Spec']['Labels']['bunkerweb.CONFIG_SITE']}/"
configs[config_type][f"{config_site}{config_name}"] = b64decode(config.attrs["Spec"]["Data"])
self.__swarm_configs.append(config.id)
return configs
def apply_config(self) -> bool:
@ -113,14 +122,40 @@ class SwarmController(Controller):
first=not self._loaded,
)
def __process_event(self, event):
if "Actor" not in event or "ID" not in event["Actor"] or "Type" not in event:
return False
if event["Type"] not in ["service", "config"]:
return False
if event["Type"] == "service":
if event["Actor"]["ID"] in self.__swarm_instances or event["Actor"]["ID"] in self.__swarm_services:
return True
try:
labels = self.__client.services.get(event["Actor"]["ID"]).attrs["Spec"]["Labels"]
return "bunkerweb.INSTANCE" in labels or "bunkerweb.SERVER_NAME" in labels
except:
return False
if event["Type"] == "config":
if event["Actor"]["ID"] in self.__swarm_configs:
return True
try:
return "bunkerweb.CONFIG_TYPE" in self.__client.configs.get(event["Actor"]["ID"]).attrs["Spec"]["Labels"]
except:
return False
return False
def __event(self, event_type):
while True:
locked = False
error = False
try:
for _ in self.__client.events(decode=True, filters={"type": event_type}):
for event in self.__client.events(decode=True, filters={"type": event_type}):
self.__internal_lock.acquire()
locked = True
if not self.__process_event(event):
self.__internal_lock.release()
locked = False
continue
try:
self._update_settings()
self._instances = self.get_instances()
@ -137,6 +172,7 @@ class SwarmController(Controller):
self._logger.info(
"Successfully deployed new configuration 🚀",
)
self._set_autoconf_load_db()
except:
self._logger.error(f"Exception while processing Swarm event ({event_type}) :\n{format_exc()}")
self.__internal_lock.release()

View file

@ -13,6 +13,11 @@ server {
# default mime type is JSON
default_type 'application/json';
# variables
set $reason '';
set $reason_data '';
set $ctx_ref '';
# check IP and do the API call
access_by_lua_block {
-- Instantiate objects and import required modules

View file

@ -1,7 +1,9 @@
server {
# reason variable
# variables
set $reason '';
set $reason_data '';
set $ctx_ref '';
server_name _;
@ -99,6 +101,8 @@ server {
local ok, ret = call_plugin(plugin_obj, "log_default")
if not ok then
logger:log(ERR, ret)
elseif not ret.ret then
logger:log(ERR, plugin_id .. ":log_default() call failed : " .. ret.msg)
else
logger:log(INFO, plugin_id .. ":log_default() call successful : " .. ret.msg)
end

View file

@ -85,6 +85,7 @@ ssl_certificate_by_lua_block {
if not ok then
logger:log(ERR, "error while setting private key : " .. err)
else
logger:log(INFO, "certificate set by " .. plugin_id)
return true
end
end

View file

@ -137,10 +137,6 @@ function bunkernet:access()
if not self:is_needed() then
return self:ret(true, "service doesn't use BunkerNet, skipping access")
end
-- Check id
if not self.bunkernet_id then
return self:ret(false, "missing instance ID")
end
-- Check if IP is global
if not self.ctx.bw.ip_is_global then
return self:ret(true, "IP is not global")
@ -149,6 +145,10 @@ function bunkernet:access()
if is_whitelisted(self.ctx) then
return self:ret(true, "client is whitelisted")
end
-- Check id
if not self.bunkernet_id then
return self:ret(false, "missing instance ID")
end
-- Extract DB
local db, err = self.datastore:get("plugin_bunkernet_db", true)
if db then
@ -175,10 +175,6 @@ function bunkernet:log(bypass_checks)
if not self:is_needed() then
return self:ret(true, "service doesn't use BunkerNet, skipping log")
end
-- Check id
if not self.bunkernet_id then
return self:ret(false, "missing instance ID")
end
end
-- Check if IP has been blocked
local reason, reason_data = get_reason(self.ctx)
@ -192,6 +188,10 @@ function bunkernet:log(bypass_checks)
if not self.ctx.bw.ip_is_global then
return self:ret(true, "IP is not global")
end
-- Check id
if not self.bunkernet_id then
return self:ret(false, "missing instance ID")
end
-- Check if IP has been reported recently
local ok, data = self.cachestore:get("plugin_bunkernet_" .. self.ctx.bw.remote_addr .. "_" .. reason)
if not ok then
@ -239,10 +239,6 @@ function bunkernet:log_default()
if not self:is_needed() then
return self:ret(true, "no service uses BunkerNet, skipping log_default")
end
-- Check id
if not self.bunkernet_id then
return self:ret(false, "missing instance ID")
end
-- Check if default server is disabled
local check, err = get_variable("DISABLE_DEFAULT_SERVER", false)
if check == nil then

View file

@ -32,12 +32,7 @@ try:
bunkernet_activated = False
# Multisite case
if getenv("MULTISITE", "no") == "yes":
servers = getenv("SERVER_NAME") or []
if isinstance(servers, str):
servers = servers.split(" ")
for first_server in servers:
for first_server in getenv("SERVER_NAME", "").split(" "):
if getenv(f"{first_server}_USE_BUNKERNET", getenv("USE_BUNKERNET", "yes")) == "yes":
bunkernet_activated = True
break

View file

@ -36,8 +36,8 @@ function customcert:init()
for server_name, multisite_vars in pairs(vars) do
if multisite_vars["USE_CUSTOM_SSL"] == "yes" and server_name ~= "global" then
local check, data = read_files({
"/var/cache/bunkerweb/customcert/" .. server_name .. "/cert.pem",
"/var/cache/bunkerweb/customcert/" .. server_name .. "/key.pem",
"/var/cache/bunkerweb/customcert/" .. server_name .. ".cert.pem",
"/var/cache/bunkerweb/customcert/" .. server_name .. ".key.pem",
})
if not check then
self.logger:log(ERR, "error while reading files : " .. data)
@ -60,8 +60,8 @@ function customcert:init()
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
end
local check, data = read_files({
"/var/cache/bunkerweb/customcert/" .. server_name:match("%S+") .. "/cert.pem",
"/var/cache/bunkerweb/customcert/" .. server_name:match("%S+") .. "/key.pem",
"/var/cache/bunkerweb/customcert/" .. server_name:match("%S+") .. ".cert.pem",
"/var/cache/bunkerweb/customcert/" .. server_name:match("%S+") .. ".key.pem",
})
if not check then
self.logger:log(ERR, "error while reading files : " .. data)
@ -87,15 +87,14 @@ function customcert:ssl_certificate()
if not server_name then
return self:ret(false, "can't get server_name : " .. err)
end
if self.variables["USE_CUSTOM_SSL"] == "yes" then
local data
data, err = self.datastore:get("plugin_customcert_" .. server_name, true)
if not data then
return self:ret(
false,
"error while getting plugin_customcert_" .. server_name .. " from datastore : " .. err
)
end
local data
data, err = self.datastore:get("plugin_customcert_" .. server_name, true)
if not data and err ~= "not found" then
return self:ret(
false,
"error while getting plugin_customcert_" .. server_name .. " from datastore : " .. err
)
elseif data then
return self:ret(true, "certificate/key data found", data)
end
return self:ret(true, "custom certificate is not used")

View file

@ -28,6 +28,7 @@ db = None
def check_cert(cert_path: str, key_path: str, first_server: str) -> bool:
try:
ret = False
if not cert_path or not key_path:
logger.warning("Both variables CUSTOM_SSL_CERT and CUSTOM_SSL_KEY have to be set to use custom certificates")
return False
@ -48,19 +49,17 @@ def check_cert(cert_path: str, key_path: str, first_server: str) -> bool:
"cache",
"bunkerweb",
"customcert",
first_server,
"cert.pem",
f"{first_server}.cert.pem",
)
cert_cache_path.parent.mkdir(parents=True, exist_ok=True)
cert_hash = file_hash(cert_path)
old_hash = cache_hash(cert_cache_path, db)
if old_hash == cert_hash:
return False
cached, err = cache_file(cert_path, cert_cache_path, cert_hash, db, delete_file=False)
if not cached:
logger.error(f"Error while caching custom-cert cert.pem file : {err}")
if old_hash != cert_hash:
ret = True
cached, err = cache_file(cert_path, cert_cache_path, cert_hash, db, delete_file=False)
if not cached:
logger.error(f"Error while caching custom-cert cert.pem file : {err}")
key_cache_path = Path(
sep,
@ -68,19 +67,19 @@ def check_cert(cert_path: str, key_path: str, first_server: str) -> bool:
"cache",
"bunkerweb",
"customcert",
first_server,
"key.pem",
f"{first_server}.key.pem",
)
key_cache_path.parent.mkdir(parents=True, exist_ok=True)
key_hash = file_hash(key_path)
old_hash = cache_hash(key_cache_path, db)
if old_hash != key_hash:
ret = True
cached, err = cache_file(key_path, key_cache_path, key_hash, db, delete_file=False)
if not cached:
logger.error(f"Error while caching custom-cert key.pem file : {err}")
return True
return ret
except:
logger.error(
f"Exception while running custom-cert.py (check_cert) :\n{format_exc()}",
@ -104,7 +103,7 @@ try:
key_data = b64decode(getenv("CUSTOM_SSL_KEY_DATA", ""))
for file, data in (("cert.pem", cert_data), ("key.pem", key_data)):
if data != b"":
file_path = Path(sep, "var", "tmp", "bunkerweb", "customcert", first_server, file)
file_path = Path(sep, "var", "tmp", "bunkerweb", "customcert", f"{first_server}.{file}")
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_bytes(data)
if file == "cert.pem":
@ -141,7 +140,7 @@ try:
key_data = b64decode(getenv(f"{first_server}_CUSTOM_SSL_KEY_DATA", ""))
for file, data in (("cert.pem", cert_data), ("key.pem", key_data)):
if data != b"":
file_path = Path(sep, "var", "tmp", "bunkerweb", "customcert", first_server, file)
file_path = Path(sep, "var", "tmp", "bunkerweb", "customcert", f"{first_server}.{file}")
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_bytes(data)
if file == "cert.pem":

View file

@ -100,15 +100,14 @@ function letsencrypt:ssl_certificate()
if not server_name then
return self:ret(false, "can't get server_name : " .. err)
end
if self.variables["AUTO_LETS_ENCRYPT"] == "yes" then
local data
data, err = self.datastore:get("plugin_letsencrypt_" .. server_name, true)
if not data then
return self:ret(
false,
"error while getting plugin_letsencrypt_" .. server_name .. " from datastore : " .. err
)
end
local data
data, err = self.datastore:get("plugin_letsencrypt_" .. server_name, true)
if not data and err ~= "not found" then
return self:ret(
false,
"error while getting plugin_letsencrypt_" .. server_name .. " from datastore : " .. err
)
elseif data then
return self:ret(true, "certificate/key data found", data)
end
return self:ret(true, "let's encrypt is not used")
@ -175,9 +174,9 @@ function letsencrypt:api()
if not ok then
return self:ret(true, "can't remove validation token : " .. err, HTTP_INTERNAL_SERVER_ERROR)
end
return true, HTTP_OK, { status = "success", msg = "validation token removed" }
return self:ret(true, "validation token removed", HTTP_OK)
end
return true, HTTP_NOT_FOUND, { status = "error", msg = "unknown request" }
return self:ret(true, "unknown request", HTTP_NOT_FOUND)
end
return letsencrypt

View file

@ -87,18 +87,17 @@ function selfsigned:ssl_certificate()
if not server_name then
return self:ret(false, "can't get server_name : " .. err)
end
if self.variables["GENERATE_SELF_SIGNED_SSL"] == "yes" then
local data
data, err = self.datastore:get("plugin_selfsigned_" .. server_name, true)
if not data then
return self:ret(
false,
"error while getting plugin_selfsigned_" .. server_name .. " from datastore : " .. err
)
end
local data
data, err = self.datastore:get("plugin_selfsigned_" .. server_name, true)
if not data and err ~= "not found" then
return self:ret(
false,
"error while getting plugin_selfsigned_" .. server_name .. " from datastore : " .. err
)
elseif data then
return self:ret(true, "certificate/key data found", data)
end
return self:ret(true, "selfsigned is not used")
return self:ret(true, "self signed is not used")
end
function selfsigned:load_data(data, server_name)

View file

@ -97,7 +97,7 @@ class Global_values(Base):
ForeignKey("bw_settings.id", onupdate="cascade", ondelete="cascade"),
primary_key=True,
)
value = Column(String(4096), nullable=False)
value = Column(String(8192), nullable=False)
suffix = Column(Integer, primary_key=True, nullable=True, default=0)
method = Column(METHODS_ENUM, nullable=False)
@ -128,7 +128,7 @@ class Services_settings(Base):
ForeignKey("bw_settings.id", onupdate="cascade", ondelete="cascade"),
primary_key=True,
)
value = Column(String(4096), nullable=False)
value = Column(String(8192), nullable=False)
suffix = Column(Integer, primary_key=True, nullable=True, default=0)
method = Column(METHODS_ENUM, nullable=False)

View file

@ -3,7 +3,7 @@
--license agpl3
--version %VERSION%
--architecture %ARCH%
--depends bash --depends python3 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends nginx-mod-stream --depends pcre --depends libpq --depends libcap
--depends bash --depends python3 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends nginx-mod-stream --depends pcre --depends libpq --depends libcap --depends openssl
--description "BunkerWeb %VERSION% for Fedora 39"
--url "https://www.bunkerweb.io"
--maintainer "Bunkerity <contact at bunkerity dot com>"

View file

@ -3,7 +3,7 @@
--license agpl3
--version %VERSION%
--architecture %ARCH%
--depends bash --depends python39 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends geoip --depends libpq --depends libcap
--depends bash --depends python39 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends geoip --depends libpq --depends libcap --depends openssl
--description "BunkerWeb %VERSION% for RHEL 8"
--url "https://www.bunkerweb.io"
--maintainer "Bunkerity <contact at bunkerity dot com>"

View file

@ -10,7 +10,7 @@ from yaml import safe_load, dump
class AutoconfTest(Test):
def __init__(self, name, timeout, tests, no_copy_container=False, delay=0):
def __init__(self, name, timeout, tests, no_copy_container=False, delay=0, domains={}):
super().__init__(
name,
"autoconf",
@ -19,13 +19,7 @@ class AutoconfTest(Test):
no_copy_container=no_copy_container,
delay=delay,
)
self._domains = {
r"www\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1')}",
r"auth\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1')}",
r"app1\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_1')}",
r"app2\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_2')}",
r"app3\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_3')}",
}
self._domains = domains
self._check_domains()
@staticmethod

View file

@ -7,7 +7,7 @@ from logger import log
class DockerTest(Test):
def __init__(self, name, timeout, tests, no_copy_container=False, delay=0):
def __init__(self, name, timeout, tests, no_copy_container=False, delay=0, domains={}):
super().__init__(
name,
"docker",
@ -16,13 +16,7 @@ class DockerTest(Test):
no_copy_container=no_copy_container,
delay=delay,
)
self._domains = {
r"www\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1"),
r"auth\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1"),
r"app1\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_1"),
r"app2\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_2"),
r"app3\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_3"),
}
self._domains = domains
self._check_domains()
@staticmethod
@ -60,7 +54,7 @@ class DockerTest(Test):
Test.replace_in_file(
compose,
r"AUTO_LETS_ENCRYPT=yes",
"AUTO_LETS_ENCRYPT=yes\n - USE_LETS_ENCRYPT_STAGING=yes",
"AUTO_LETS_ENCRYPT=yes\n - USE_LETS_ENCRYPT_STAGING=yes\n - LOG_LEVEL=info",
)
Test.replace_in_file(compose, r"DISABLE_DEFAULT_SERVER=yes", "DISABLE_DEFAULT_SERVER=no")
for ex_domain, test_domain in self._domains.items():

View file

@ -10,15 +10,9 @@ from yaml import safe_load_all, dump_all
class KubernetesTest(Test):
def __init__(self, name, timeout, tests, delay=0):
def __init__(self, name, timeout, tests, delay=0, domains={}):
super().__init__(name, "kubernetes", timeout, tests, delay=delay)
self._domains = {
r"www\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_2')}",
r"auth\.example\.com": f"{Test.random_string(1)}.{getenv('TEST_DOMAIN1_2')}",
r"app1\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1')}",
r"app2\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN2')}",
r"app3\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN3')}",
}
self._domains = domains
@staticmethod
def init():
@ -38,6 +32,7 @@ class KubernetesTest(Test):
"USE_PROXY_PROTOCOL": "yes",
"REAL_IP_FROM": "100.64.0.0/10 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8",
"REAL_IP_HEADER": "proxy_protocol",
"LOG_LEVEL": "info"
}
replace_env = {"API_WHITELIST_IP": "127.0.0.1/8 100.64.0.0/10 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8"}
for yaml in data:

View file

@ -8,15 +8,9 @@ from logger import log
class LinuxTest(Test):
def __init__(self, name, timeout, tests, distro):
def __init__(self, name, timeout, tests, distro, domains={}):
super().__init__(name, "linux", timeout, tests, delay=20)
self._domains = {
r"www\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1')}",
r"auth\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1')}",
r"app1\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_1')}",
r"app2\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_2')}",
r"app3\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_3')}",
}
self._domains = domains
if distro not in ("ubuntu", "debian", "fedora", "centos", "rhel"):
raise Exception(f"unknown distro {distro}")
self.__distro = distro
@ -114,7 +108,7 @@ class LinuxTest(Test):
raise Exception("docker exec cp variables.env failed (test)")
proc = self.docker_exec(
self.__distro,
"echo '' >> /etc/bunkerweb/variables.env ; echo 'USE_LETS_ENCRYPT_STAGING=yes' >> /etc/bunkerweb/variables.env",
"echo '' >> /etc/bunkerweb/variables.env ; echo 'USE_LETS_ENCRYPT_STAGING=yes' >> /etc/bunkerweb/variables.env ; echo 'LOG_LEVEL=info' >> /etc/bunkerweb/variables.env",
)
if proc.returncode != 0:
raise (Exception("docker exec append variables.env failed (test)"))

View file

@ -10,15 +10,9 @@ from yaml import safe_load, dump
class SwarmTest(Test):
def __init__(self, name, timeout, tests, delay=0):
def __init__(self, name, timeout, tests, delay=0, domains={}):
super().__init__(name, "swarm", timeout, tests, delay=delay)
self._domains = {
r"www\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_1')}",
r"auth\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1_2')}",
r"app1\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN1')}",
r"app2\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN2')}",
r"app3\.example\.com": f"{Test.random_string(6)}.{getenv('TEST_DOMAIN3')}",
}
self._domains = domains
@staticmethod
def init():
@ -38,6 +32,7 @@ class SwarmTest(Test):
if "AUTO_LETS_ENCRYPT=yes" not in data["services"]["bunkerweb"]["environment"]:
data["services"]["bunkerweb"]["environment"].append("AUTO_LETS_ENCRYPT=yes")
data["services"]["bunkerweb"]["environment"].append("USE_LETS_ENCRYPT_STAGING=yes")
data["services"]["bunkerweb"]["environment"].append("LOG_LEVEL=info")
del data["services"]["bunkerweb"]["deploy"]["placement"]
with open(compose, "w") as f:
f.write(dump(data))

View file

@ -142,15 +142,24 @@ class Test(ABC):
r = get(ex_url, timeout=10, verify=False)
ok = test["status"] == r.status_code
if ok and "tls" in test:
ex_tls = test["tls"]
tls_edit = True
if "tls_edit" in test:
tls_edit = test["tls_edit"]
if tls_edit:
for ex_domain, test_domain in self._domains.items():
if search(ex_domain, ex_tls):
ex_tls = sub(ex_domain, test_domain, ex_tls)
connection = create_connection((urlparse(ex_url).netloc, 443))
context = SSLContext()
sock = context.wrap_socket(connection, server_hostname=urlparse(ex_url).netloc)
cert = sock.getpeercert(True)
sock.close()
x509 = crypto.load_certificate(crypto.FILETYPE_ASN1, cert)
if x509.get_subject().CN != test["tls"]:
if x509.get_subject().CN != ex_tls:
ok = False
log("TEST", "⚠️", f"wrong cert CN : {x509.get_subject().CN}")
log("TEST", "⚠️", f"wrong cert CN : {x509.get_subject().CN} != {ex_tls}")
return ok
except:
return False
raise (Exception(f"unknown test type {test['type']}"))

View file

@ -3,10 +3,10 @@
from pathlib import Path
from sys import path, argv, exit
from glob import glob
from os import _exit
from os import _exit, getenv
from os.path import isfile
from traceback import format_exc
from json import loads
from json import loads, dumps
from subprocess import run
path.extend((f"{Path.cwd()}/utils", f"{Path.cwd()}/tests"))
@ -16,6 +16,7 @@ from AutoconfTest import AutoconfTest
from SwarmTest import SwarmTest
from KubernetesTest import KubernetesTest
from LinuxTest import LinuxTest
from Test import Test
from logger import log
if len(argv) <= 1:
@ -52,6 +53,32 @@ if not ret:
log("TESTS", "", "Test.init() failed")
exit(1)
domains = {}
if test_type in ["docker", "autoconf", "linux"]:
domains = {
r"www\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1"),
r"auth\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1"),
r"app1\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_1"),
r"app2\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_2"),
r"app3\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_3"),
}
elif test_type == "kubernetes":
domains = {
r"www\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_2"),
r"auth\.example\.com": Test.random_string(1) + "." + getenv("TEST_DOMAIN1_2"),
r"app1\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1"),
r"app2\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN2"),
r"app3\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN3"),
}
elif test_type == "swarm":
domains = {
r"www\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_1"),
r"auth\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1_2"),
r"app1\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN1"),
r"app2\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN2"),
r"app3\.example\.com": Test.random_string(6) + "." + getenv("TEST_DOMAIN3"),
}
for example in glob("./examples/*"):
if isfile(f"{example}/tests.json"):
try:
@ -64,6 +91,7 @@ for example in glob("./examples/*"):
"Skipping tests for " + tests["name"] + " (not in kinds)",
)
continue
log("TESTS", "", f"JSON test = {dumps(tests)}")
test_obj = None
no_copy_container = False
delay = 0
@ -78,6 +106,7 @@ for example in glob("./examples/*"):
tests["tests"],
no_copy_container=no_copy_container,
delay=delay,
domains=domains,
)
elif test_type == "autoconf":
test_obj = AutoconfTest(
@ -86,13 +115,14 @@ for example in glob("./examples/*"):
tests["tests"],
no_copy_container=no_copy_container,
delay=delay,
domains=domains,
)
elif test_type == "swarm":
test_obj = SwarmTest(tests["name"], tests["timeout"], tests["tests"], delay=delay)
test_obj = SwarmTest(tests["name"], tests["timeout"], tests["tests"], delay=delay, domains=domains)
elif test_type == "kubernetes":
test_obj = KubernetesTest(tests["name"], tests["timeout"], tests["tests"], delay=delay)
test_obj = KubernetesTest(tests["name"], tests["timeout"], tests["tests"], delay=delay, domains=domains)
elif test_type == "linux":
test_obj = LinuxTest(tests["name"], tests["timeout"], tests["tests"], distro)
test_obj = LinuxTest(tests["name"], tests["timeout"], tests["tests"], distro, domains=domains)
if not test_obj.run_tests():
log("TESTS", "", "Tests failed for " + tests["name"])
if test_type == "linux":

View file

@ -12,7 +12,7 @@ resource "scaleway_vpc_private_network" "pn" {
resource "scaleway_k8s_cluster" "cluster" {
type = "kapsule"
name = "bw_k8s"
version = "1.24.7"
version = "1.28.2"
cni = "cilium"
private_network_id = scaleway_vpc_private_network.pn.id
delete_additional_resources = true