continuous-integration/drone/pr Build is passing
HTTPS_PORT — второй Netty-сервер с тем же HttpHandler и тем же сертификатом, что SMTP/IMAP (Let's Encrypt от acme, перечитывается при продлении). HSTS добавляет Spring Security. docker-compose.public.yml: 443 → 8443. HTTP 8080 остаётся для прокси внутри сети. Тесты: HttpsConfigTest, всего 74; живой запуск: /api/config по HTTPS, HSTS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
82 lines
3.1 KiB
Java
82 lines
3.1 KiB
Java
package com.mailserver.config;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
|
import org.springframework.context.SmartLifecycle;
|
|
import org.springframework.context.annotation.Bean;
|
|
import org.springframework.context.annotation.Configuration;
|
|
import org.springframework.http.server.reactive.HttpHandler;
|
|
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
|
|
import reactor.netty.DisposableServer;
|
|
import reactor.netty.http.server.HttpServer;
|
|
|
|
import java.time.Duration;
|
|
|
|
/**
|
|
* The web client and API over HTTPS on the mail host itself (mailserver.https.port, in public compose 8443 →
|
|
* published 443): https://mail.jambocheck.com opens the web mail, no second name through Jambotron's Caddy.
|
|
* Same certificate as SMTP/IMAP (Let's Encrypt from the acme service, reloaded when renewed). The plain HTTP port
|
|
* (8080) stays for a reverse proxy inside the network (staging: domain in Jambotron).
|
|
*/
|
|
@Configuration
|
|
public class HttpsConfig {
|
|
|
|
@Bean
|
|
@ConditionalOnExpression("${mailserver.https.port:0} > 0 and ${mailserver.tls.enabled:true}")
|
|
public HttpsServer httpsServer(HttpHandler handler, MailServerConfig.SmtpTlsContext tls,
|
|
@Value("${mailserver.smtp.host:0.0.0.0}") String host,
|
|
@Value("${mailserver.https.port}") int port) {
|
|
return new HttpsServer(handler, tls, host, port);
|
|
}
|
|
|
|
public static class HttpsServer implements SmartLifecycle {
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(HttpsServer.class);
|
|
|
|
private final HttpHandler handler;
|
|
private final MailServerConfig.SmtpTlsContext tls;
|
|
private final String host;
|
|
private final int port;
|
|
private volatile DisposableServer server;
|
|
|
|
HttpsServer(HttpHandler handler, MailServerConfig.SmtpTlsContext tls, String host, int port) {
|
|
this.handler = handler;
|
|
this.tls = tls;
|
|
this.host = host;
|
|
this.port = port;
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
server = HttpServer.create()
|
|
.host(host)
|
|
.port(port)
|
|
.secure(spec -> spec.sslContext(tls.context()))
|
|
// HSTS: Spring Security adds it for HTTPS requests by itself
|
|
.handle(new ReactorHttpHandlerAdapter(handler))
|
|
.bindNow(Duration.ofSeconds(30));
|
|
log.info("HTTPS (web client + API) listening on {}:{}", host, server.port());
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
DisposableServer current = server;
|
|
if (current != null) {
|
|
current.disposeNow(Duration.ofSeconds(10));
|
|
server = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isRunning() {
|
|
return server != null;
|
|
}
|
|
|
|
public int port() {
|
|
return server == null ? -1 : server.port();
|
|
}
|
|
}
|
|
}
|