Christian Risi 3eda2cf0a1 TODO: add Brokers for both SSLTerminationFS and Manual, complete broker manager init
Co-authored-by: Oscar Urselli <oscar0urselli@users.noreply.github.com>
2025-07-03 21:34:58 +00:00

291 lines
7.2 KiB
TypeScript

import { EndpointType } from "$lib/server/enums/endpoints"
import { httpVersion, protocolToString, proxyProtocol, secureProtocol, stringToProtocol, type NginxProtocol } from "$lib/server/enums/protocols"
import { validatePort } from "$lib/server/utils/ports-utils"
import type { Stats } from "fs"
import type { FSHeader, IEndpointFS } from "./endpoints"
import { createHeader, parseDefaultHeader, parseGenericHeader } from "../utils"
import { hashUtil } from "$lib/server/utils/filesystem-utils"
import type { IEndpoint } from "$lib/server/classes/endpoints/endpoints-interfaces"
import { SSLTermination } from "$lib/server/classes/endpoints/ssl-termination-endpoint"
// TODO: add broker implementation
export class SSLTerminationFS implements IEndpointFS {
private static __type = EndpointType.SSL_TERMINATION
public get type() {
return SSLTerminationFS.__type
}
public name: string
public stats: Stats
public get path() {
return `${this.protocol}/name.conf`
}
public hash: string
public sslPort: number
public clearPort: number
public servicePort: number
public serviceEndpoint: string
public protocol: NginxProtocol
public certificateURI: string
public privateKeyURI: string
constructor(
name: string,
stats: Stats,
hash: string,
sslPort: number,
clearPort: number,
servicePort: number,
serviceEndpoint: string,
protocol: NginxProtocol,
certificateURI: string,
privateKeyURI: string
) {
this.name = name
this.stats = stats
this.hash = hash
this.sslPort = sslPort
this.clearPort = clearPort
this.servicePort = servicePort
this.serviceEndpoint = serviceEndpoint
this.protocol = protocol
this.certificateURI = certificateURI
this.privateKeyURI = privateKeyURI
}
public toIEndpoint(): IEndpoint {
return new SSLTermination(
this.name,
this.path,
this.sslPort,
this.clearPort,
this.servicePort,
this.serviceEndpoint,
this.protocol,
this.certificateURI,
this.privateKeyURI
)
}
headerHash(): string {
return hashUtil(
this.createHeader()
)
}
toConf(): string {
const HEADER = this.createHeader()
const SSL_SERVER = this.createSSLServer()
const CLEAR_SERVER = this.createClearServer()
const CONF = HEADER + SSL_SERVER + CLEAR_SERVER
return CONF
}
ports(): number[] {
return [
this.sslPort,
this.clearPort
]
}
public static parseConf(fsHeader: FSHeader, conf: string): SSLTerminationFS {
// TODO: parse header
const defHeader = parseDefaultHeader(conf)
const keyValue = parseGenericHeader(conf)
const name = keyValue.get("NAME")
if (!name) {
throw new Error("Could not parse")
}
const protocol = stringToProtocol(
keyValue.get("PROTOCOL") ?? ""
)
const sslPort = Number.parseInt(
keyValue.get(
"SSL_PORT"
) ?? ""
)
validatePort(sslPort)
const clearPort = Number.parseInt(
keyValue.get(
"CLEAR_PORT"
) ?? ""
)
validatePort(clearPort)
const servicePort = Number.parseInt(
keyValue.get(
"SERVICE_PORT"
) ?? ""
)
validatePort(servicePort)
const serviceEndpoint = keyValue.get("SERVICE_ENDPOINT")
if (!serviceEndpoint) {
throw new Error("Could not parse")
}
const certificateURI = keyValue.get("CERTIFICATE_PATH")
if (!certificateURI) {
throw new Error("Could not parse")
}
const privateKeyURI = keyValue.get("KEY_PATH")
if (!privateKeyURI) {
throw new Error("Could not parse")
}
return new SSLTerminationFS(
name,
fsHeader.stats,
fsHeader.hash,
sslPort,
clearPort,
servicePort,
serviceEndpoint,
protocol,
certificateURI,
privateKeyURI
)
}
private createHeader() {
return createHeader(
this,
[
{
key: "NAME",
value: this.name
},
{
key: "PROTOCOL",
value: protocolToString(this.protocol)
},
{
key: "SSL_PORT",
value: this.sslPort
},
{
key: "CLEAR_PORT",
value: this.clearPort
},
{
key: "SERVICE_PORT",
value: this.servicePort
},
{
key: "SERVICE_ENDPOINT",
value: this.serviceEndpoint
},
{
key: "CERTIFICATE_PATH",
value: this.certificateURI
},
{
key: "KEY_PATH",
value: this.privateKeyURI
}
]
)
}
// UGLY: refactor into a flexible method
private createSSLServer() {
const CLEAR_PROTOCOL = `${this.protocol}`
const HTTP_VERSION = httpVersion(this.protocol)
const PROXY_OPTION = proxyProtocol(this.protocol)
// UGLY: put to constants
let conf = [
"server {\n",
"\tmore_clear_headers Server;\n",
`\tlisten ${this.sslPort};`
]
if (HTTP_VERSION !== 1) {
conf.push(
`\thttp${HTTP_VERSION} on;`
)
}
// TODO: check if we should support less protocols
conf.push(
"\n",
"\tlocation / {",
`\t\t${PROXY_OPTION} ${CLEAR_PROTOCOL}://127.0.0.1:${this.clearPort};`,
"\t}",
"\n",
`ssl_certificate ${this.certificateURI};`,
`ssl_certificate_key ${this.privateKeyURI};`,
"\tssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;",
"\n",
"}"
)
return conf.join("\n")
}
private createClearServer() {
const SSL_PROTOCOL = secureProtocol(this.protocol)
const HTTP_VERSION = httpVersion(this.protocol)
const PROXY_OPTION = proxyProtocol(this.protocol)
// UGLY: put to constants
let conf = [
"server {\n",
"\tmore_clear_headers Server;\n",
`\tlisten ${this.clearPort};`
]
if (HTTP_VERSION !== 1) {
conf.push(
`\thttp${HTTP_VERSION} on;`
)
}
// TODO: check if we should support less protocols
conf.push(
"\n",
"\tlocation / {",
`\t\t${PROXY_OPTION} ${SSL_PROTOCOL}://${this.serviceEndpoint}:${this.servicePort};`,
"\t}",
"\n",
"}"
)
return conf.join("\n")
}
}