mirror of
https://github.com/emmett-framework/granian.git
synced 2025-08-04 17:08:02 +00:00
Add support for encrypted SSL keys (#386)
This commit is contained in:
parent
2c982303a9
commit
033d9e5ffc
14 changed files with 322 additions and 24 deletions
191
Cargo.lock
generated
191
Cargo.lock
generated
|
@ -17,6 +17,17 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.86"
|
||||
|
@ -62,6 +73,12 @@ version = "0.22.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
|
@ -77,6 +94,15 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
|
@ -89,6 +115,15 @@ version = "1.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
|
||||
[[package]]
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.7"
|
||||
|
@ -101,6 +136,22 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
|
@ -141,6 +192,16 @@ version = "2.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
@ -149,6 +210,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -298,8 +360,10 @@ dependencies = [
|
|||
"itertools",
|
||||
"log",
|
||||
"mimalloc",
|
||||
"pem",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"pkcs8",
|
||||
"pyo3",
|
||||
"pyo3-log",
|
||||
"rustls-pemfile",
|
||||
|
@ -349,6 +413,15 @@ version = "0.3.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.1.0"
|
||||
|
@ -446,6 +519,16 @@ version = "2.0.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
|
@ -576,6 +659,26 @@ dependencies = [
|
|||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
|
@ -614,6 +717,33 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs5"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"cbc",
|
||||
"der",
|
||||
"pbkdf2",
|
||||
"scrypt",
|
||||
"sha2",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs5",
|
||||
"rand_core",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.7.0"
|
||||
|
@ -833,12 +963,52 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "salsa20"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "scrypt"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
|
||||
dependencies = [
|
||||
"pbkdf2",
|
||||
"salsa20",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.208"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.208"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
|
@ -850,6 +1020,17 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
|
@ -890,6 +1071,16 @@ version = "0.9.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
|
|
@ -39,8 +39,10 @@ hyper = { version = "=1.4", features = ["http1", "http2", "server"] }
|
|||
hyper-util = { version = "=0.1", features = ["server-auto", "tokio"] }
|
||||
itertools = "0.13"
|
||||
log = "0.4"
|
||||
pem = "=3.0"
|
||||
percent-encoding = "=2.3"
|
||||
pin-project = "1.1"
|
||||
pkcs8 = { version = "=0.10", features = ["encryption", "pkcs5"] }
|
||||
pyo3 = { version = "=0.22", features = ["anyhow", "extension-module", "generate-import-lib"] }
|
||||
pyo3-log = { version = "=0.11", git = "https://github.com/vorner/pyo3-log", rev = "b62a250533" }
|
||||
rustls-pemfile = "2.1"
|
||||
|
|
|
@ -176,9 +176,11 @@ Options:
|
|||
(disabled)]
|
||||
--access-log-fmt TEXT Access log format [env var:
|
||||
GRANIAN_LOG_ACCESS_FMT]
|
||||
--ssl-keyfile FILE SSL key file [env var: GRANIAN_SSL_KEYFILE]
|
||||
--ssl-certificate FILE SSL certificate file [env var:
|
||||
GRANIAN_SSL_CERTIFICATE]
|
||||
--ssl-keyfile FILE SSL key file [env var: GRANIAN_SSL_KEYFILE]
|
||||
--ssl-keyfile-password TEXT SSL key password [env var:
|
||||
GRANIAN_SSL_KEYFILE_PASSWORD]
|
||||
--url-path-prefix TEXT URL path prefix the app is mounted on [env
|
||||
var: GRANIAN_URL_PATH_PREFIX]
|
||||
--respawn-failed-workers / --no-respawn-failed-workers
|
||||
|
|
|
@ -168,16 +168,17 @@ def option(*param_decls: str, cls: Optional[Type[click.Option]] = None, **attrs:
|
|||
)
|
||||
@option('--access-log/--no-access-log', 'log_access_enabled', default=False, help='Enable access log')
|
||||
@option('--access-log-fmt', 'log_access_fmt', help='Access log format')
|
||||
@option(
|
||||
'--ssl-keyfile',
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, path_type=pathlib.Path),
|
||||
help='SSL key file',
|
||||
)
|
||||
@option(
|
||||
'--ssl-certificate',
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, path_type=pathlib.Path),
|
||||
help='SSL certificate file',
|
||||
)
|
||||
@option(
|
||||
'--ssl-keyfile',
|
||||
type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, path_type=pathlib.Path),
|
||||
help='SSL key file',
|
||||
)
|
||||
@option('--ssl-keyfile-password', help='SSL key password')
|
||||
@option('--url-path-prefix', help='URL path prefix the app is mounted on')
|
||||
@option(
|
||||
'--respawn-failed-workers/--no-respawn-failed-workers',
|
||||
|
@ -270,8 +271,9 @@ def cli(
|
|||
log_access_fmt: Optional[str],
|
||||
log_level: LogLevels,
|
||||
log_config: Optional[pathlib.Path],
|
||||
ssl_keyfile: Optional[pathlib.Path],
|
||||
ssl_certificate: Optional[pathlib.Path],
|
||||
ssl_keyfile: Optional[pathlib.Path],
|
||||
ssl_keyfile_password: Optional[str],
|
||||
url_path_prefix: Optional[str],
|
||||
respawn_failed_workers: bool,
|
||||
respawn_interval: float,
|
||||
|
@ -329,6 +331,7 @@ def cli(
|
|||
log_access_format=log_access_fmt,
|
||||
ssl_cert=ssl_certificate,
|
||||
ssl_key=ssl_keyfile,
|
||||
ssl_key_password=ssl_keyfile_password,
|
||||
url_path_prefix=url_path_prefix,
|
||||
respawn_failed_workers=respawn_failed_workers,
|
||||
respawn_interval=respawn_interval,
|
||||
|
|
|
@ -92,6 +92,7 @@ class Granian:
|
|||
log_access_format: Optional[str] = None,
|
||||
ssl_cert: Optional[Path] = None,
|
||||
ssl_key: Optional[Path] = None,
|
||||
ssl_key_password: Optional[str] = None,
|
||||
url_path_prefix: Optional[str] = None,
|
||||
respawn_failed_workers: bool = False,
|
||||
respawn_interval: float = 3.5,
|
||||
|
@ -147,7 +148,7 @@ class Granian:
|
|||
|
||||
configure_logging(self.log_level, self.log_config, self.log_enabled)
|
||||
|
||||
self.build_ssl_context(ssl_cert, ssl_key)
|
||||
self.build_ssl_context(ssl_cert, ssl_key, ssl_key_password)
|
||||
self._shd = None
|
||||
self._sfd = None
|
||||
self.procs: List[Worker] = []
|
||||
|
@ -159,17 +160,17 @@ class Granian:
|
|||
self.lifetime_signal = False
|
||||
self.pid = None
|
||||
|
||||
def build_ssl_context(self, cert: Optional[Path], key: Optional[Path]):
|
||||
def build_ssl_context(self, cert: Optional[Path], key: Optional[Path], password: Optional[str]):
|
||||
if not (cert and key):
|
||||
self.ssl_ctx = (False, None, None)
|
||||
return
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
ctx.load_cert_chain(cert, key, None)
|
||||
ctx.load_cert_chain(cert, key, password)
|
||||
# with cert.open("rb") as f:
|
||||
# cert_contents = f.read()
|
||||
# with key.open("rb") as f:
|
||||
# key_contents = f.read()
|
||||
self.ssl_ctx = (True, str(cert.resolve()), str(key.resolve()))
|
||||
self.ssl_ctx = (True, str(cert.resolve()), str(key.resolve()), password)
|
||||
|
||||
@staticmethod
|
||||
def _spawn_asgi_worker(
|
||||
|
@ -191,7 +192,7 @@ class Granian:
|
|||
log_level: LogLevels,
|
||||
log_config: Dict[str, Any],
|
||||
log_access_fmt: Optional[str],
|
||||
ssl_ctx: Tuple[bool, Optional[str], Optional[str]],
|
||||
ssl_ctx: Tuple[bool, Optional[str], Optional[str], Optional[str]],
|
||||
scope_opts: Dict[str, Any],
|
||||
):
|
||||
from granian._loops import loops, set_loop_signals
|
||||
|
@ -246,7 +247,7 @@ class Granian:
|
|||
log_level: LogLevels,
|
||||
log_config: Dict[str, Any],
|
||||
log_access_fmt: Optional[str],
|
||||
ssl_ctx: Tuple[bool, Optional[str], Optional[str]],
|
||||
ssl_ctx: Tuple[bool, Optional[str], Optional[str], Optional[str]],
|
||||
scope_opts: Dict[str, Any],
|
||||
):
|
||||
from granian._loops import loops, set_loop_signals
|
||||
|
@ -308,7 +309,7 @@ class Granian:
|
|||
log_level: LogLevels,
|
||||
log_config: Dict[str, Any],
|
||||
log_access_fmt: Optional[str],
|
||||
ssl_ctx: Tuple[bool, Optional[str], Optional[str]],
|
||||
ssl_ctx: Tuple[bool, Optional[str], Optional[str], Optional[str]],
|
||||
scope_opts: Dict[str, Any],
|
||||
):
|
||||
from granian._loops import loops, set_loop_signals
|
||||
|
@ -374,7 +375,7 @@ class Granian:
|
|||
log_level: LogLevels,
|
||||
log_config: Dict[str, Any],
|
||||
log_access_fmt: Optional[str],
|
||||
ssl_ctx: Tuple[bool, Optional[str], Optional[str]],
|
||||
ssl_ctx: Tuple[bool, Optional[str], Optional[str], Optional[str]],
|
||||
scope_opts: Dict[str, Any],
|
||||
):
|
||||
from granian._loops import loops, set_loop_signals
|
||||
|
|
|
@ -46,7 +46,8 @@ impl ASGIWorker {
|
|||
opt_enabled=true,
|
||||
ssl_enabled=false,
|
||||
ssl_cert=None,
|
||||
ssl_key=None
|
||||
ssl_key=None,
|
||||
ssl_key_password=None
|
||||
)
|
||||
)]
|
||||
fn new(
|
||||
|
@ -64,6 +65,7 @@ impl ASGIWorker {
|
|||
ssl_enabled: bool,
|
||||
ssl_cert: Option<&str>,
|
||||
ssl_key: Option<&str>,
|
||||
ssl_key_password: Option<&str>,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
config: WorkerConfig::new(
|
||||
|
@ -80,6 +82,7 @@ impl ASGIWorker {
|
|||
ssl_enabled,
|
||||
ssl_cert,
|
||||
ssl_key,
|
||||
ssl_key_password,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -46,7 +46,8 @@ impl RSGIWorker {
|
|||
opt_enabled=true,
|
||||
ssl_enabled=false,
|
||||
ssl_cert=None,
|
||||
ssl_key=None
|
||||
ssl_key=None,
|
||||
ssl_key_password=None
|
||||
)
|
||||
)]
|
||||
fn new(
|
||||
|
@ -64,6 +65,7 @@ impl RSGIWorker {
|
|||
ssl_enabled: bool,
|
||||
ssl_cert: Option<&str>,
|
||||
ssl_key: Option<&str>,
|
||||
ssl_key_password: Option<&str>,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
config: WorkerConfig::new(
|
||||
|
@ -80,6 +82,7 @@ impl RSGIWorker {
|
|||
ssl_enabled,
|
||||
ssl_cert,
|
||||
ssl_key,
|
||||
ssl_key_password,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
|
27
src/tls.rs
27
src/tls.rs
|
@ -1,3 +1,4 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use std::{
|
||||
fs, io,
|
||||
iter::Iterator,
|
||||
|
@ -18,7 +19,7 @@ use tls_listener::{
|
|||
pub(crate) fn tls_listener(
|
||||
config: Arc<ServerConfig>,
|
||||
tcp: TcpListener,
|
||||
) -> anyhow::Result<(TlsListener<tokio::net::TcpListener, TlsAcceptor>, SocketAddr)> {
|
||||
) -> Result<(TlsListener<tokio::net::TcpListener, TlsAcceptor>, SocketAddr)> {
|
||||
let tcp_listener = tokio::net::TcpListener::from_std(tcp).unwrap();
|
||||
let local_addr = tcp_listener.local_addr()?;
|
||||
let listener = TlsListener::new(TlsAcceptor::from(config), tcp_listener);
|
||||
|
@ -29,6 +30,26 @@ pub(crate) fn load_certs(filename: String) -> io::Result<Vec<Certificate<'static
|
|||
rustls_pemfile::certs(&mut io::BufReader::new(fs::File::open(filename)?)).collect()
|
||||
}
|
||||
|
||||
pub(crate) fn load_private_key(filename: String) -> io::Result<PrivateKey<'static>> {
|
||||
rustls_pemfile::private_key(&mut io::BufReader::new(fs::File::open(filename)?)).map(std::option::Option::unwrap)
|
||||
pub(crate) fn load_private_key(filename: String, password: Option<String>) -> Result<PrivateKey<'static>> {
|
||||
match &password {
|
||||
Some(pwd) => {
|
||||
let expected_tag = "ENCRYPTED PRIVATE KEY";
|
||||
let content = fs::read(filename)?;
|
||||
let sections = pem::parse_many(content)?;
|
||||
let mut iter = sections
|
||||
.into_iter()
|
||||
.filter(|v| v.tag() == expected_tag)
|
||||
.map(|v| v.contents().to_vec());
|
||||
let key = pkcs8::EncryptedPrivateKeyInfo::try_from(
|
||||
iter.next().map_or_else(|| Err(anyhow!("Invalid key")), Ok)?.as_slice(),
|
||||
)
|
||||
.map_err(|_| anyhow!("Invalid key"))?
|
||||
.decrypt(pwd)
|
||||
.map_err(|_| anyhow!("Cannot decrypt key"))?;
|
||||
Ok(PrivateKey::Pkcs8(key.to_bytes().to_vec().into()))
|
||||
}
|
||||
None => rustls_pemfile::private_key(&mut io::BufReader::new(fs::File::open(filename)?))
|
||||
.map_err(std::convert::Into::into)
|
||||
.map(std::option::Option::unwrap),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ pub(crate) struct WorkerConfig {
|
|||
pub ssl_enabled: bool,
|
||||
ssl_cert: Option<String>,
|
||||
ssl_key: Option<String>,
|
||||
ssl_key_password: Option<String>,
|
||||
}
|
||||
|
||||
impl WorkerConfig {
|
||||
|
@ -85,6 +86,7 @@ impl WorkerConfig {
|
|||
ssl_enabled: bool,
|
||||
ssl_cert: Option<&str>,
|
||||
ssl_key: Option<&str>,
|
||||
ssl_key_password: Option<&str>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
|
@ -100,6 +102,7 @@ impl WorkerConfig {
|
|||
ssl_enabled,
|
||||
ssl_cert: ssl_cert.map(std::convert::Into::into),
|
||||
ssl_key: ssl_key.map(std::convert::Into::into),
|
||||
ssl_key_password: ssl_key_password.map(std::convert::Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +125,7 @@ impl WorkerConfig {
|
|||
.with_no_client_auth()
|
||||
.with_single_cert(
|
||||
tls_load_certs(self.ssl_cert.clone().unwrap()).unwrap(),
|
||||
tls_load_pkey(self.ssl_key.clone().unwrap()).unwrap(),
|
||||
tls_load_pkey(self.ssl_key.clone().unwrap(), self.ssl_key_password.clone()).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
cfg.alpn_protocols = match &self.http_mode[..] {
|
||||
|
|
|
@ -32,7 +32,8 @@ impl WSGIWorker {
|
|||
http2_opts=None,
|
||||
ssl_enabled=false,
|
||||
ssl_cert=None,
|
||||
ssl_key=None
|
||||
ssl_key=None,
|
||||
ssl_key_password=None
|
||||
)
|
||||
)]
|
||||
fn new(
|
||||
|
@ -48,6 +49,7 @@ impl WSGIWorker {
|
|||
ssl_enabled: bool,
|
||||
ssl_cert: Option<&str>,
|
||||
ssl_key: Option<&str>,
|
||||
ssl_key_password: Option<&str>,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
config: WorkerConfig::new(
|
||||
|
@ -64,6 +66,7 @@ impl WSGIWorker {
|
|||
ssl_enabled,
|
||||
ssl_cert,
|
||||
ssl_key,
|
||||
ssl_key_password,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -26,8 +26,13 @@ async def _server(interface, port, threading_mode, tls=False):
|
|||
'loop_opt': bool(os.getenv('LOOP_OPT')),
|
||||
}
|
||||
if tls:
|
||||
kwargs['ssl_cert'] = certs_path / 'cert.pem'
|
||||
kwargs['ssl_key'] = certs_path / 'key.pem'
|
||||
if tls == 'private':
|
||||
kwargs['ssl_cert'] = certs_path / 'pcert.pem'
|
||||
kwargs['ssl_key'] = certs_path / 'pkey.pem'
|
||||
kwargs['ssl_key_password'] = 'foobar' # noqa: S105
|
||||
else:
|
||||
kwargs['ssl_cert'] = certs_path / 'cert.pem'
|
||||
kwargs['ssl_key'] = certs_path / 'key.pem'
|
||||
|
||||
succeeded, spawn_failures = False, 0
|
||||
while spawn_failures < 3:
|
||||
|
|
21
tests/fixtures/tls/pcert.pem
vendored
Normal file
21
tests/fixtures/tls/pcert.pem
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDazCCAlOgAwIBAgIUSpxGABYdWSF5SL/UUy+nSERO82IwDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA4MzExNjM5NTNaFw0zNDA4
|
||||
MjkxNjM5NTNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQCVu4Y8vVxnRL1Ve+v/WdnkoETwxOeXxS8eFhknh5Td
|
||||
kk9pUQLVvOgl/xoHEXbqtllqoAJBvMipFN/2drzfPQdES+TFg89Z5z4fzVMK5LDU
|
||||
0D5NTrPIDooHKBD9VF8CKoWQE0bqsyE7VezL550lconK5gQBT1qVNmy3uJDvTdN5
|
||||
+SAxa1VlIXnfzl56HN4lEjGwwMIku2hTIE/g2Np4mY+753TP3+lJEMJV0MC7Jm+p
|
||||
0E/K30eea6vHRekfanASFGUTQuYTi1MRHOhG6mSgBcLrjhkesibCVhIcojwouqw0
|
||||
rHISnTwdVnNgsHYYZwXzyG90B43PiQhiOtQvCzVOyrWnAgMBAAGjUzBRMB0GA1Ud
|
||||
DgQWBBQcVutKZhcuW/oKo/nuHnkiz7h2rjAfBgNVHSMEGDAWgBQcVutKZhcuW/oK
|
||||
o/nuHnkiz7h2rjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBt
|
||||
xs3aozUrPXcJFZpobAWyAwLO2TRLcD2wwyNLDeeCDoKD2c+SQY7FMv7nQZ7C2Jpl
|
||||
DE5h7PedD1OAHLXTbsNpXMCAEnHewMO0UCsHl6Er/8f+KuCcBWVcm1Xbv5ksF5sA
|
||||
KrbDpNsQVRwDqhwVJxvZ0YfHnr4+UheimT0KzI9zZKsl7FaIrAUm6Gxh/6EYOhAG
|
||||
sC5OTtK5OYk/uafHEoScxM+hx+QabEsCb48QgU3pmlGSrp3g1zxEE30spIk7ek3g
|
||||
1TP/Gf+RcdzDYODZHZGms0aKDXluVn+bIarUoZ4LJ6wtJC+s7yQWBjrzWUJ4jDkY
|
||||
Dcgo5IV2CEL7Npy4dI40
|
||||
-----END CERTIFICATE-----
|
30
tests/fixtures/tls/pkey.pem
vendored
Normal file
30
tests/fixtures/tls/pkey.pem
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
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
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
|
@ -47,3 +47,13 @@ async def test_rsgi_ws_scope(rsgi_server, threading_mode):
|
|||
|
||||
data = json.loads(res)
|
||||
assert data['scheme'] == 'https'
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tls_encrypted_key(rsgi_server):
|
||||
async with rsgi_server('workers', tls='priv') as port:
|
||||
res = httpx.get(f'https://localhost:{port}/info?test=true', verify=False)
|
||||
|
||||
assert res.status_code == 200
|
||||
data = res.json()
|
||||
assert data['scheme'] == 'https'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue