mirror of
https://seed.flo-the.dev/z3gWc1qgaeZaoGwL4WTstLNoqjayM.git
synced 2025-12-06 12:57:35 +01:00
Compare commits
16 Commits
f59934cfd0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 267ba1e25d | |||
| 715008e3da | |||
| 1cb073b7cd | |||
| 99579a27e7 | |||
| 1470e69a4b | |||
|
e5460e1629
|
|||
|
e2ad2fc15d
|
|||
|
c9e398eb7f
|
|||
|
d3055a9e8b
|
|||
|
b306ecd228
|
|||
|
f40359baec
|
|||
|
e860cf24f1
|
|||
|
d00a45849e
|
|||
|
9f4273163a
|
|||
|
0494812be5
|
|||
|
84bf6e4712
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ config.ini
|
|||||||
*.pfx
|
*.pfx
|
||||||
*.crt
|
*.crt
|
||||||
*.pem
|
*.pem
|
||||||
|
*.pw
|
||||||
!tests/privkey.pem
|
!tests/privkey.pem
|
||||||
!tests/signer.pem
|
!tests/signer.pem
|
||||||
|
|
||||||
|
|||||||
1
.gitsigners
Normal file
1
.gitsigners
Normal file
@@ -0,0 +1 @@
|
|||||||
|
florian.brandes@posteo.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICGTgYytxH0XAlMlxc7A+OYUcQhmG7AXRui+Gxhsy0oq
|
||||||
41
Dockerfile
Normal file
41
Dockerfile
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM python:3.13.8-slim as base
|
||||||
|
# Prevents Python from writing pyc files.
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
# Keeps Python from buffering stdout and stderr to avoid situations where
|
||||||
|
# the application crashes without emitting any logs due to buffering.
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Create a non-privileged user that the app will run under.
|
||||||
|
# See https://docs.docker.com/go/dockerfile-user-best-practices/
|
||||||
|
ARG UID=10001
|
||||||
|
RUN adduser \
|
||||||
|
--disabled-password \
|
||||||
|
--gecos "" \
|
||||||
|
--home "/nonexistent" \
|
||||||
|
--shell "/sbin/nologin" \
|
||||||
|
--no-create-home \
|
||||||
|
--uid "${UID}" \
|
||||||
|
appuser
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y build-essential libssl-dev python3-dev swig
|
||||||
|
|
||||||
|
# Download dependencies as a separate step to take advantage of Docker's caching.
|
||||||
|
# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
|
||||||
|
# Leverage a bind mount to requirements.txt to avoid having to copy them into
|
||||||
|
# into this layer.
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||||
|
--mount=type=bind,source=requirements.txt,target=requirements.txt \
|
||||||
|
python -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Switch to the non-privileged user to run the application.
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
ADD smtprd_ng /app/smtprd_ng
|
||||||
|
|
||||||
|
EXPOSE 8025
|
||||||
|
CMD python smtprd_ng/smtprd.py --config /app/config.ini
|
||||||
66
README.md
66
README.md
@@ -11,13 +11,15 @@ This can also for example use `msmtp`. This way scripts can use `sendmail` which
|
|||||||
|
|
||||||
Additionally, we can sign and encrypt the emails with S/MIME certificates, which adds a layer of authentification and security for automated information delivery (think security notifications, logs, etc.)
|
Additionally, we can sign and encrypt the emails with S/MIME certificates, which adds a layer of authentification and security for automated information delivery (think security notifications, logs, etc.)
|
||||||
|
|
||||||
|
Please note: This will only forward emails to email addresses specified in `config.ini`, so it is not useful as a general SMTP-relay (like `msmtp`) but only for a predefined email set. This is by design.
|
||||||
|
|
||||||
For now, this is a proof-of-concept.
|
For now, this is a proof-of-concept.
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Relay emails received on a locally listening SMTP server
|
- Relay emails received on a locally listening SMTP server
|
||||||
- Encrypt and sign the relayed mails (experimental)
|
- Encrypt and sign the relayed mails
|
||||||
|
|
||||||
|
|
||||||
## Installation/Hacking
|
## Installation/Hacking
|
||||||
@@ -49,6 +51,17 @@ Note: `build` must be installed. You should use a `venv` for this.
|
|||||||
|
|
||||||
Clone this repo and run `devenv shell`
|
Clone this repo and run `devenv shell`
|
||||||
|
|
||||||
|
### docker
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1. Clone this repo
|
||||||
|
2. `cp config.example config.ini`
|
||||||
|
3. edit `config.ini` as you need it. I'd suggest using `/app/pw` as password file and bild-mounting it.
|
||||||
|
4. `docker build -t smtprd-ng:latest .`
|
||||||
|
5. `docker run -p 8025:8025 -v ./config.ini:/app/config.ini -v ./pw:/app/pw --rm -it smtprd-ng:latest`
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Contributions are always welcome!
|
Contributions are always welcome!
|
||||||
@@ -57,7 +70,7 @@ Please read https://radicle.xyz/guides/user to get used to clone this repo with
|
|||||||
|
|
||||||
If you just want to clone the repo without `radicle` you can clone the repo with `git clone https://seed.radicle.garden/z3gWc1qgaeZaoGwL4WTstLNoqjayM.git smtprd-ng`
|
If you just want to clone the repo without `radicle` you can clone the repo with `git clone https://seed.radicle.garden/z3gWc1qgaeZaoGwL4WTstLNoqjayM.git smtprd-ng`
|
||||||
|
|
||||||
Beware: This is very early alpha ;-)
|
Beware: This is alpha ;-)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -86,13 +99,58 @@ Where
|
|||||||
|
|
||||||
Plese note: Right now, there is no way to set a password, so the private key is unprotected and should only be used for this automation and have appropiate file permissions. I might add the option to set a password through the config file in the future, though.
|
Plese note: Right now, there is no way to set a password, so the private key is unprotected and should only be used for this automation and have appropiate file permissions. I might add the option to set a password through the config file in the future, though.
|
||||||
|
|
||||||
|
### Systemd
|
||||||
|
|
||||||
|
An example `systemd` file is in the `sytemd` subfolder. Be sure to replace @smtprd@ with a path to the python executable.
|
||||||
|
|
||||||
|
For `NixOS` you yan use the provided `nixosModule` like so:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# flake.nix
|
||||||
|
{
|
||||||
|
inputs.smtp = {
|
||||||
|
url = "git+https://seed.radicle.garden/z3gWc1qgaeZaoGwL4WTstLNoqjayM.git";
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = inputs@{ ... }: {
|
||||||
|
|
||||||
|
nixosConfigurations = {
|
||||||
|
my-config = inputs.nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
modules = [
|
||||||
|
inputs.smtp.nixosModules.smtprd-ng
|
||||||
|
{
|
||||||
|
services.smtprd-ng = {
|
||||||
|
enable = true;
|
||||||
|
client = {
|
||||||
|
hostname = "smtp.example.com";
|
||||||
|
username = "username";
|
||||||
|
password_file = "/etc/smtp_pw";
|
||||||
|
sender = "username@example.com";
|
||||||
|
start_tls = true;
|
||||||
|
smime_cert = "${./smime.crt}";
|
||||||
|
smime_cert_private = "/etc/private.key";
|
||||||
|
};
|
||||||
|
emails = {
|
||||||
|
"monitor@example.com" = "${./cert_for_monitor_example_com}";
|
||||||
|
"second_monitor@example.com" = "";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- Add wheel for pypi
|
- Add wheel for pypi
|
||||||
|
|
||||||
- Add systemd service
|
|
||||||
|
|
||||||
- Cleanup code
|
- Cleanup code
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ sender = foo@example.com
|
|||||||
# start_tls = false
|
# start_tls = false
|
||||||
# smime_cert = /path/to/cert
|
# smime_cert = /path/to/cert
|
||||||
# smime_cert_private = /path/to/cert.key
|
# smime_cert_private = /path/to/cert.key
|
||||||
# smime_to_cert = /path/to/recipient_cert
|
|
||||||
|
|
||||||
[emails]
|
[emails]
|
||||||
# If you want to encrypt outgoing email, add the recipient certificate to the email
|
# If you want to encrypt outgoing email, add the recipient certificate to the email
|
||||||
|
|||||||
16
default.nix
16
default.nix
@@ -1,11 +1,11 @@
|
|||||||
{ pkgs ? import <nixpkgs> { }
|
{
|
||||||
, lib ? pkgs.lib
|
pkgs ? import <nixpkgs> { },
|
||||||
,
|
lib ? pkgs.lib,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
pkgs.python3Packages.buildPythonPackage rec {
|
pkgs.python3Packages.buildPythonPackage rec {
|
||||||
pname = "smtprd-ng";
|
pname = "smtprd-ng";
|
||||||
version = "git";
|
version = "0.3.1";
|
||||||
pyproject = true;
|
pyproject = true;
|
||||||
|
|
||||||
src = lib.cleanSource ./.;
|
src = lib.cleanSource ./.;
|
||||||
@@ -22,18 +22,10 @@ pkgs.python3Packages.buildPythonPackage rec {
|
|||||||
nativeCheckInputs = [ pkgs.python3Packages.pytestCheckHook ];
|
nativeCheckInputs = [ pkgs.python3Packages.pytestCheckHook ];
|
||||||
pythonImportsCheck = [ "smtprd_ng" ];
|
pythonImportsCheck = [ "smtprd_ng" ];
|
||||||
|
|
||||||
postInstall = ''
|
|
||||||
install -D -m 644 systemd/smtprd-ng.service \
|
|
||||||
$out/lib/systemd/system/smtprd-ng.service
|
|
||||||
substituteInPlace $out/lib/systemd/system/smtprd-ng.service \
|
|
||||||
--replace-fail @smtprd@ "$out/bin/smtprd-ng"
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
description = "SMTP forwarding relay daemon with signing and encryption";
|
description = "SMTP forwarding relay daemon with signing and encryption";
|
||||||
homepage = "https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3gWc1qgaeZaoGwL4WTstLNoqjayM";
|
homepage = "https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3gWc1qgaeZaoGwL4WTstLNoqjayM";
|
||||||
license = lib.licenses.agpl3Only;
|
license = lib.licenses.agpl3Only;
|
||||||
maintainers = [ lib.maintainers.gador ];
|
maintainers = [ lib.maintainers.gador ];
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
83
devenv.lock
83
devenv.lock
@@ -3,11 +3,10 @@
|
|||||||
"devenv": {
|
"devenv": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "src/modules",
|
"dir": "src/modules",
|
||||||
"lastModified": 1719993933,
|
"lastModified": 1761596764,
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "devenv",
|
"repo": "devenv",
|
||||||
"rev": "83b295ec9febbc662040ffa8539d23f294af275d",
|
"rev": "17560d064ba5e4fc946c0ea0ee7b31ec291e706f",
|
||||||
"treeHash": "decc8709d4ea38f8c3eff4a2a2a4fd396564609f",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -20,11 +19,10 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696426674,
|
"lastModified": 1761588595,
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
|
||||||
"treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -33,10 +31,31 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1760663237,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"gitignore": {
|
"gitignore": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"pre-commit-hooks",
|
"git-hooks",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -45,7 +64,6 @@
|
|||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "gitignore.nix",
|
"repo": "gitignore.nix",
|
||||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||||
"treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -56,11 +74,10 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716977621,
|
"lastModified": 1761313199,
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "devenv-nixpkgs",
|
"repo": "devenv-nixpkgs",
|
||||||
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
|
"rev": "d1c30452ebecfc55185ae6d1c983c09da0c274ff",
|
||||||
"treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -70,50 +87,14 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1719837636,
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "28f8f3531ebdbea069995c20bd946a295699f275",
|
|
||||||
"treeHash": "0bd224f9c40a54835c5afeedd325da7056a6f14c",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-23.11",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pre-commit-hooks": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": "flake-compat",
|
|
||||||
"gitignore": "gitignore",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1719259945,
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
|
|
||||||
"treeHash": "1a76ff89a9d4017b48abbb1bad8837b35d604ffc",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"devenv": "devenv",
|
"devenv": "devenv",
|
||||||
|
"git-hooks": "git-hooks",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
"pre-commit-hooks": [
|
||||||
|
"git-hooks"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
39
devenv.nix
39
devenv.nix
@@ -1,8 +1,9 @@
|
|||||||
{ pkgs
|
{
|
||||||
, lib
|
pkgs,
|
||||||
, config
|
lib,
|
||||||
, inputs
|
config,
|
||||||
, ...
|
inputs,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
@@ -11,7 +12,6 @@ let
|
|||||||
./requirements-dev.txt
|
./requirements-dev.txt
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
# https://devenv.sh/packages/
|
# https://devenv.sh/packages/
|
||||||
packages = [
|
packages = [
|
||||||
@@ -20,19 +20,19 @@ in
|
|||||||
pkgs.openssl
|
pkgs.openssl
|
||||||
];
|
];
|
||||||
|
|
||||||
# https://devenv.sh/tests/
|
env = {
|
||||||
# enterTest = ''
|
LDFLAGS = "-L${pkgs.openssl.dev}/lib";
|
||||||
# echo "Running tests"
|
CFLAGS = "-I${pkgs.openssl.dev}/include";
|
||||||
# git --version | grep "2.42.0"
|
SWIG_FEATURES = "-I${pkgs.openssl.dev}/include";
|
||||||
# '';
|
};
|
||||||
|
|
||||||
# https://devenv.sh/services/
|
|
||||||
# services.postgres.enable = true;
|
|
||||||
|
|
||||||
# https://devenv.sh/languages/
|
# https://devenv.sh/languages/
|
||||||
languages.nix.enable = true;
|
languages.nix.enable = true;
|
||||||
languages.python = {
|
languages.python = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
libraries = [
|
||||||
|
pkgs.openssl.dev
|
||||||
|
];
|
||||||
venv = {
|
venv = {
|
||||||
enable = true;
|
enable = true;
|
||||||
requirements = builtins.readFile req;
|
requirements = builtins.readFile req;
|
||||||
@@ -40,8 +40,9 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
# https://devenv.sh/pre-commit-hooks/
|
# https://devenv.sh/pre-commit-hooks/
|
||||||
pre-commit.hooks = {
|
git-hooks.hooks = {
|
||||||
nixpkgs-fmt.enable = true;
|
nixfmt.enable = true;
|
||||||
|
# nixfmt.package = pkgs.nixfmt-rfc-style;
|
||||||
# remove unused imports
|
# remove unused imports
|
||||||
autoflake.enable = true;
|
autoflake.enable = true;
|
||||||
# formatter
|
# formatter
|
||||||
@@ -54,11 +55,5 @@ in
|
|||||||
pylint.enable = true;
|
pylint.enable = true;
|
||||||
detect-private-keys.enable = true;
|
detect-private-keys.enable = true;
|
||||||
detect-private-keys.excludes = [ "tests/" ];
|
detect-private-keys.excludes = [ "tests/" ];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# https://devenv.sh/processes/
|
|
||||||
# processes.ping.exec = "ping example.com";
|
|
||||||
|
|
||||||
# See full reference at https://devenv.sh/reference/options/
|
|
||||||
}
|
}
|
||||||
|
|||||||
49
flake.lock
generated
49
flake.lock
generated
@@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696426674,
|
"lastModified": 1747046372,
|
||||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -21,11 +21,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1710146030,
|
"lastModified": 1731533236,
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -57,11 +57,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720087678,
|
"lastModified": 1761594641,
|
||||||
"narHash": "sha256-uOhYJU3ldDKXYV+mFaXcPtyjq/UIMh/6SCuoVNU9rxM=",
|
"narHash": "sha256-sImk6SJQASDLQo8l+0zWWaBgg7TueLS6lTvdH5pBZpo=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "1afc5440469f94e7ed26e8648820971b102afdc3",
|
"rev": "1666250dbe4141e4ca8aaf89b40a3a51c2e36144",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -70,29 +70,13 @@
|
|||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1718811006,
|
|
||||||
"narHash": "sha256-0Y8IrGhRmBmT7HHXlxxepg2t8j1X90++qRN3lukGaIk=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "03d771e513ce90147b65fe922d87d3a0356fc125",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-23.11",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719082008,
|
"lastModified": 1759070547,
|
||||||
"narHash": "sha256-jHJSUH619zBQ6WdC21fFAlDxHErKVDJ5fpN0Hgx4sjs=",
|
"narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9693852a2070b398ee123a329e68f0dab5526681",
|
"rev": "647e5c14cbd5067f44ac86b74f014962df460840",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -106,15 +90,14 @@
|
|||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"gitignore": "gitignore",
|
"gitignore": "gitignore",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2"
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719259945,
|
"lastModified": 1760663237,
|
||||||
"narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=",
|
"narHash": "sha256-BflA6U4AM1bzuRMR8QqzPXqh8sWVCNDzOdsxXEguJIc=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
|
"rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
15
flake.nix
15
flake.nix
@@ -8,19 +8,21 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
{ self
|
{
|
||||||
, nixpkgs
|
self,
|
||||||
, flake-utils
|
nixpkgs,
|
||||||
, pre-commit-hooks
|
flake-utils,
|
||||||
,
|
pre-commit-hooks,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
supportedSystems = [
|
supportedSystems = [
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
"aarch64-darwin"
|
"aarch64-darwin"
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
nixosModules.smtprd-ng = import ./module.nix;
|
||||||
overlays.default = import ./overlay.nix { inherit self; };
|
overlays.default = import ./overlay.nix { inherit self; };
|
||||||
}
|
}
|
||||||
// flake-utils.lib.eachSystem supportedSystems (
|
// flake-utils.lib.eachSystem supportedSystems (
|
||||||
@@ -39,7 +41,8 @@
|
|||||||
pre-commit-check = pre-commit-hooks.lib.${system}.run rec {
|
pre-commit-check = pre-commit-hooks.lib.${system}.run rec {
|
||||||
src = ./.;
|
src = ./.;
|
||||||
hooks = {
|
hooks = {
|
||||||
nixpkgs-fmt.enable = true;
|
nixfmt.enable = true;
|
||||||
|
nixfmt.package = pkgs.nixfmt-rfc-style;
|
||||||
autoflake.enable = true;
|
autoflake.enable = true;
|
||||||
black.enable = true;
|
black.enable = true;
|
||||||
isort.enable = true;
|
isort.enable = true;
|
||||||
|
|||||||
186
module.nix
Normal file
186
module.nix
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.services.smtprd-ng;
|
||||||
|
smtprd-ng = pkgs.callPackage ./. { };
|
||||||
|
|
||||||
|
cfgText = generators.toINI { } {
|
||||||
|
server = {
|
||||||
|
hostname = cfg.server.hostname;
|
||||||
|
port = cfg.server.port;
|
||||||
|
};
|
||||||
|
client = {
|
||||||
|
hostname = cfg.client.hostname;
|
||||||
|
port = cfg.client.port;
|
||||||
|
username = cfg.client.username;
|
||||||
|
password_file =
|
||||||
|
if cfg.client.password_file != "" then "/run/credentials/smtprd-ng.service/password_file" else "";
|
||||||
|
sender = cfg.client.sender;
|
||||||
|
use_tls = cfg.client.use_tls;
|
||||||
|
start_tls = cfg.client.start_tls;
|
||||||
|
smime_cert = cfg.client.smime_cert;
|
||||||
|
smime_cert_private =
|
||||||
|
if cfg.client.smime_cert_private != "" then
|
||||||
|
"/run/credentials/smtprd-ng.service/smime_cert_private"
|
||||||
|
else
|
||||||
|
"";
|
||||||
|
};
|
||||||
|
emails = cfg.emails;
|
||||||
|
};
|
||||||
|
confFile = pkgs.writeText "config.ini" cfgText;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.smtprd-ng = {
|
||||||
|
enable = mkEnableOption "smtprd-ng";
|
||||||
|
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = smtprd-ng;
|
||||||
|
description = mdDoc ''
|
||||||
|
The smtprd-ng package to use.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
server = {
|
||||||
|
hostname = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "localhost";
|
||||||
|
description = mdDoc ''
|
||||||
|
The hostname the local SMTP server should listen on.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 8025;
|
||||||
|
description = mdDoc ''
|
||||||
|
The port the local SMTP server should listen on.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
client = {
|
||||||
|
hostname = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = mdDoc ''
|
||||||
|
The hostname of the remote SMTP server.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 587;
|
||||||
|
description = mdDoc ''
|
||||||
|
The port of the remote SMTP server.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
username = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = mdDoc ''
|
||||||
|
The username to login to the remote SMTP server.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
password_file = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
description = mdDoc ''
|
||||||
|
The path to the file containing the password to login to the remote SMTP server.
|
||||||
|
Should not be in /nix/store!
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sender = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = mdDoc ''
|
||||||
|
The email of the sender. Some SMTP servers require this to be the real FROM email address of the user.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
use_tls = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = mdDoc ''
|
||||||
|
Use TLS to connect. Mutually exclusive with `start_tls`
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
start_tls = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = mdDoc ''
|
||||||
|
Use STARTLS to connect. Mutually exclusive with `use_tls`
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
smime_cert = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
example = "''\${./mycert.pem}";
|
||||||
|
description = mdDoc ''
|
||||||
|
The path to the S/MIME certificate used to sign messages.
|
||||||
|
If empty, will neither encrypt, nor sign relayed messages.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
smime_cert_private = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
description = mdDoc ''
|
||||||
|
The path to the S/MIME private key for the certificate used to sign messages.
|
||||||
|
Should not be in /nix/store!
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
emails = mkOption {
|
||||||
|
type = types.nullOr (types.attrsOf (types.nullOr types.str));
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
A required set of recipients. A certificate is optional, but required if
|
||||||
|
messages should be signed and encrypted.
|
||||||
|
'';
|
||||||
|
example = {
|
||||||
|
"monitoring.foo@example.com" = "`/path/to/certificate`";
|
||||||
|
"unencrypted@example.com" = "`null`";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !cfg.client.use_tls || !cfg.client.start_tls;
|
||||||
|
message = "Use either TLS or STARTTLS, not both.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
cfg.client.smime_cert == null
|
||||||
|
|| (cfg.client.smime_cert != null && cfg.client.smime_cert_private != null);
|
||||||
|
message = "If a S/MIME certificate should be used to sign messages, the private key to this certificate must be supplied.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.emails != null;
|
||||||
|
message = "At least one recipient must be configured.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services = {
|
||||||
|
smtprd-ng = {
|
||||||
|
description = "Run local SMTP relay";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
requires = [ "network.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
LoadCredential = [
|
||||||
|
"smime_cert_private:${cfg.client.smime_cert_private}"
|
||||||
|
"password_file:${cfg.client.password_file}"
|
||||||
|
];
|
||||||
|
DynamicUser = true;
|
||||||
|
User = "smtprd-ng";
|
||||||
|
Group = "smtprd-ng";
|
||||||
|
Restart = "on-failure";
|
||||||
|
ExecStart = "${cfg.package}/bin/smtprd-ng --config ${confFile}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,4 +1 @@
|
|||||||
{ self }:
|
{ self }: _final: prev: { smtprd-ng = self.packages.${prev.system}.smtprd-ng; }
|
||||||
_final: prev: {
|
|
||||||
smtprd-ng = self.packages.${prev.system}.smtprd-ng;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "smtprd-ng"
|
name = "smtprd-ng"
|
||||||
version = "0.0.1"
|
version = "0.3.1"
|
||||||
description = "SMTP forwarding relay daemon with signing and encryption"
|
description = "SMTP forwarding relay daemon with signing and encryption"
|
||||||
authors = [{ name = "Florian Brandes", email = "dev@mail.flo-the.dev" }]
|
authors = [{ name = "Florian Brandes", email = "dev@mail.flo-the.dev" }]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ class Config:
|
|||||||
password=cls._read_from_file(
|
password=cls._read_from_file(
|
||||||
(config.get("client", "password_file", fallback=""))
|
(config.get("client", "password_file", fallback=""))
|
||||||
),
|
),
|
||||||
|
# TODO: Remove or add nixos option
|
||||||
set_reply_to=config.getboolean(
|
set_reply_to=config.getboolean(
|
||||||
"client", "set_reply_to", fallback=False
|
"client", "set_reply_to", fallback=False
|
||||||
),
|
),
|
||||||
@@ -316,13 +317,18 @@ class SMTPClient(SMTP):
|
|||||||
f"'{message.get('Subject', '')}'"
|
f"'{message.get('Subject', '')}'"
|
||||||
)
|
)
|
||||||
if self._config.smime_cert and self._emails.email_certs[recipient] != "":
|
if self._config.smime_cert and self._emails.email_certs[recipient] != "":
|
||||||
message = self._encrypt_and_sign(message, recipient)
|
m = self._encrypt_and_sign(message, recipient)
|
||||||
|
else:
|
||||||
|
m = message
|
||||||
|
m.add_header("From", self._config.sender)
|
||||||
|
m.add_header("To", recipient)
|
||||||
|
m = m.as_bytes()
|
||||||
async with (
|
async with (
|
||||||
self._lock
|
self._lock
|
||||||
): # TODO: consumer task from spool queue, reusing connections
|
): # TODO: consumer task from spool queue, reusing connections
|
||||||
try:
|
try:
|
||||||
await self.connect()
|
await self.connect()
|
||||||
await self._send_message(message, self._config.sender, recipient)
|
await self._send_message(m, self._config.sender, recipient)
|
||||||
except SMTPRecipientsRefused as e:
|
except SMTPRecipientsRefused as e:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Recipients refused: {', '.join(_.recipient for _ in e.recipients)}"
|
f"Recipients refused: {', '.join(_.recipient for _ in e.recipients)}"
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
-----BEGIN PRIVATE KEY-----
|
||||||
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALb6E084QJJVnRGg
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKZQOkKyDARSm0nn
|
||||||
Tij6Mc+ZzO3bV8Izt+A2RkpV4aYTjClVc2pLHh8KyjTA/bqnUYXfQCEDTHx2RsE5
|
UCcNS0jmWa70RUH4AyZGBNGPN87z9CkjuiNWYb75aMomoAOdfzFNbPvqTiBkmrx6
|
||||||
DqmyaYxWejcptw3Ht1YkZO2RW7zLSNQjlwFfcgf92XH1uzUxido/Lbw8LkHnJqCO
|
GJHrpA77GSkctEXWeSstxFdZWr14eR0WIKq4Cg7bZqqYliVaxsPUozIGZihMcx8e
|
||||||
4jemU9wP9b82JBgou91VEzqTfHPXAgMBAAECgYAc601d+fAKsMlQXdu8kj6JJy/C
|
uyepTqOD5cePFKsNL0ZbxcJRD79dAgMBAAECgYBwLoF953cvm0Df8sUf8lmrzKUF
|
||||||
cCZgpTfskeduHEC7tN80MTM6m4C5O0VWLSJs+8DgvbYvAYx3J2Jra48rtu0DYrd/
|
3AKzYsVNFcG+UOqGEAvLjCvMFkJMfRNv9+nLRns7GEzBB+S2+OVe6MSV+OppkgxR
|
||||||
gWqijODMDr6l8jgEeA8DN0OwcWhTB6unowHzR7RadijzP0nOIiGyArPtguGk4XKw
|
AszmV9S/A3BS4DaCYj3xGJAguZJhqWLGqXc23ukuCsnpIBtldSIT7Dj4i4gC8Auq
|
||||||
NcRoU36bxb/smBCaIQJBAOFMt0xTx6celUUB+b7HKNLe9kZb2shSZXzKIuzqAPJd
|
gvNxNQgZC2yMm0sGSQJBANLJrr8zJmKG7kTq6PM4psmSkfbd1s3Tugqkeg86xnRu
|
||||||
8TafC2CSt6iFTfhwr+kARz300r0y/JrGG30a6B8kRb8CQQDP6PvDoY6atLU35h+l
|
8BI5n15VB2mUvWbO3U+BtTFKI+69mlnq6Av496E+PVMCQQDJ/HZxq14tc3du28fV
|
||||||
WjuvkOKaR6ny/uarNP3nHapxgC9cNUkySFBDrSIJhNorYSGSMxqhEGUPWHzhH0PG
|
R2cd5uIW5zqRsfOwYkoJ/SXnQf1FHfoM1w7ibmZEu8S2kfmjWuo6O3PXMunnBMqM
|
||||||
H0fpAkEAkelpXNl1mFpKOiMJZ/D8E3Wq8e5TRyF18NfIvr7eVhlZOxLN/4GFyHJt
|
eMqPAkAnuy4+CeUlUyucP+8S0U3W1tK+hogTmeIKWYBMWJbJoZOMy+G3RS21f/zH
|
||||||
CNWSV8iCWzHPuhDnYCWlb+SZKHIJaQJACs4i94HX9XZazLLrBh7wZylyfW4oCPby
|
YRyj/N3rYX8uY/yxEC8W+qGqefHTAkAu+3Zhgkbps434T07wISevIOE+CpLpCMdy
|
||||||
agdxAqfqCcgNrg8e5LwZX8sJr9D1vbdolT6Orbw6ZFfG9bQ4Q32wsQJAZzbkDv+l
|
I6qtICEM5aCPzyU2j19688r9+d8LyHCUurT4zpNnXwRhJBApFH+pAkEAt2EbsszX
|
||||||
UBCR9qNqEpOtyOhBV8b8NzKgI/SBjgNbfzOki3r88qkwbJSMOZPZv3kkwmkjLTfz
|
S1aLA1vof1u6RHFYCylng5wDsreGg2ON3tHpt020n0PwrFPVfJH+KaAkRJC3EYgg
|
||||||
x7eQLB/6eQFmFw==
|
gsqdDSdEUyHIsQ==
|
||||||
-----END PRIVATE KEY-----
|
-----END PRIVATE KEY-----
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIICsDCCAhmgAwIBAgIUF0xm6FBeRujH+4/G1506jAty04UwDQYJKoZIhvcNAQEL
|
MIICZjCCAc+gAwIBAgIUW4OUtBdWs+bYoFmwkWdi9Zx813AwDQYJKoZIhvcNAQEL
|
||||||
BQAwajELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEjMCEGCSqGSIb3DQEJARYUc2VuZGVy
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNTA3MTYwNjUxMjRaFw0yNjA3
|
||||||
QGxvY2FsaG9zdC5jb20wHhcNMjQwNzA2MTEyMjU2WhcNMjUwNzA2MTEyMjU2WjBq
|
MTYwNjUxMjRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwgZ8wDQYJKoZIhvcNAQEB
|
||||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMSMwIQYJKoZIhvcNAQkBFhRzZW5kZXJAbG9j
|
BQADgY0AMIGJAoGBAKZQOkKyDARSm0nnUCcNS0jmWa70RUH4AyZGBNGPN87z9Ckj
|
||||||
YWxob3N0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtvoTTzhAklWd
|
uiNWYb75aMomoAOdfzFNbPvqTiBkmrx6GJHrpA77GSkctEXWeSstxFdZWr14eR0W
|
||||||
EaBOKPoxz5nM7dtXwjO34DZGSlXhphOMKVVzakseHwrKNMD9uqdRhd9AIQNMfHZG
|
IKq4Cg7bZqqYliVaxsPUozIGZihMcx8euyepTqOD5cePFKsNL0ZbxcJRD79dAgMB
|
||||||
wTkOqbJpjFZ6Nym3Dce3ViRk7ZFbvMtI1COXAV9yB/3ZcfW7NTGJ2j8tvDwuQecm
|
AAGjUzBRMB0GA1UdDgQWBBRirbLkQUSJy7TsedWuFgEGwGZ2MjAfBgNVHSMEGDAW
|
||||||
oI7iN6ZT3A/1vzYkGCi73VUTOpN8c9cCAwEAAaNTMFEwHQYDVR0OBBYEFDFFLpTd
|
gBRirbLkQUSJy7TsedWuFgEGwGZ2MjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
|
||||||
y1co5C6OVCLJRgIicstMMB8GA1UdIwQYMBaAFDFFLpTdy1co5C6OVCLJRgIicstM
|
DQEBCwUAA4GBAGCzB1nq3PMBwSXetoXXO50ZI5+SNM+u6KVI3z0t6Tsg3FmvtP0c
|
||||||
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEATdAHRrnrl2aQ8SI3
|
WlJ8V9CthyQaf02+bbfKlJ85LHa62w9H2b/WozzpF0GqUujrGA9j4NFDGq19KtI5
|
||||||
kQgUsTp5hHc3M5b2+ZbNYCsz7SYQBtHniGId9vBh941gUs8R8X16Cdp0pjVAayeU
|
mX4WTiCkcLhh1xV7CLAdrDWtOPLTb+O53/t9xkOYIzXHpc2ha+EhiU0G
|
||||||
CCW1Zs47tA9IIT1hslOORibTcSQKr7TI+RprURyky8m2T9PbOSLgmnjlbydbxN7L
|
|
||||||
hxx8dg1pWRfEKBSvO1gOkcTo7SQ=
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ def test_client_encrypt():
|
|||||||
# new line is important to seperate header from body
|
# new line is important to seperate header from body
|
||||||
assert lines[7] == ""
|
assert lines[7] == ""
|
||||||
assert (
|
assert (
|
||||||
lines[8] == "MIIBdgYJKoZIhvcNAQcDoIIBZzCCAWMCAQAxggEeMIIBGgIBADCBgjBqMQswCQYD"
|
lines[8] == "MIIBTgYJKoZIhvcNAQcDoIIBPzCCATsCAQAxgfcwgfQCAQAwXTBFMQswCQYDVQQG"
|
||||||
)
|
)
|
||||||
|
|
||||||
# test decryption
|
# test decryption
|
||||||
|
|||||||
Reference in New Issue
Block a user