mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
road to certificate fallback
This commit is contained in:
parent
35d46f424a
commit
34c0657224
12 changed files with 98 additions and 194 deletions
|
|
@ -3,8 +3,10 @@
|
|||
## v1.5.5 - YYYY/MM/DD
|
||||
|
||||
- [BUGFIX] Fix issues with the database when upgrading from version 1.5.3 and 1.5.4 to the most recent version
|
||||
- [BUGFIX] Fix ModSecurity-nginx to make it work with brotli
|
||||
- [FEATURE] Add Anonymous reporting feature
|
||||
- [FEATURE] Add support for fallback Referrer-Policies
|
||||
- [MISC] Fallback to default HTTPS certificate to prevent errors
|
||||
- [DEPS] Updated ModSecurity to v3.0.11
|
||||
|
||||
## v1.5.4 - 2023/12/04
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ server {
|
|||
set $server_name '{{ SERVER_NAME.split(" ")[0] }}';
|
||||
|
||||
# include LUA files
|
||||
include {{ NGINX_PREFIX }}ssl-certificate-stream-lua.conf;
|
||||
include {{ NGINX_PREFIX }}preread-stream-lua.conf;
|
||||
include {{ NGINX_PREFIX }}log-stream-lua.conf;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
ssl_certificate /var/cache/bunkerweb/default-server-cert/cert.pem;
|
||||
ssl_certificate_key /var/cache/bunkerweb/default-server-cert/cert.key;
|
||||
ssl_protocols {{ SSL_PROTOCOLS }};
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_tickets off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
{% if "TLSv1.2" in SSL_PROTOCOLS +%}
|
||||
ssl_dhparam /etc/nginx/dhparam;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
{% endif %}
|
||||
|
||||
{% if AUTO_LETS_ENCRYPT == "yes" or USE_CUSTOM_SSL == "yes" or GENERATE_SELF_SIGNED_SSL == "yes" %}
|
||||
listen 0.0.0.0:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
|
||||
{% if USE_IPV6 == "yes" +%}
|
||||
listen [::]:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
ssl_certificate_by_lua_block {
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cclusterstore = require "bunkerweb.clusterstore"
|
||||
local cjson = require "cjson"
|
||||
local ssl = require "ngx.ssl"
|
||||
|
||||
-- Start ssl_certificate phase
|
||||
local logger = clogger:new("SSL-CERTIFICATE")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "ssl_certificate phase started")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Call ssl_certificate() methods
|
||||
logger:log(ngx.INFO, "calling ssl_certificate() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.ssl_certificate) do
|
||||
-- Require call
|
||||
local plugin_lua, err = helpers.require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ngx.ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(ngx.INFO, err)
|
||||
else
|
||||
-- Check if plugin has ssl_certificate method
|
||||
if plugin_lua.ssl_certificate ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "ssl_certificate")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":ssl_certificate() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":ssl_certificate() call successful : " .. ret.msg)
|
||||
if ret.status then
|
||||
logger:log(ngx.INFO, plugin_id .. " is setting certificate/key : " .. ret.msg)
|
||||
local ok, err = ssl.set_cert(ret.status[1])
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while setting certificate : " .. err)
|
||||
else
|
||||
local ok, err = ssl.set_priv_key(ret.status[2])
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while setting private key : " .. err)
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method ssl_certificate() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called ssl_certificate() methods of plugins")
|
||||
|
||||
logger:log(ngx.INFO, "ssl_certificate phase ended")
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
# {% set os_path = import("os.path") %}
|
||||
|
||||
# {% if USE_CUSTOM_SSL == "yes" %}
|
||||
|
||||
# # listen on HTTPS PORT
|
||||
# listen 0.0.0.0:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
# {% if USE_IPV6 == "yes" +%}
|
||||
# listen [::]:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
# {% endif %}
|
||||
|
||||
# # TLS config
|
||||
# # ssl_certificate /var/cache/bunkerweb/default-server-cert/cert.pem;
|
||||
# # ssl_certificate_key /var/cache/bunkerweb/default-server-cert/cert.key;
|
||||
# # {% if os_path.isfile("/var/cache/bunkerweb/customcert/" + SERVER_NAME.split(" ")[0] + "/cert.pem") %}
|
||||
# # ssl_certificate /var/cache/bunkerweb/customcert/{{ SERVER_NAME.split(" ")[0] }}/cert.pem;
|
||||
# # {% else %}
|
||||
# # ssl_certificate /var/cache/bunkerweb/customcert/cert.pem;
|
||||
# # {% endif %}
|
||||
# # {% if os_path.isfile("/var/cache/bunkerweb/customcert/" + SERVER_NAME.split(" ")[0] + "/key.pem") %}
|
||||
# # ssl_certificate_key /var/cache/bunkerweb/customcert/{{ SERVER_NAME.split(" ")[0] }}/key.pem;
|
||||
# # {% else %}
|
||||
# # ssl_certificate_key /var/cache/bunkerweb/customcert/key.pem;
|
||||
# # {% endif %}
|
||||
# ssl_protocols {{ SSL_PROTOCOLS }};
|
||||
# ssl_prefer_server_ciphers on;
|
||||
# ssl_session_tickets off;
|
||||
# ssl_session_timeout 1d;
|
||||
# ssl_session_cache shared:MozSSL:10m;
|
||||
# {% if "TLSv1.2" in SSL_PROTOCOLS +%}
|
||||
# ssl_dhparam /etc/nginx/dhparam;
|
||||
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
# {% endif %}
|
||||
|
||||
# {% endif %}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
{% set os_path = import("os.path") %}
|
||||
|
||||
{% if USE_CUSTOM_SSL == "yes" %}
|
||||
{% if os_path.isfile("/var/cache/bunkerweb/customcert/cert.pem") and os_path.isfile("/var/cache/bunkerweb/customcert/key.pem") or os_path.isfile("/var/cache/bunkerweb/customcert/" + SERVER_NAME.split(" ")[0] + "/cert.pem") and os_path.isfile("/var/cache/bunkerweb/customcert/" + SERVER_NAME.split(" ")[0] + "/key.pem") +%}
|
||||
|
||||
# listen
|
||||
listen 0.0.0.0:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
|
||||
{% if USE_IPV6 == "yes" +%}
|
||||
listen [::]:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
|
||||
{% endif %}
|
||||
|
||||
# TLS config
|
||||
{% if os_path.isfile("/var/cache/bunkerweb/customcert/" + SERVER_NAME.split(" ")[0] + "/cert.pem") %}
|
||||
ssl_certificate /var/cache/bunkerweb/customcert/{{ SERVER_NAME.split(" ")[0] }}/cert.pem;
|
||||
{% else %}
|
||||
ssl_certificate /var/cache/bunkerweb/customcert/cert.pem;
|
||||
{% endif %}
|
||||
{% if os_path.isfile("/var/cache/bunkerweb/customcert/" + SERVER_NAME.split(" ")[0] + "/key.pem") %}
|
||||
ssl_certificate_key /var/cache/bunkerweb/customcert/{{ SERVER_NAME.split(" ")[0] }}/key.pem;
|
||||
{% else %}
|
||||
ssl_certificate_key /var/cache/bunkerweb/customcert/key.pem;
|
||||
{% endif %}
|
||||
ssl_protocols {{ SSL_PROTOCOLS }};
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_tickets off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSLStream:10m;
|
||||
{% if "TLSv1.2" in SSL_PROTOCOLS +%}
|
||||
ssl_dhparam /etc/nginx/dhparam;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
@ -2,29 +2,4 @@
|
|||
location ~ ^/.well-known/acme-challenge/ {
|
||||
root /var/tmp/bunkerweb/lets-encrypt;
|
||||
auth_basic off;
|
||||
}
|
||||
|
||||
# {% if AUTO_LETS_ENCRYPT == "yes" %}
|
||||
|
||||
# # listen on HTTPS PORT
|
||||
# listen 0.0.0.0:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
# {% if USE_IPV6 == "yes" +%}
|
||||
# listen [::]:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
# {% endif %}
|
||||
|
||||
# # TLS config
|
||||
# ssl_certificate /var/cache/bunkerweb/default-server-cert/cert.pem;
|
||||
# ssl_certificate_key /var/cache/bunkerweb/default-server-cert/cert.key;
|
||||
# #ssl_certificate /var/cache/bunkerweb/letsencrypt/etc/live/{{ SERVER_NAME.split(" ")[0] }}/fullchain.pem;
|
||||
# #ssl_certificate_key /var/cache/bunkerweb/letsencrypt/etc/live/{{ SERVER_NAME.split(" ")[0] }}/privkey.pem;
|
||||
# ssl_protocols {{ SSL_PROTOCOLS }};
|
||||
# ssl_prefer_server_ciphers on;
|
||||
# ssl_session_tickets off;
|
||||
# ssl_session_timeout 1d;
|
||||
# ssl_session_cache shared:MozSSL:10m;
|
||||
# {% if "TLSv1.2" in SSL_PROTOCOLS +%}
|
||||
# ssl_dhparam /etc/nginx/dhparam;
|
||||
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
# {% endif %}
|
||||
|
||||
# {% endif %}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
{% if AUTO_LETS_ENCRYPT == "yes" %}
|
||||
|
||||
# listen
|
||||
listen 0.0.0.0:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
|
||||
{% if USE_IPV6 == "yes" +%}
|
||||
listen [::]:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
|
||||
{% endif %}
|
||||
|
||||
# TLS config
|
||||
ssl_certificate /var/cache/bunkerweb/letsencrypt/etc/live/{{ SERVER_NAME.split(" ")[0] }}/fullchain.pem;
|
||||
ssl_certificate_key /var/cache/bunkerweb/letsencrypt/etc/live/{{ SERVER_NAME.split(" ")[0] }}/privkey.pem;
|
||||
ssl_protocols {{ SSL_PROTOCOLS }};
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_tickets off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSLStream:10m;
|
||||
{% if "TLSv1.2" in SSL_PROTOCOLS +%}
|
||||
ssl_dhparam /etc/nginx/dhparam;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
|
@ -45,7 +45,7 @@ function letsencrypt:init()
|
|||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local check, data = self:read_files(server_name:gmatch("%S+")[1])
|
||||
local check, data = self:read_files(server_name:match("%S+"))
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
|
|
|
|||
|
|
@ -26,36 +26,6 @@ logger = setup_logger("DEFAULT-SERVER-CERT", getenv("LOG_LEVEL", "INFO"))
|
|||
status = 0
|
||||
|
||||
try:
|
||||
# Check if we need to generate a self-signed default cert for non-SNI "clients"
|
||||
# need_default_cert = False
|
||||
# if getenv("MULTISITE", "no") == "yes":
|
||||
# for first_server in getenv("SERVER_NAME", "").split(" "):
|
||||
# for check_var in (
|
||||
# "USE_CUSTOM_SSL",
|
||||
# "AUTO_LETS_ENCRYPT",
|
||||
# "GENERATE_SELF_SIGNED_SSL",
|
||||
# ):
|
||||
# if getenv(f"{first_server}_{check_var}", getenv(check_var, "no")) == "yes":
|
||||
# need_default_cert = True
|
||||
# break
|
||||
# if need_default_cert:
|
||||
# break
|
||||
# elif getenv("DISABLE_DEFAULT_SERVER", "no") == "yes" and (
|
||||
# "yes"
|
||||
# in (
|
||||
# getenv("USE_CUSTOM_SSL", "no"),
|
||||
# getenv("AUTO_LETS_ENCRYPT", "no"),
|
||||
# getenv("GENERATE_SELF_SIGNED_SSL", "no"),
|
||||
# )
|
||||
# ):
|
||||
# need_default_cert = True
|
||||
|
||||
# # Generate the self-signed certificate
|
||||
# if not need_default_cert:
|
||||
# logger.info(
|
||||
# "Skipping generation of self-signed certificate for default server (not needed)",
|
||||
# )
|
||||
# _exit(0)
|
||||
|
||||
cert_path = Path(sep, "var", "cache", "bunkerweb", "default-server-cert")
|
||||
cert_path.mkdir(parents=True, exist_ok=True)
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
# {% if GENERATE_SELF_SIGNED_SSL == "yes" %}
|
||||
|
||||
# # listen on HTTPS PORT
|
||||
# listen 0.0.0.0:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
# {% if USE_IPV6 == "yes" +%}
|
||||
# listen [::]:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
# {% endif %}
|
||||
|
||||
# # TLS config
|
||||
# ssl_certificate /var/cache/bunkerweb/default-server-cert/cert.pem;
|
||||
# ssl_certificate_key /var/cache/bunkerweb/default-server-cert/cert.key;
|
||||
# # ssl_certificate /var/cache/bunkerweb/selfsigned/{{ SERVER_NAME.split(" ")[0] }}.pem;
|
||||
# # ssl_certificate_key /var/cache/bunkerweb/selfsigned/{{ SERVER_NAME.split(" ")[0] }}.key;
|
||||
# ssl_protocols {{ SSL_PROTOCOLS }};
|
||||
# ssl_prefer_server_ciphers on;
|
||||
# ssl_session_tickets off;
|
||||
# ssl_session_timeout 1d;
|
||||
# ssl_session_cache shared:MozSSL:10m;
|
||||
# {% if "TLSv1.2" in SSL_PROTOCOLS +%}
|
||||
# ssl_dhparam /etc/nginx/dhparam;
|
||||
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
# {% endif %}
|
||||
|
||||
# {% endif %}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
{% if GENERATE_SELF_SIGNED_SSL == "yes" %}
|
||||
|
||||
# listen
|
||||
listen 0.0.0.0:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
|
||||
{% if USE_IPV6 == "yes" +%}
|
||||
listen [::]:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
|
||||
{% endif %}
|
||||
|
||||
# TLS config
|
||||
ssl_certificate /var/cache/bunkerweb/selfsigned/{{ SERVER_NAME.split(" ")[0] }}.pem;
|
||||
ssl_certificate_key /var/cache/bunkerweb/selfsigned/{{ SERVER_NAME.split(" ")[0] }}.key;
|
||||
ssl_protocols {{ SSL_PROTOCOLS }};
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_tickets off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSLStream:10m;
|
||||
{% if "TLSv1.2" in SSL_PROTOCOLS +%}
|
||||
ssl_dhparam /etc/nginx/dhparam;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
|
@ -44,7 +44,7 @@ function selfsigned:init()
|
|||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local check, data = self:read_files(server_name:gmatch("%S+")[1])
|
||||
local check, data = self:read_files(server_name:match("%S+"))
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
|
|
|
|||
Loading…
Reference in a new issue