Some update
This commit is contained in:
parent
961e7aebe4
commit
63a1499977
22 changed files with 12037 additions and 53 deletions
|
|
@ -1 +1,2 @@
|
|||
run/
|
||||
run/
|
||||
control-server/build/
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
run/
|
||||
run/
|
||||
control-server/build/
|
||||
76
.vscode/settings.json
vendored
Normal file
76
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"ostream": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"cctype": "cpp",
|
||||
"charconv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"codecvt": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"format": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ranges": "cpp",
|
||||
"span": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"variant": "cpp",
|
||||
"text_encoding": "cpp",
|
||||
"queue": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"any": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"coroutine": "cpp",
|
||||
"source_location": "cpp",
|
||||
"future": "cpp",
|
||||
"mutex": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"thread": "cpp",
|
||||
"list": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"stdfloat": "cpp",
|
||||
"fstream": "cpp"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +1,44 @@
|
|||
# Remember to update!
|
||||
FROM docker.io/nginx:1.29.0-alpine-slim
|
||||
ARG NGINX_TAG=1.29.0-alpine-slim
|
||||
|
||||
#----------------------------------------#
|
||||
|
||||
FROM docker.io/nginx:${NGINX_TAG} AS builder
|
||||
|
||||
WORKDIR /usr/control-server
|
||||
|
||||
COPY control-server /usr/control-server
|
||||
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
cmake \
|
||||
&& mkdir -p build \
|
||||
&& cd build \
|
||||
&& cmake .. \
|
||||
&& make -j$(nproc) \
|
||||
&& make install \
|
||||
&& du -h /usr/local/bin/control_server
|
||||
|
||||
#----------------------------------------#
|
||||
|
||||
FROM docker.io/nginx:${NGINX_TAG}
|
||||
|
||||
ENV DOMAIN="example.localhost"
|
||||
ENV SERVER_ID="server"
|
||||
ENV RELOAD_FILE="/var/run/nginx-reload"
|
||||
ENV ACME_CHALLENGE_URL="http://acme-challenge.${DOMAIN}/.well-known/acme-challenge/"
|
||||
ENV CONTROL_DOMAIN="control.localhost"
|
||||
ENV CONTROL_TOKEN="Tr0ub4dor&3"
|
||||
|
||||
RUN apk add --no-cache \
|
||||
inotify-tools # For reloading
|
||||
|
||||
# Copy the configuration files
|
||||
COPY nginx /etc/nginx/
|
||||
|
||||
# Copy the dummy certificate files
|
||||
COPY certificates /etc/ssl/
|
||||
|
||||
# Copy the entrypoint scripts
|
||||
COPY --chmod=0755 docker-entrypoint.d /docker-entrypoint.d/
|
||||
COPY --from=builder /usr/local/bin/control_server /usr/local/bin/control_server
|
||||
|
||||
# Copy the scripts
|
||||
COPY --chmod=0755 scripts/ /opt/scripts/
|
||||
RUN ln -s /opt/scripts/reload.sh /usr/local/bin/reload
|
||||
|
||||
# Create the volumes for certificates and website files
|
||||
VOLUME /etc/ssl/certs
|
||||
VOLUME /var/www/html
|
||||
|
||||
# Expose the ports for HTTP and HTTPS
|
||||
EXPOSE 80/tcp 443/tcp 443/udp
|
||||
19
README.md
19
README.md
|
|
@ -9,6 +9,7 @@ This is a container that helps host a static website.
|
|||
**Requires** the following environment variables:
|
||||
- `DOMAIN`: The domain
|
||||
- `ACME_CHALLENGE_HOST`: The source of `.well-known/acme-challenge`
|
||||
- `CONTROL_TOKEN`: Token to access the control server
|
||||
|
||||
You're also encouraged to provide your own:
|
||||
- `/etc/ssl/dhparam.pem`, generated with:
|
||||
|
|
@ -22,5 +23,23 @@ You're also encouraged to provide your own:
|
|||
- `SERVER_ID`: How to call this server (for info)
|
||||
- **Mount** `/var/run/nginx-reload`: modify this file to reload nginx
|
||||
|
||||
## Control server
|
||||
Authorize as you normally would with a Bearer token.
|
||||
|
||||
If you get an empty response, watch the status code!
|
||||
|
||||
Response format:
|
||||
```json
|
||||
{
|
||||
"status": "ok|client_error|server_error",
|
||||
"message": "Optional, human-readable feedback message"
|
||||
}
|
||||
```
|
||||
|
||||
Endpoints:
|
||||
- `/`: Health check
|
||||
- `/reload`: Reloads nginx
|
||||
- `/certificate/<domain>`: Uploads a certificate (POST, upload like a form with field names `certificate` and `private_key`)
|
||||
|
||||
## TODO
|
||||
- support for multiple domains
|
||||
110
control-server/CMakeLists.txt
Normal file
110
control-server/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# Project meta data
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
project(
|
||||
control_server
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# Build type & C++ standard
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||
message(FATAL_ERROR "Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}. Only Clang and GNU compilers are supported.")
|
||||
endif()
|
||||
|
||||
if (NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Build type" FORCE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# Output layout (bin/ lib/ inside the build dir)
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
include(GNUInstallDirs)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# LTO / IPO
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
set_property(GLOBAL PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
|
||||
set_property(GLOBAL PROPERTY INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# Helper functions
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
function(enable_warnings tgt)
|
||||
target_compile_options(${tgt} PRIVATE
|
||||
-Wall -Wextra -Wpedantic
|
||||
-Wconversion -Wshadow
|
||||
-Wduplicated-cond -Wduplicated-branches
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Wstrict-aliasing>
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(enable_size_optimizations tgt)
|
||||
# Put the ultra-tiny flags only on Release/MinsizeRel
|
||||
target_compile_options(${tgt} PRIVATE
|
||||
$<$<CONFIG:MinSizeRel,Release>:-Os>
|
||||
-ffunction-sections -fdata-sections
|
||||
-fno-rtti -fno-unwind-tables
|
||||
-fno-asynchronous-unwind-tables -fomit-frame-pointer
|
||||
-fno-ident -pipe
|
||||
)
|
||||
target_link_options(${tgt} PRIVATE
|
||||
$<$<CONFIG:Debug>:-Wl,-Map=${tgt}.map>
|
||||
$<$<CONFIG:MinSizeRel,Release>:-Wl,--gc-sections>
|
||||
$<$<CONFIG:MinSizeRel,Release>:-s>
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(enable_static tgt)
|
||||
target_link_options(${tgt} PRIVATE
|
||||
$<$<CONFIG:MinSizeRel,Release>:-static>
|
||||
$<$<CONFIG:MinSizeRel,Release>:-static-libstdc++>
|
||||
$<$<CONFIG:MinSizeRel,Release>:-static-libgcc>
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# Position Independent Code (PIC)
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie")
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# Target
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
add_executable(control_server
|
||||
src/control_server.cpp
|
||||
src/nginx_process.cpp
|
||||
src/certificate.cpp
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
target_include_directories(control_server PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
target_link_libraries(control_server PRIVATE
|
||||
stdc++fs
|
||||
)
|
||||
|
||||
enable_warnings(control_server)
|
||||
enable_size_optimizations(control_server)
|
||||
enable_static(control_server)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# Install
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
install(
|
||||
TARGETS control_server
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
17
control-server/include/certificate.hpp
Normal file
17
control-server/include/certificate.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
class CertificateManager {
|
||||
public:
|
||||
CertificateManager(const std::filesystem::path& certificate_directory_path);
|
||||
|
||||
bool certificate_exists(const std::string& domain) const;
|
||||
void write_private_key(const std::string& domain, const std::string& private_key) const;
|
||||
void write_certificate(const std::string& domain, const std::string& certificate) const;
|
||||
private:
|
||||
std::filesystem::path certificate_root_path_;
|
||||
|
||||
inline std::filesystem::path get_certificate_directory(const std::string& domain) const;
|
||||
};
|
||||
22
control-server/include/control_server.hpp
Normal file
22
control-server/include/control_server.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "httplib.hpp"
|
||||
#include "nginx_process.hpp"
|
||||
#include "certificate.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ControlServer {
|
||||
public:
|
||||
ControlServer(const std::string& socket_path, NginxProcess& nginx_process, CertificateManager& certificate_manager);
|
||||
|
||||
void start();
|
||||
void listen();
|
||||
private:
|
||||
std::string socket_path_;
|
||||
NginxProcess& nginx_process_;
|
||||
CertificateManager& certificate_manager_;
|
||||
httplib::Server server_;
|
||||
|
||||
void setup_routes();
|
||||
};
|
||||
11492
control-server/include/httplib.hpp
Normal file
11492
control-server/include/httplib.hpp
Normal file
File diff suppressed because it is too large
Load diff
13
control-server/include/nginx_process.hpp
Normal file
13
control-server/include/nginx_process.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
|
||||
class NginxProcess {
|
||||
public:
|
||||
NginxProcess(const std::string& pid_file_path);
|
||||
pid_t read_pid() const;
|
||||
void reload() const;
|
||||
private:
|
||||
std::string pid_file_path_;
|
||||
};
|
||||
|
||||
35
control-server/src/certificate.cpp
Normal file
35
control-server/src/certificate.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#include "certificate.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <system_error>
|
||||
|
||||
void write_to_file(std::filesystem::path path, const std::string& content) {
|
||||
std::ofstream private_key_file;
|
||||
private_key_file.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
|
||||
// These lines will throw
|
||||
private_key_file.open(path);
|
||||
private_key_file << content;
|
||||
}
|
||||
|
||||
CertificateManager::CertificateManager(const std::filesystem::path& certificate_root_path)
|
||||
: certificate_root_path_(certificate_root_path) {}
|
||||
|
||||
inline std::filesystem::path CertificateManager::get_certificate_directory(const std::string& domain) const {
|
||||
return certificate_root_path_ / domain;
|
||||
}
|
||||
|
||||
bool CertificateManager::certificate_exists(const std::string& domain) const {
|
||||
return std::filesystem::exists(get_certificate_directory(domain));
|
||||
}
|
||||
|
||||
void CertificateManager::write_private_key(const std::string& domain, const std::string& private_key) const {
|
||||
std::filesystem::path path = get_certificate_directory(domain) / "privkey.pem";
|
||||
write_to_file(domain, private_key.c_str());
|
||||
}
|
||||
|
||||
void CertificateManager::write_certificate(const std::string& domain, const std::string& private_key) const {
|
||||
std::filesystem::path path = get_certificate_directory(domain) / "fullchain.pem";
|
||||
write_to_file(domain, private_key.c_str());
|
||||
}
|
||||
101
control-server/src/control_server.cpp
Normal file
101
control-server/src/control_server.cpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#include "control_server.hpp"
|
||||
|
||||
#include "nginx_process.hpp"
|
||||
|
||||
ControlServer::ControlServer(const std::string& socket_path, NginxProcess& nginx_process, CertificateManager& certificate_manager)
|
||||
: socket_path_(socket_path), nginx_process_(nginx_process), certificate_manager_(certificate_manager) {}
|
||||
|
||||
inline void response_ok(httplib::Response &res) {
|
||||
res.set_content("{\"status\": \"ok\"}", "application/json");
|
||||
}
|
||||
|
||||
inline void response_client_error(httplib::Response &res, const std::string &message, const httplib::StatusCode status) {
|
||||
res.status = status;
|
||||
res.set_content("{\"status\": \"client_error\", \"message\": \"" + message + "\"}", "application/json");
|
||||
}
|
||||
|
||||
inline void response_server_error(httplib::Response &res, const std::string &message, const httplib::StatusCode status) {
|
||||
res.status = status;
|
||||
res.set_content("{\"status\": \"server_error\", \"message\": \"" + message + "\"}", "application/json");
|
||||
}
|
||||
|
||||
void ControlServer::setup_routes() {
|
||||
server_.Get("/", [](const httplib::Request& /* req */, httplib::Response &res) {
|
||||
std::cout << "Received health request" << std::endl;
|
||||
|
||||
response_ok(res);
|
||||
});
|
||||
|
||||
server_.Get("/reload", [this](const httplib::Request& /* req */, httplib::Response &res) {
|
||||
std::cout << "Received reload request" << std::endl;
|
||||
|
||||
try
|
||||
{
|
||||
nginx_process_.reload();
|
||||
response_ok(res);
|
||||
std::cout << "Nginx reloaded successfully" << std::endl;
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
response_server_error(res, e.what(), httplib::StatusCode::InternalServerError_500);
|
||||
std::cout << "Failed to reload Nginx: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
server_.Get("/certificate/:domain", [](const httplib::Request& /* req */, httplib::Response &res) {
|
||||
res.status = httplib::StatusCode::MethodNotAllowed_405;
|
||||
// TODO maybe we could do something here?
|
||||
});
|
||||
|
||||
server_.Post("/certificate/:domain", [this](const httplib::Request& req, httplib::Response &res) {
|
||||
// TODO do we need to sanitize?
|
||||
std::string domain = req.get_param_value("domain");
|
||||
|
||||
if (!req.form.has_file("certificate") || !req.form.has_file("private_key")) {
|
||||
response_client_error(res, "Missing certificate and/or private_key fields", httplib::StatusCode::BadRequest_400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!certificate_manager_.certificate_exists(domain)) {
|
||||
response_client_error(res, "Invalid domain", httplib::StatusCode::BadRequest_400);
|
||||
return;
|
||||
}
|
||||
|
||||
httplib::FormData private_key_field = req.form.get_file("private_key");
|
||||
httplib::FormData certificate_field = req.form.get_file("certificate");
|
||||
|
||||
// TODO more safeguards
|
||||
try {
|
||||
certificate_manager_.write_private_key(domain, private_key_field.content);
|
||||
certificate_manager_.write_certificate(domain, certificate_field.content);
|
||||
} catch (const std::exception& e) {
|
||||
response_server_error(res, e.what(), httplib::StatusCode::InternalServerError_500);
|
||||
std::cout << "Failed to load certificates: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void bind_to_socket(httplib::Server &server, const std::string &socket_path) {
|
||||
unlink(socket_path.c_str()); // Remove existing socket file if it exists
|
||||
|
||||
server.set_address_family(AF_UNIX);
|
||||
|
||||
if (!server.bind_to_port(socket_path, 80)) {
|
||||
throw std::runtime_error("Failed to bind to socket");
|
||||
}
|
||||
|
||||
if (chmod(socket_path.c_str(), 0666) == -1) { // TODO Adjust permissions as needed
|
||||
throw std::runtime_error("Failed to set socket permissions");
|
||||
}
|
||||
}
|
||||
|
||||
void ControlServer::start() {
|
||||
setup_routes();
|
||||
bind_to_socket(server_, socket_path_);
|
||||
}
|
||||
|
||||
void ControlServer::listen() {
|
||||
if (!server_.listen_after_bind()) {
|
||||
throw std::runtime_error("Failed to start listening on socket");
|
||||
}
|
||||
}
|
||||
27
control-server/src/main.cpp
Normal file
27
control-server/src/main.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include "control_server.hpp"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 4) {
|
||||
std::cerr << "Usage: " << argv[0] << " <socket_path> <pid_file_path> <certificate_path>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* SOCKET_PATH = argv[1];
|
||||
const char* NGINX_PID_FILE_PATH = argv[2];
|
||||
const char* CERTIFICATE_PATH = argv[3];
|
||||
|
||||
NginxProcess nginx_process(NGINX_PID_FILE_PATH);
|
||||
CertificateManager certificate_manager(CERTIFICATE_PATH);
|
||||
ControlServer control_server(SOCKET_PATH, nginx_process, certificate_manager);
|
||||
|
||||
try {
|
||||
control_server.start();
|
||||
|
||||
std::cout << "Control server is listening on " << SOCKET_PATH << std::endl;
|
||||
|
||||
control_server.listen();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
34
control-server/src/nginx_process.cpp
Normal file
34
control-server/src/nginx_process.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#include "nginx_process.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
|
||||
NginxProcess::NginxProcess(const std::string& pid_file_path)
|
||||
: pid_file_path_(pid_file_path) {}
|
||||
|
||||
pid_t NginxProcess::read_pid() const {
|
||||
std::ifstream pid_file(pid_file_path_);
|
||||
if (!pid_file.is_open()) {
|
||||
throw std::runtime_error("Failed to open PID file: " + pid_file_path_);
|
||||
}
|
||||
|
||||
pid_t pid = -1;
|
||||
pid_file >> pid;
|
||||
|
||||
if (pid_file.fail() || pid <= 0) {
|
||||
pid_file.close();
|
||||
throw std::runtime_error("Invalid PID read from file");
|
||||
}
|
||||
|
||||
pid_file.close();
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
void NginxProcess::reload() const {
|
||||
pid_t pid = read_pid();
|
||||
|
||||
if (kill(pid, SIGHUP) == -1) {
|
||||
throw std::runtime_error("Failed to send SIGHUP to Nginx with PID: " + std::to_string(pid) + ". Reason: " + strerror(errno));
|
||||
}
|
||||
}
|
||||
|
|
@ -6,11 +6,13 @@ services:
|
|||
environment:
|
||||
DOMAIN: example.localhost
|
||||
SERVER_ID: development
|
||||
ACME_CHALLENGE_URL: http://localhost/acme-challenge
|
||||
ACME_CHALLENGE_URL: https://files.catbox.moe/xpfyfh
|
||||
CONTROL_DOMAIN: control.localhost
|
||||
CONTROL_TOKEN: Tr0ub4dor&3
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "443:443/udp"
|
||||
- "8080:80"
|
||||
- "8443:443"
|
||||
- "8443:443/udp"
|
||||
volumes:
|
||||
- ./run/html:/var/www/html
|
||||
- ./run/certs:/etc/ssl/certs
|
||||
|
|
|
|||
|
|
@ -1,17 +1,29 @@
|
|||
|
||||
#!/bin/sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ls -la /etc/ssl
|
||||
ls -la /etc/nginx
|
||||
: "${DOMAIN?Error: DOMAIN environment variable is not set.}"
|
||||
: "${CONTROL_DOMAIN?Error: CONTROL_DOMAIN environment variable is not set.}"
|
||||
|
||||
CERTIFICATE_ROOT="/etc/ssl"
|
||||
CERTIFICATE_DOMAIN_ROOT="$CERTIFICATE_ROOT/certs/$DOMAIN"
|
||||
|
||||
mkdir -p "$CERTIFICATE_DOMAIN_ROOT"
|
||||
setup_snakeoil_cert() {
|
||||
local domain="$1"
|
||||
local cert_dir="$CERTIFICATE_ROOT/certs/$domain"
|
||||
|
||||
cp -n "$CERTIFICATE_ROOT/snakeoil.pem" "$CERTIFICATE_DOMAIN_ROOT/fullchain.pem"
|
||||
cp -n "$CERTIFICATE_ROOT/snakeoil.key" "$CERTIFICATE_DOMAIN_ROOT/privkey.pem"
|
||||
mkdir -p "$cert_dir"
|
||||
|
||||
chmod 700 "$CERTIFICATE_DOMAIN_ROOT/fullchain.pem"
|
||||
chmod 700 "$CERTIFICATE_DOMAIN_ROOT/privkey.pem"
|
||||
cp -n "$CERTIFICATE_ROOT/snakeoil.pem" "$cert_dir/fullchain.pem"
|
||||
cp -n "$CERTIFICATE_ROOT/snakeoil.key" "$cert_dir/privkey.pem"
|
||||
|
||||
chmod 644 "$cert_dir/fullchain.pem"
|
||||
chmod 600 "$cert_dir/privkey.pem"
|
||||
}
|
||||
|
||||
for domain_to_setup in "$DOMAIN" "$CONTROL_DOMAIN"; do
|
||||
echo "Ensuring certificate for domain: $domain_to_setup"
|
||||
setup_snakeoil_cert "$domain_to_setup"
|
||||
done
|
||||
|
||||
echo "Placeholder certificate setup complete."
|
||||
9
docker-entrypoint.d/50-control-server.sh
Normal file
9
docker-entrypoint.d/50-control-server.sh
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CONTROL_SERVER_SOCKET="/var/run/control-server.sock"
|
||||
NGINX_PID_FILE="/var/run/nginx.pid"
|
||||
CERTIFICATE_PATH="/etc/ssl/certs"
|
||||
|
||||
control_server $CONTROL_SERVER_SOCKET $NGINX_PID_FILE $CERTIFICATE_PATH &
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "Starting reload watcher..."
|
||||
|
||||
/opt/scripts/watch-reload.sh &
|
||||
|
|
@ -17,9 +17,7 @@ http {
|
|||
'$status $body_bytes_sent bytes "$http_referer" '
|
||||
'"$http_x_forwarded_for"';
|
||||
|
||||
# While I removed PII from the above log format, still better not logging
|
||||
access_log /dev/null main; # /var/log/nginx/access.log main;
|
||||
|
||||
access_log /var/log/nginx/access.log main; # /dev/null to disable
|
||||
|
||||
server_tokens off;
|
||||
|
||||
|
|
@ -83,7 +81,8 @@ http {
|
|||
|
||||
server_name _;
|
||||
|
||||
return 444;
|
||||
default_type text/plain;
|
||||
return 200 "OK"; # 444 makes no sense here because we have sent the certificate already
|
||||
}
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
|
|
|
|||
21
nginx/templates/control.conf.template
Normal file
21
nginx/templates/control.conf.template
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
server {
|
||||
listen 443 ssl;
|
||||
listen 443 quic;
|
||||
listen [::]:443 ssl;
|
||||
listen [::]:443 quic;
|
||||
|
||||
server_name ${CONTROL_DOMAIN};
|
||||
|
||||
ssl_certificate /etc/ssl/certs/${CONTROL_DOMAIN}/fullchain.pem;
|
||||
ssl_certificate_key /etc/ssl/certs/${CONTROL_DOMAIN}/privkey.pem;
|
||||
|
||||
location / {
|
||||
default_type text/plain;
|
||||
|
||||
if ($http_authorization != "Bearer ${CONTROL_TOKEN}") {
|
||||
return 200 "OK"; # 444 makes no sense here because we have sent the certificate already
|
||||
}
|
||||
|
||||
proxy_pass http://unix:/var/run/control-server.sock;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,8 @@ server {
|
|||
root /var/www/html/${DOMAIN};
|
||||
index index.html;
|
||||
|
||||
location .well-known/acme-challenge {
|
||||
location /.well-known/acme-challenge {
|
||||
proxy_buffering off;
|
||||
proxy_pass ${ACME_CHALLENGE_URL};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
touch "$RELOAD_FILE"
|
||||
|
||||
# The loop will run once every time the file is saved.
|
||||
while inotifywait -e close_write "$RELOAD_FILE"; do
|
||||
echo "File '$RELOAD_FILE' changed. Reloading."
|
||||
if nginx -t; then
|
||||
nginx -s reload
|
||||
else
|
||||
echo "Nginx configuration is invalid. Skipping reload."
|
||||
fi
|
||||
done
|
||||
Loading…
Add table
Add a link
Reference in a new issue