[FIX] base: store SSL key/cert for smtpd tests

The various keys and certificates used by the TestIrMailServerSMTPD test
suite were generated on-the-fly via a shell script present next to the
test. It is just easier to save the keys and certs in git rather than
re-generating them everytime.

Changed the private keys from RSA to ed25519 for the smaller files size,
changed the validity date to a thousand year.

task-3703209
opw-3640374

Part-of: odoo/odoo#151483
This commit is contained in:
Julien Castiaux
2024-04-24 07:55:42 +00:00
parent 3b493f5631
commit ef9010c6ed
11 changed files with 111 additions and 224 deletions
-3
View File
@@ -1,3 +0,0 @@
*.pem
index*
serial*
+12
View File
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIByTCCAXugAwIBAgIUXOt3OuI/y68E6ogXPRKjMxM3d4kwBQYDK2VwMFExCzAJ
BgNVBAYTAkJFMRQwEgYDVQQHDAtIb3V0ZXNpcGxvdTEsMCoGA1UEAwwjSG91dGVz
aXBsb3UgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwIBcNMjQwNDIyMTUxNDU3WhgP
MzAyMzA4MjQxNTE0NTdaMFExCzAJBgNVBAYTAkJFMRQwEgYDVQQHDAtIb3V0ZXNp
cGxvdTEsMCoGA1UEAwwjSG91dGVzaXBsb3UgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkwKjAFBgMrZXADIQA9E6L8dKzknznGYYkNMVRt78Rs8L5sIOLFaJHfzfpcdaNj
MGEwHQYDVR0OBBYEFOCOFRwpR4cKRG3au4pqU5tc8ItOMB8GA1UdIwQYMBaAFOCO
FRwpR4cKRG3au4pqU5tc8ItOMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
AgGGMAUGAytlcANBAMDS7x9X9IIM1jCQdzTibtVu14mZmXoBJ8GoFMojUSvyz86y
81vpi1ZEOZoZm/HadUBlONvL4aGqbu0t6yABeA8=
-----END CERTIFICATE-----
@@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIICCTCCAbugAwIBAgICEAAwBQYDK2VwMFExCzAJBgNVBAYTAkJFMRQwEgYDVQQH
DAtIb3V0ZXNpcGxvdTEsMCoGA1UEAwwjSG91dGVzaXBsb3UgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwIBcNMjQwNDIyMTUxNDU3WhgPMzAyMzA4MjQxNTE0NTdaMEAx
CzAJBgNVBAYTAkJFMRQwEgYDVQQHDAtIb3V0ZXNpcGxvdTEbMBkGA1UEAwwSSG91
dGVzaXBsb3UgQ2xpZW50MCowBQYDK2VwAyEAFkZcOJ5bNr2NZcwZuXkdC5PfX+fu
M+YWyh5eeMtU7mujgcUwgcIwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBaAw
MwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIENsaWVudCBDZXJ0aWZp
Y2F0ZTAdBgNVHQ4EFgQUxag2EgTttBUfjSg/MHMs+4h/16cwHwYDVR0jBBgwFoAU
4I4VHClHhwpEbdq7impTm1zwi04wDgYDVR0PAQH/BAQDAgXgMB0GA1UdJQQWMBQG
CCsGAQUFBwMCBggrBgEFBQcDBDAFBgMrZXADQQAezi5mtJ3Y5A8DLuxL7g9rPff7
qUQqwg3RlGQ7QAkwiuUa6nQ1kfcIGSTFe6sOBL5ACMLwxP1ibgAENxBamvAC
-----END CERTIFICATE-----
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEICS5j5g1kNSbzhCRT7eDN4k12P0x5n8tg6HLPcCuY7DD
-----END PRIVATE KEY-----
-84
View File
@@ -1,84 +0,0 @@
# Use openssl to generate multiple signed RSA key/cert pairs.
#
# usage: cd path/to/dir/of/gencert.sh/; ./gencert.sh
#
# This script runs multiple openssl commands to create a fake certificate
# authority (CA) for the fake city of "Houtsiplou".
# https://fr.wiktionary.org/wiki/Houte-Si-Plou
#
# It then uses that CA to generate and sign 3 other certificates for various
# purposes: a client pair, a server pair, and an "any purpose" pair that is not
# restricted to client or server side. It also generates a self-signed pair to
# use as an "invalid" pair for testing.
#
# In Python you'll want to `load_verify_locations(cafile='path/to/ca.cert.pem')`
# to trust the certification authority and the other pairs that were signed
# with it. Note that it won't trust the autorities embedded in your OS anymore.
# https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_verify_locations
#
# This script and configuration were largely inspired by
# https://jamielinux.com/docs/openssl-certificate-authority/
set -o errexit
set -o nounset
if test ! -f openssl.conf
then
echo Cannot find openssl.conf in the working directory.
exit 1
fi
# reset auto generated files
rm -f *.pem index* serial*
touch index.txt
echo 1000 > serial
set -o xtrace
# create root cA
openssl genpkey -config openssl.conf -quiet -algorithm RSA -out ca.key.pem
openssl req -config openssl.conf -x509 -days 1000 \
-extensions ca_cert -key ca.key.pem -out ca.cert.pem \
-subj '/C=BE/L=Houtesiplou/CN=Houtesiplou Certification Authority'
openssl x509 -noout -text -in ca.cert.pem
# create any purpose
#openssl genpkey -config openssl.conf -quiet -algorithm RSA -out any_purpose.key.pem
#openssl req -config openssl.conf -new \
# -key any_purpose.key.pem -out any_purpose.csr.pem \
# -subj '/C=BE/L=Houtesiplou/CN=Houtesiplou Any Purpose'
#openssl ca -config openssl.conf -notext -md sha256 -batch -days 1000 -notext \
# -extensions any_cert -keyfile ca.key.pem -cert ca.cert.pem \
# -in any_purpose.csr.pem -out any_purpose.cert.pem
#openssl x509 -noout -text -in any_purpose.cert.pem
# create client
openssl genpkey -config openssl.conf -quiet -algorithm RSA -out client.key.pem
openssl req -config openssl.conf -new \
-key client.key.pem -out client.csr.pem \
-subj '/C=BE/L=Houtesiplou/CN=Houtesiplou Client'
openssl ca -config openssl.conf -notext -md sha256 -batch -days 1000 -notext \
-extensions client_cert -keyfile ca.key.pem -cert ca.cert.pem \
-in client.csr.pem -out client.cert.pem
openssl x509 -noout -text -in client.cert.pem
# create server
openssl genpkey -config openssl.conf -quiet -algorithm RSA -out server.key.pem
openssl req -config openssl.conf -new \
-key server.key.pem -out server.csr.pem \
-subj '/C=BE/L=Houtesiplou/CN=Houtesiplou Server'
openssl ca -config openssl.conf -notext -md sha256 -batch -days 1000 -notext \
-extensions server_cert -keyfile ca.key.pem -cert ca.cert.pem \
-in server.csr.pem -out server.cert.pem
openssl x509 -noout -text -in server.cert.pem
# create untrusted self-signed pair
openssl req -new -x509 -noenc -days 1000 -subj '/CN=SelfSigned Lmtd' \
-out self_signed.cert.pem -keyout self_signed.key.pem
# remove useless files
rm *.csr.pem *ca.key.pem
set +o xtrace
echo -e "\nDone! Here are your files:"
find $(pwd) -name '*.*.pem'
-84
View File
@@ -1,84 +0,0 @@
# OpenSSL configuration file.
# Largely inspired from https://jamielinux.com/docs/openssl-certificate-authority/appendix/root-configuration-file.html
[ ca ]
# `man ca`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
certs = .
new_certs_dir = .
database = ./index.txt
serial = ./serial
RANDFILE = ./.rand
name_opt = ca_default
cert_opt = ca_default
default_days = 1
preserve = no
default_md = sha256
policy = policy0
[ policy0 ]
# See the POLICY FORMAT section of `man ca`.
countryName = match
localityName = match
commonName = supplied
emailAddress = optional
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = ca_cert
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
localityName = Locality Name
commonName = Common Name
emailAddress = Email Address
[ ca_cert ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ any_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server, client, email
nsComment = "OpenSSL Generated Any Purpose Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
[ client_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:localhost
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIUfnEIpcavhz3Pn9P5YMIZzku+OJUwDQYJKoZIhvcNAQEL
BQAwGjEYMBYGA1UEAwwPU2VsZlNpZ25lZCBMbXRkMCAXDTI0MDQyMjE1MTQ1N1oY
DzMwMjMwODI0MTUxNDU3WjAaMRgwFgYDVQQDDA9TZWxmU2lnbmVkIExtdGQwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIfgXKzK/FiQzqqAtOCqbTSCtp
rFr5hpTQJWscfQXfkQiJC0nLLojj4sLSNf/PnP8eglju+AIO6st2TF3v+qugog0o
D9cMvjhzqwLfpJzSr7EN5n4X3TAoyucPg/aF3+ACXwVXuTfxTCbJbEeBrWSk7b1k
R1bORVQvH895V08rVs2MZ5v3sutF0EWeiyh4O/EzpSiKywLUE5eLoZiJtN9j3yGm
bJii/fCcON4sQTxpL2/i2jbPo2dH2EKKS1xO0LBt4IoiOidRTQiOsjL2VANZiCer
UuWUlsG5albCcExvVzQnINRb7+6F6NJqJdC+rRqQG6YeXpUeXBe8GMZjpqLdAgMB
AAGjUzBRMB0GA1UdDgQWBBSRC2v9uCKuJ6lpWVbe2nO6gI6bJTAfBgNVHSMEGDAW
gBSRC2v9uCKuJ6lpWVbe2nO6gI6bJTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQDADAKCY4WDhVJnReyP2fc/E/N2Wa29BCdR9LZxS2sNd5gyQnZJ
FHLqu1R+tMACtXG4q1AEuN0yn7hsqCfy3hQH5mFLqQzNqoWF6HDQr2b7kfw/E1S3
bH/K+6Kbu5fEYWRZNo5Ti62mEHhSS2tBw0qmN9pceT/ZFhYpe44RQzuxEckOXBpx
g1J8NAqvknfvl9nu31C8Ye98XlsHDGz/qUvmIPqXCmmg26NcT/NE26A76tgegUDo
U/K4WFOr5cyHcIfaYWFg2/SKukudQldwU92ZvJOSwYvn3MKixg/YZGK+0MwzMm4c
+ZKttL1Pwub4Gt9ZaFrzrM1t2NRTgMiKU2lQ
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDIfgXKzK/FiQzq
qAtOCqbTSCtprFr5hpTQJWscfQXfkQiJC0nLLojj4sLSNf/PnP8eglju+AIO6st2
TF3v+qugog0oD9cMvjhzqwLfpJzSr7EN5n4X3TAoyucPg/aF3+ACXwVXuTfxTCbJ
bEeBrWSk7b1kR1bORVQvH895V08rVs2MZ5v3sutF0EWeiyh4O/EzpSiKywLUE5eL
oZiJtN9j3yGmbJii/fCcON4sQTxpL2/i2jbPo2dH2EKKS1xO0LBt4IoiOidRTQiO
sjL2VANZiCerUuWUlsG5albCcExvVzQnINRb7+6F6NJqJdC+rRqQG6YeXpUeXBe8
GMZjpqLdAgMBAAECggEAF197B7eRXZB1dTJjpan1OoKv2PETEVg1/LFYCX6uaBqi
1EMrdlM1pId42dkaD5czC1imddlreAIO99AS+sPjgpBRw1QGvda72F7dMLr33fyb
F06XGutveVn00j4P8m8f0MxItaot549Oou22GlvfSfY96DUEMtDUmG0iGqMUji3V
SeHvAeginTCaoux5OPdB94jcRpsSf+B0pRS0JMpW/87+mg3bzT8ouPYkbfRZ92Wo
4x3b1kcBbq7xvtmSsN0PxqZGcuuDn3zjO1XI6HCwWWP+ICUIC6+Tw8szWIvJ9Cgp
te2nvcAYstnrW15smJTjRNhikMpu3EsBajsgfsScywKBgQDyoAGlucEfREVmVuba
hbLfhGTmKvvsgS2e7p5ktJPkdCyvImU6l9Crzt8qD+9ic2LEgvRBtqaaHkC8DgLc
LiEL/QiIbNdGhfxm9+bWMW0wx6HWRwC1b7t1BT3Dh4y9I4YYkLR2NDqh1R8D4X0D
mEk9pfR2nFEDjkGyDp9gyCB68wKBgQDTi21DuKu14WQCfrInn3SGoDTeHGRhV6b6
HrOJj4ppbqffjUcvvpSzYJAZtk8eHdHMM8tut+OejxPzabFdgXrO4EaMiH5XYaTx
zDFpTe/cGZvb5m9jg6fEb0YCB6KfefDpFABXOlaILyl8JwnC0BT1bEL7k8c/s/it
LIVIy38+7wKBgQDG0lonPZ5FigO5BpOtFQzs36hzeVvyhjUlXXNNITFkb9NCPVRo
/ImSkTcNV/uaWOXiFVImG5BREWOI945ecirAkT0R1udesmOQ2as/cUeCRsWXO54Y
EJS0I3Rmq8ioIdk8fjB0AP7fKS9+VaTFcmDqdPlszVISMNwjFpqCi90aAwKBgQCT
RkxJi3Wv6DyyJ/Zr820ylLJ5t5aC1n0fQOSJbm9UO3+P+VGIAcyQnTd1TyEBvIzk
92I3sLo9FysymXCrwor3H9i92gDrYMVuuVPlFidZOlLx4xnFVFEmRrmcjChBkqmP
+ybJk4nOwdbF4n+/KxKMUlTHxPhAd1E3bvlT1qi97QKBgAHUTniLWiqxH4wyspIW
SV7EW87Suat+p6TdR81FonNAgLMGjtTqRcHkoCfQfExpPycuybpqtJK7IeiVqNc5
lCe48Hl6hpASfwe9T2K3q68UACU5pk5i/PTydFCI+zwVbaiBGip71skzGUVzcPY9
JBbw8hEOjPwcK4SAgi+Yprmc
-----END PRIVATE KEY-----
@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIIChzCCAjmgAwIBAgICEAEwBQYDK2VwMFExCzAJBgNVBAYTAkJFMRQwEgYDVQQH
DAtIb3V0ZXNpcGxvdTEsMCoGA1UEAwwjSG91dGVzaXBsb3UgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwIBcNMjQwNDIyMTUxNDU3WhgPMzAyMzA4MjQxNTE0NTdaMEAx
CzAJBgNVBAYTAkJFMRQwEgYDVQQHDAtIb3V0ZXNpcGxvdTEbMBkGA1UEAwwSSG91
dGVzaXBsb3UgU2VydmVyMCowBQYDK2VwAyEACb6GUUvOz8O1IlrH9/4sxaRGAemi
c/eMlQ7xjAjUbLOjggFCMIIBPjAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG
QDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp
ZmljYXRlMB0GA1UdDgQWBBRkIUgQms0wKozvwQp1b1XU53E3kjCBjgYDVR0jBIGG
MIGDgBTgjhUcKUeHCkRt2ruKalObXPCLTqFVpFMwUTELMAkGA1UEBhMCQkUxFDAS
BgNVBAcMC0hvdXRlc2lwbG91MSwwKgYDVQQDDCNIb3V0ZXNpcGxvdSBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eYIUXOt3OuI/y68E6ogXPRKjMxM3d4kwDgYDVR0PAQH/
BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMBQGA1UdEQQNMAuCCWxvY2FsaG9z
dDAFBgMrZXADQQDidpmoltxDR1Qh5ZDluVC1DENZpXk+My23SDzATvxovbmkHEK2
/LdWbqrTPJZIHGhs9W/7vWSbLzyHYTKH2fAD
-----END CERTIFICATE-----
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIHSVKSyvMJwmNHpoMj3BLlBy40bQ2vJJyUNknXcyPUkO
-----END PRIVATE KEY-----
@@ -4,9 +4,6 @@ import shutil
import smtplib
import socket
import ssl
import subprocess
import sys
import tempfile
import unittest
import warnings
from base64 import b64encode
@@ -52,49 +49,13 @@ def _smtp_authenticate(server, session, enveloppe, mechanism, data):
class Certificate:
def __init__(self, key, cert):
self.key = key
self.cert = cert
self.key = key and Path(file_path(key, filter_ext='.pem'))
self.cert = Path(file_path(cert, filter_ext='.pem'))
def __repr__(self):
return f"Certificate({self.key=}, {self.cert=})"
def _generate_certificates(tempdir):
"""Generate and sign a few RSA key pairs inside a temporary directory.
It invokes the openssl command line to create a new Certification
Authority and then use that CA to generate extra keys and
certificates for a user, a server.
It also creates a self-signed certificate, to test that invalid
certificates are correctly rejected.
It returns 4 Certificate: CA, client, server, self-signed. Each
Certificate has two attributes: key and cert, both are paths.
"""
certdir = Path(tempdir.name)
# Create a fake certificate authority and use it to generate a few
# signed certificates.
shutil.copy(file_path('base/tests/ssl/openssl.conf'), certdir)
shutil.copy(file_path('base/tests/ssl/gencert.sh'), certdir)
subprocess.run(
[shutil.which('sh'), certdir / 'gencert.sh'],
check=True,
cwd=certdir,
stdout=sys.stdout if _logger.isEnabledFor(logging.DEBUG) else subprocess.DEVNULL,
stderr=subprocess.STDOUT,
)
# Collect files and return them
return (
Certificate(key=None, cert=certdir / 'ca.cert.pem'),
Certificate(key=certdir / 'client.key.pem', cert=certdir / 'client.cert.pem'),
Certificate(key=certdir / 'server.key.pem', cert=certdir / 'server.cert.pem'),
Certificate(key=certdir / 'self_signed.key.pem', cert=certdir / 'self_signed.cert.pem'),
)
# skip when optional dependencies are not found
@unittest.skipUnless(aiosmtpd, "aiosmtpd couldn't be imported")
@unittest.skipUnless(_openssl, "openssl not found in path")
@@ -141,15 +102,19 @@ class TestIrMailServerSMTPD(TransactionCaseWithUserDemo):
# decrease aiosmtpd verbosity, odoo INFO = aiosmtpd WARNING
logging.getLogger('mail.log').setLevel(_logger.getEffectiveLevel() + 10)
# create a few certificates for ssl/tls+starttls and mock the
# various smtplib classes into trusting the created root CA.
_logger.info("Using openssl to generate fake certificates, show "
"subprocess output with: --log-handler %s:DEBUG", __name__)
tempdir = tempfile.TemporaryDirectory(prefix='odoo-test-smtpd-')
cls.addClassCleanup(tempdir.cleanup)
cls.ssl_ca, cls.ssl_client, cls.ssl_server, cls.ssl_self_signed = \
_generate_certificates(tempdir)
# Get various TLS keys and certificates. CA was used to sign
# both client and server. self_signed is... self signed.
cls.ssl_ca, cls.ssl_client, cls.ssl_server, cls.ssl_self_signed = [
Certificate(None, 'base/tests/ssl/ca.cert.pem'),
Certificate('base/tests/ssl/client.key.pem',
'base/tests/ssl/client.cert.pem'),
Certificate('base/tests/ssl/server.key.pem',
'base/tests/ssl/server.cert.pem'),
Certificate('base/tests/ssl/self_signed.key.pem',
'base/tests/ssl/self_signed.cert.pem'),
]
# Patch the two SMTP client classes into trusting the above CA
class TEST_SMTP(smtplib.SMTP):
def starttls(self, *, context):
if context is None:
@@ -157,15 +122,14 @@ class TestIrMailServerSMTPD(TransactionCaseWithUserDemo):
# context = ssl.create_default_context() # what it should do
context.load_verify_locations(cafile=str(cls.ssl_ca.cert))
super().starttls(context=context)
patcher = patch('smtplib.SMTP', TEST_SMTP)
patcher.start()
cls.addClassCleanup(patcher.stop)
class TEST_SMTP_SSL(smtplib.SMTP_SSL):
def _get_socket(self, *args, **kwargs):
# self.context = ssl.create_default_context() # what it should do
self.context.load_verify_locations(cafile=str(cls.ssl_ca.cert))
return super()._get_socket(*args, **kwargs)
patcher = patch('smtplib.SMTP', TEST_SMTP)
patcher.start()
cls.addClassCleanup(patcher.stop)
patcher = patch('smtplib.SMTP_SSL', TEST_SMTP_SSL)
patcher.start()
cls.addClassCleanup(patcher.stop)