fix(backend): Serve valid headers for HSTS and HSTS preload

Signed-off-by: eternal-flame-AD <yume@yumechi.jp>
This commit is contained in:
eternal-flame-AD 2024-10-19 02:31:41 -05:00
parent b1aac6acc3
commit 238fb2727d
No known key found for this signature in database
9 changed files with 75 additions and 5 deletions

View file

@ -174,6 +174,12 @@ id: 'aidx'
# Whether disable HSTS # Whether disable HSTS
#disableHsts: true #disableHsts: true
# Whether to enable HSTS preload
# Read these before enabling:
# - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security
# - https://hstspreload.org/
#hstsPreload: false
# Number of worker processes # Number of worker processes
#clusterLimit: 1 #clusterLimit: 1

View file

@ -168,6 +168,12 @@ id: 'aidx'
# Whether disable HSTS # Whether disable HSTS
#disableHsts: true #disableHsts: true
# Whether to enable HSTS preload
# Read these before enabling:
# - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security
# - https://hstspreload.org/
#hstsPreload: false
# Number of worker processes # Number of worker processes
#clusterLimit: 1 #clusterLimit: 1

View file

@ -250,6 +250,12 @@ id: 'aidx'
# Whether disable HSTS # Whether disable HSTS
#disableHsts: true #disableHsts: true
# Whether to enable HSTS preload
# Read these before enabling:
# - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security
# - https://hstspreload.org/
#hstsPreload: false
# Number of worker processes # Number of worker processes
#clusterLimit: 1 #clusterLimit: 1

View file

@ -161,6 +161,12 @@ id: 'aidx'
# Whether disable HSTS # Whether disable HSTS
#disableHsts: true #disableHsts: true
# Whether to enable HSTS preload
# Read these before enabling:
# - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security
# - https://hstspreload.org/
#hstsPreload: false
# Number of worker processes # Number of worker processes
#clusterLimit: 1 #clusterLimit: 1

View file

@ -182,6 +182,12 @@ id: "aidx"
# Whether disable HSTS # Whether disable HSTS
#disableHsts: true #disableHsts: true
# Whether to enable HSTS preload
# Read these before enabling:
# - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security
# - https://hstspreload.org/
#hstsPreload: false
# Number of worker processes # Number of worker processes
#clusterLimit: 1 #clusterLimit: 1

View file

@ -28,6 +28,7 @@ type Source = {
socket?: string; socket?: string;
chmodSocket?: string; chmodSocket?: string;
disableHsts?: boolean; disableHsts?: boolean;
hstsPreload?: boolean;
db: { db: {
host: string; host: string;
port: number; port: number;
@ -107,6 +108,7 @@ export type Config = {
socket: string | undefined; socket: string | undefined;
chmodSocket: string | undefined; chmodSocket: string | undefined;
disableHsts: boolean | undefined; disableHsts: boolean | undefined;
hstsPreload: boolean | undefined;
db: { db: {
host: string; host: string;
port: number; port: number;
@ -241,6 +243,7 @@ export function loadConfig(): Config {
socket: config.socket, socket: config.socket,
chmodSocket: config.chmodSocket, chmodSocket: config.chmodSocket,
disableHsts: config.disableHsts, disableHsts: config.disableHsts,
hstsPreload: config.hstsPreload ?? false,
host, host,
hostname, hostname,
scheme, scheme,

View file

@ -31,6 +31,7 @@ import { HealthServerService } from './HealthServerService.js';
import { ClientServerService } from './web/ClientServerService.js'; import { ClientServerService } from './web/ClientServerService.js';
import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; import { OpenApiServerService } from './api/openapi/OpenApiServerService.js';
import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
import { makeHstsHook } from './hsts.js';
const _dirname = fileURLToPath(new URL('.', import.meta.url)); const _dirname = fileURLToPath(new URL('.', import.meta.url));
@ -81,12 +82,10 @@ export class ServerService implements OnApplicationShutdown {
this.#fastify = fastify; this.#fastify = fastify;
// HSTS // HSTS
// 6months (15552000sec)
if (this.config.url.startsWith('https') && !this.config.disableHsts) { if (this.config.url.startsWith('https') && !this.config.disableHsts) {
fastify.addHook('onRequest', (request, reply, done) => { const preload = this.config.hstsPreload;
reply.header('strict-transport-security', 'max-age=15552000; preload'); const host = new URL(this.config.url).host;
done(); fastify.addHook('onRequest', makeHstsHook(host, preload));
});
} }
// Register raw-body parser for ActivityPub HTTP signature validation. // Register raw-body parser for ActivityPub HTTP signature validation.

View file

@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { onRequestHookHandler } from "fastify";
// 6months (15552000sec) by default, 1year (31536000sec) if preload is enabled
export function makeHstsHook(host: string, preload: boolean = false): onRequestHookHandler {
if (preload) {
return (request, reply, done) => {
// we must permanent redirect http to https for preload to be eligible
// however we do not want to do this if a reverse proxy is detected
if (
(request.host === host || request.hostname === host) &&
(request.headers['x-forwarded-proto'] ?? request.protocol) === 'http') {
reply.redirect(`https://${request.hostname}${request.url}`, 301);
} else {
// 1 year is mandatory for preload
reply.header('strict-transport-security', 'max-age=31536000; includeSubDomains; preload');
}
done();
}
} else {
return (request, reply, done) => {
reply.header('strict-transport-security', 'max-age=15552000');
done();
}
}
}

View file

@ -52,6 +52,7 @@ import { FeedService } from './FeedService.js';
import { UrlPreviewService } from './UrlPreviewService.js'; import { UrlPreviewService } from './UrlPreviewService.js';
import { ClientLoggerService } from './ClientLoggerService.js'; import { ClientLoggerService } from './ClientLoggerService.js';
import type { FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify'; import type { FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify';
import { makeHstsHook } from '../hsts.js';
const _filename = fileURLToPath(import.meta.url); const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename); const _dirname = dirname(_filename);
@ -207,6 +208,13 @@ export class ClientServerService {
//#region Bull Dashboard //#region Bull Dashboard
const bullBoardPath = '/queue'; const bullBoardPath = '/queue';
// HSTS
if (this.config.url.startsWith('https') && !this.config.disableHsts) {
const preload = this.config.hstsPreload;
const host = new URL(this.config.url).host;
fastify.addHook('onRequest', makeHstsHook(host, preload));
}
// Authenticate // Authenticate
fastify.addHook('onRequest', async (request, reply) => { fastify.addHook('onRequest', async (request, reply) => {
if (request.routeOptions.url == null) { if (request.routeOptions.url == null) {