mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Add custom cert to setup wizard + Make it possible to edit some settings when the service is created using the wizard
This commit is contained in:
parent
aa46ba62d2
commit
56dd0f069c
4 changed files with 219 additions and 22 deletions
|
|
@ -210,7 +210,7 @@ class Config:
|
|||
|
||||
return variables
|
||||
|
||||
def new_service(self, variables: dict, is_draft: bool = False, override_method: str = "ui") -> Tuple[str, int]:
|
||||
def new_service(self, variables: dict, is_draft: bool = False, override_method: str = "ui", check_changes: bool = True) -> Tuple[str, int]:
|
||||
"""Creates a new service from the given variables
|
||||
|
||||
Parameters
|
||||
|
|
@ -235,7 +235,9 @@ class Config:
|
|||
return f"Service {service['SERVER_NAME'].split(' ')[0]} already exists.", 1
|
||||
|
||||
services.append(variables | {"IS_DRAFT": "yes" if is_draft else "no"})
|
||||
ret = self.gen_conf(self.get_config(methods=False), services, check_changes=not is_draft, override_method=override_method)
|
||||
ret = self.gen_conf(
|
||||
self.get_config(methods=False), services, check_changes=False if not check_changes else not is_draft, override_method=override_method
|
||||
)
|
||||
if isinstance(ret, str):
|
||||
return ret, 1
|
||||
return f"Configuration for {variables['SERVER_NAME'].split(' ')[0]} has been generated.", 0
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ from os import getenv
|
|||
from threading import Thread
|
||||
from time import time
|
||||
|
||||
from flask import Blueprint, Response, flash, redirect, render_template, request, session, url_for
|
||||
from flask import Blueprint, Response, flash, redirect, render_template, request, url_for
|
||||
from flask_login import current_user
|
||||
|
||||
from app.models.totp import totp as TOTP
|
||||
# from app.models.totp import totp as TOTP
|
||||
|
||||
from app.dependencies import BW_CONFIG, DATA, DB
|
||||
from app.utils import USER_PASSWORD_RX, gen_password_hash
|
||||
|
|
@ -23,7 +23,18 @@ def setup_page():
|
|||
if current_user.is_authenticated:
|
||||
return redirect(url_for("home.home_page"))
|
||||
db_config = DB.get_config(
|
||||
filtered_settings=("SERVER_NAME", "MULTISITE", "USE_UI", "UI_HOST", "AUTO_LETS_ENCRYPT", "USE_LETS_ENCRYPT_STAGING", "EMAIL_LETS_ENCRYPT"),
|
||||
filtered_settings=(
|
||||
"SERVER_NAME",
|
||||
"MULTISITE",
|
||||
"USE_UI",
|
||||
"UI_HOST",
|
||||
"AUTO_LETS_ENCRYPT",
|
||||
"USE_LETS_ENCRYPT_STAGING",
|
||||
"EMAIL_LETS_ENCRYPT",
|
||||
"USE_CUSTOM_SSL",
|
||||
"CUSTOM_SSL_CERT",
|
||||
"CUSTOM_SSL_KEY",
|
||||
),
|
||||
)
|
||||
|
||||
admin_user = DB.get_ui_user()
|
||||
|
|
@ -42,7 +53,19 @@ def setup_page():
|
|||
|
||||
required_keys = []
|
||||
if not ui_reverse_proxy:
|
||||
required_keys.extend(["server_name", "ui_host", "ui_url", "auto_lets_encrypt", "lets_encrypt_staging", "email_lets_encrypt"])
|
||||
required_keys.extend(
|
||||
[
|
||||
"server_name",
|
||||
"ui_host",
|
||||
"ui_url",
|
||||
"auto_lets_encrypt",
|
||||
"lets_encrypt_staging",
|
||||
"email_lets_encrypt",
|
||||
"use_custom_ssl",
|
||||
"custom_ssl_cert",
|
||||
"custom_ssl_key",
|
||||
]
|
||||
)
|
||||
if not admin_user:
|
||||
required_keys.extend(
|
||||
["admin_username", "admin_email", "admin_password", "admin_password_check"]
|
||||
|
|
@ -104,9 +127,12 @@ def setup_page():
|
|||
DATA["RELOADING"] = True
|
||||
DATA["LAST_RELOAD"] = time()
|
||||
|
||||
config = {
|
||||
base_config = {
|
||||
"SERVER_NAME": request.form["server_name"],
|
||||
"USE_TEMPLATE": "ui",
|
||||
}
|
||||
|
||||
config = {
|
||||
"USE_REVERSE_PROXY": "yes",
|
||||
"REVERSE_PROXY_HOST": request.form["ui_host"],
|
||||
"REVERSE_PROXY_URL": request.form["ui_url"] or "/",
|
||||
|
|
@ -116,6 +142,22 @@ def setup_page():
|
|||
|
||||
if request.form.get("auto_lets_encrypt", "no") == "yes":
|
||||
config["AUTO_LETS_ENCRYPT"] = "yes"
|
||||
elif request.form.get("use_custom_ssl", "no") == "yes":
|
||||
if not all(
|
||||
[
|
||||
bool(request.form.get("custom_ssl_cert", "")),
|
||||
bool(request.form.get("custom_ssl_key", "")),
|
||||
]
|
||||
):
|
||||
return handle_error("When using a custom SSL certificate, you must set both the certificate and the key.", "setup")
|
||||
|
||||
config.update(
|
||||
{
|
||||
"USE_CUSTOM_SSL": "yes",
|
||||
"CUSTOM_SSL_CERT": request.form.get("custom_ssl_cert", ""),
|
||||
"CUSTOM_SSL_KEY": request.form.get("custom_ssl_key", ""),
|
||||
}
|
||||
)
|
||||
else:
|
||||
config.update(
|
||||
{
|
||||
|
|
@ -128,18 +170,22 @@ def setup_page():
|
|||
if not config.get("MULTISITE", "no") == "yes":
|
||||
BW_CONFIG.edit_global_conf({"MULTISITE": "yes"}, check_changes=False)
|
||||
|
||||
operation, error = BW_CONFIG.new_service(base_config, override_method="wizard", check_changes=False)
|
||||
if error:
|
||||
return handle_error(f"Couldn't create the new service: {operation}", "setup", False, "error")
|
||||
|
||||
# deepcode ignore MissingAPI: We don't need to check to wait for the thread to finish
|
||||
Thread(
|
||||
target=manage_bunkerweb,
|
||||
name="Reloading instances",
|
||||
args=("services", config, request.form["server_name"], request.form["server_name"]),
|
||||
kwargs={"operation": "new", "threaded": True, "override_method": "wizard"},
|
||||
args=("services", config | base_config, request.form["server_name"], request.form["server_name"]),
|
||||
kwargs={"operation": "edit", "threaded": True},
|
||||
).start()
|
||||
|
||||
return Response(status=200)
|
||||
|
||||
session["tmp_totp_secret"] = TOTP.generate_totp_secret()
|
||||
totp_qr_image = TOTP.generate_qrcode(current_user.get_id(), session["tmp_totp_secret"])
|
||||
# session["tmp_totp_secret"] = TOTP.generate_totp_secret() # TODO: uncomment when TOTP is implemented in setup wizard
|
||||
# totp_qr_image = TOTP.generate_qrcode(current_user.get_id(), session["tmp_totp_secret"])
|
||||
|
||||
return render_template(
|
||||
"setup.html",
|
||||
|
|
@ -151,8 +197,11 @@ def setup_page():
|
|||
auto_lets_encrypt=db_config.get("AUTO_LETS_ENCRYPT", getenv("AUTO_LETS_ENCRYPT", "no")),
|
||||
lets_encrypt_staging=db_config.get("USE_LETS_ENCRYPT_STAGING", getenv("USE_LETS_ENCRYPT_STAGING", "no")),
|
||||
email_lets_encrypt=db_config.get("EMAIL_LETS_ENCRYPT", getenv("EMAIL_LETS_ENCRYPT", "")),
|
||||
totp_qr_image=totp_qr_image,
|
||||
totp_secret=TOTP.get_totp_pretty_key(session.get("tmp_totp_secret", "")),
|
||||
use_custom_ssl=db_config.get("USE_CUSTOM_SSL", getenv("USE_CUSTOM_SSL", "no")),
|
||||
custom_ssl_cert=db_config.get("CUSTOM_SSL_CERT", getenv("CUSTOM_SSL_CERT", "")),
|
||||
custom_ssl_key=db_config.get("CUSTOM_SSL_KEY", getenv("CUSTOM_SSL_KEY", "")),
|
||||
# totp_qr_image=totp_qr_image,
|
||||
# totp_secret=TOTP.get_totp_pretty_key(session.get("tmp_totp_secret", "")),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -404,6 +404,43 @@ $(document).ready(() => {
|
|||
$confirmPasswordInput.siblings(".invalid-feedback").text("");
|
||||
}
|
||||
} else if (!uiReverseProxy && currentStep === 2) {
|
||||
const $customSslCert = $("#CUSTOM_SSL_CERT");
|
||||
const $customSslKey = $("#CUSTOM_SSL_KEY");
|
||||
if (
|
||||
$("#USE_CUSTOM_SSL").prop("checked") &&
|
||||
(!$customSslCert.val() || !$customSslKey.val())
|
||||
) {
|
||||
if (!$customSslCert.val()) {
|
||||
$customSslCert.addClass("is-invalid");
|
||||
let $feedback = $customSslCert.siblings(".invalid-feedback");
|
||||
if (!$feedback.length) {
|
||||
const $textSpan = $customSslCert
|
||||
.parent()
|
||||
.find("span.input-group-text");
|
||||
$feedback = $(
|
||||
'<div class="invalid-feedback">This field is required when using custom SSL.</div>',
|
||||
).insertAfter($textSpan.length ? $textSpan : $customSslCert);
|
||||
} else {
|
||||
$feedback.text("This field is required when using custom SSL.");
|
||||
}
|
||||
}
|
||||
if (!$customSslKey.val()) {
|
||||
$customSslKey.addClass("is-invalid");
|
||||
let $feedback = $customSslKey.siblings(".invalid-feedback");
|
||||
if (!$feedback.length) {
|
||||
const $textSpan = $customSslKey
|
||||
.parent()
|
||||
.find("span.input-group-text");
|
||||
$feedback = $(
|
||||
'<div class="invalid-feedback">This field is required when using custom SSL.</div>',
|
||||
).insertAfter($textSpan.length ? $textSpan : $customSslKey);
|
||||
} else {
|
||||
$feedback.text("This field is required when using custom SSL.");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await checkDNS();
|
||||
const modal = $("#modal-confirm-dns");
|
||||
const $checkUrl = $("#check-url");
|
||||
|
|
@ -506,6 +543,12 @@ $(document).ready(() => {
|
|||
$("#LETS_ENCRYPT_STAGING").prop("checked") ? "yes" : "no",
|
||||
);
|
||||
formData.append("email_lets_encrypt", $("#EMAIL_LETS_ENCRYPT").val());
|
||||
formData.append(
|
||||
"use_custom_ssl",
|
||||
$("#USE_CUSTOM_SSL").prop("checked") ? "yes" : "no",
|
||||
);
|
||||
formData.append("custom_ssl_cert", $("#CUSTOM_SSL_CERT").val());
|
||||
formData.append("custom_ssl_key", $("#CUSTOM_SSL_KEY").val());
|
||||
}
|
||||
|
||||
// Remove beforeunload event to prevent prompt on form submission
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
</div>
|
||||
{% if not ui_user %}
|
||||
<div class="row pb-0">
|
||||
<div class="col-12 col-sm-6 pb-3">
|
||||
<div class="col-12 col-md-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
|
|
@ -136,7 +136,7 @@
|
|||
pattern="^.{1,256}$"
|
||||
required />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-3">
|
||||
<div class="col-12 col-md-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
|
|
@ -159,7 +159,7 @@
|
|||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-email" />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-3">
|
||||
<div class="col-12 col-md-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-password"
|
||||
for="password"
|
||||
|
|
@ -210,7 +210,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-3">
|
||||
<div class="col-12 col-md-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-confirm_password"
|
||||
for="confirm_password"
|
||||
|
|
@ -311,7 +311,8 @@
|
|||
pattern="^.+$"
|
||||
required />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-6">
|
||||
<h6 class="mt-2 mb-2 fw-bold">Reverse Proxy</h6>
|
||||
<div class="col-12 col-md-6 pb-6">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-REVERSE_PROXY_HOST"
|
||||
for="REVERSE_PROXY_HOST"
|
||||
|
|
@ -344,7 +345,7 @@
|
|||
pattern="^.+$"
|
||||
required />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-6">
|
||||
<div class="col-12 col-md-6 pb-6">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
|
|
@ -374,7 +375,8 @@
|
|||
aria-labelledby="label-REVERSE_PROXY_URL"
|
||||
pattern="^.*$" />
|
||||
</div>
|
||||
<div class="col-6 col-sm-3 pb-3">
|
||||
<h6 class="mt-2 mb-2 fw-bold">Let's Encrypt</h6>
|
||||
<div class="col-6 col-md-3 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-AUTO_LETS_ENCRYPT"
|
||||
for="AUTO_LETS_ENCRYPT"
|
||||
|
|
@ -408,7 +410,7 @@
|
|||
{% if auto_lets_encrypt == "yes" %}checked{% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-sm-3 pb-3">
|
||||
<div class="col-6 col-md-3 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-USE_LETS_ENCRYPT_STAGING"
|
||||
for="USE_LETS_ENCRYPT_STAGING"
|
||||
|
|
@ -442,7 +444,7 @@
|
|||
{% if lets_encrypt_staging == "yes" %}checked{% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 pb-3">
|
||||
<div class="col-md-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-EMAIL_LETS_ENCRYPT"
|
||||
for="EMAIL_LETS_ENCRYPT"
|
||||
|
|
@ -474,6 +476,107 @@
|
|||
aria-labelledby="label-EMAIL_LETS_ENCRYPT"
|
||||
pattern="^.*$" />
|
||||
</div>
|
||||
<h6 class="mt-2 mb-2 fw-bold">Custom certificate</h6>
|
||||
<div class="col-12 col-md-2 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-USE_CUSTOM_SSL"
|
||||
for="USE_CUSTOM_SSL"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Use Custom SSL
|
||||
</label>
|
||||
{% if use_custom_ssl == "yes" %}
|
||||
<span class="badge badge-center rounded-pill bg-primary-subtle text-dark d-flex align-items-center justify-content-center p-1 me-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="From global configuration">
|
||||
<span class="bx bx-globe bx-xs"></span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="Whether to use a custom SSL certificate">
|
||||
<span class="bx bx-question-mark bx-xs"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mt-1">
|
||||
<input id="USE_CUSTOM_SSL"
|
||||
name="USE_CUSTOM_SSL"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
aria-labelledby="label-USE_CUSTOM_SSL"
|
||||
{% if use_custom_ssl == "yes" %}checked{% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-5 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-CUSTOM_SSL_CERT"
|
||||
for="CUSTOM_SSL_CERT"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Custom SSL Certificate
|
||||
</label>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if custom_ssl_cert %}
|
||||
<span class="badge badge-center rounded-pill bg-primary-subtle text-dark d-flex align-items-center justify-content-center p-1 me-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="From global configuration">
|
||||
<span class="bx bx-globe bx-xs"></span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="The custom SSL certificate path">
|
||||
<span class="bx bx-question-mark bx-xs"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<input id="CUSTOM_SSL_CERT"
|
||||
name="CUSTOM_SSL_CERT"
|
||||
type="text"
|
||||
placeholder="/tmp/cert.pem"
|
||||
value="{{ custom_ssl_cert }}"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-CUSTOM_SSL_CERT"
|
||||
pattern="^.*$" />
|
||||
</div>
|
||||
<div class="col-6 col-md-5 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-CUSTOM_SSL_KEY"
|
||||
for="CUSTOM_SSL_KEY"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Custom SSL Key
|
||||
</label>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if custom_ssl_key %}
|
||||
<span class="badge badge-center rounded-pill bg-primary-subtle text-dark d-flex align-items-center justify-content-center p-1 me-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="From global configuration">
|
||||
<span class="bx bx-globe bx-xs"></span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="The custom SSL key path">
|
||||
<span class="bx bx-question-mark bx-xs"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<input id="CUSTOM_SSL_KEY"
|
||||
name="CUSTOM_SSL_KEY"
|
||||
type="text"
|
||||
placeholder="/tmp/key.pem"
|
||||
value="{{ custom_ssl_key }}"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-CUSTOM_SSL_KEY"
|
||||
pattern="^.*$" />
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-center relative w-full p-2 text-primary rounded-lg fw-bold">
|
||||
|
|
|
|||
Loading…
Reference in a new issue