summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorAlex Schendel <alex.schendel@intel.com>2023-07-21 23:32:38 +0300
committerAlex Schendel <alex.schendel@intel.com>2023-07-25 04:21:17 +0300
commit47af83218a7f64b451b7e7bccc82ecdb75e68a0a (patch)
treea25f613c90e70d415450c8d4447e99cb81b739ef /scripts
parentfc3edfdd8b63c1efc4aed7e882584b6aec1b1a11 (diff)
downloadbmcweb-47af83218a7f64b451b7e7bccc82ecdb75e68a0a.tar.xz
scripts: Script to autogenerate TLS certs
This script autogenerates: 1. Self-signed CA certificate/key pair 2. Server certificate/key pair 3. Client certificate/key pair 4. PKCS12 archive to store client certificate/key pair These files are all generated and then stored in a local ./certs directory. Following this, they are added to the BMC over Redfish. Then, the script attempts to use the client certificate/key pair to access a Redfish url with permissions while not providing username or password. If this succeeds, then it generates the PKCS12 archive file and directs the user to import it into a browser if they wish to test webui or would prefer to do any testing in browser rather than over curl or similar data tranfer tools for HTTP. Tested: Monitored output to ensure that each step succeeded and once the PKCS12 archive file was generated, imported it into a browser and accessed a redfish url with permissions while not being redirected to the login route. Change-Id: Ie8a393feb472281d1865e52bddbdb58edbf5b071 Signed-off-by: Alex Schendel <alex.schendel@intel.com>
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/generate_auth_certificates.py289
1 files changed, 289 insertions, 0 deletions
diff --git a/scripts/generate_auth_certificates.py b/scripts/generate_auth_certificates.py
new file mode 100755
index 0000000000..c05e381fae
--- /dev/null
+++ b/scripts/generate_auth_certificates.py
@@ -0,0 +1,289 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+
+import requests
+
+try:
+ import redfish
+except ModuleNotFoundError:
+ raise Exception("Please run pip install redfish to run this script.")
+try:
+ from OpenSSL import crypto
+except ImportError:
+ raise Exception("Please run pip install pyOpenSSL to run this script.")
+
+# Script to generate a certificates for a CA, server, and client
+# allowing for client authentication using mTLS certificates.
+# This can then be used to test mTLS client authentication for Redfish
+# and webUI. Note that this requires the pyOpenSSL library to function.
+# TODO: Use EC keys rather than RSA keys.
+
+
+def generateCACert(serial):
+ # CA key
+ key = crypto.PKey()
+ key.generate_key(crypto.TYPE_RSA, 2048)
+
+ # CA cert
+ cert = crypto.X509()
+ cert.set_serial_number(serial)
+ cert.set_version(2)
+ cert.set_pubkey(key)
+ cert.gmtime_adj_notBefore(0)
+ cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
+
+ caCertSubject = cert.get_subject()
+ caCertSubject.countryName = "US"
+ caCertSubject.stateOrProvinceName = "California"
+ caCertSubject.localityName = "San Francisco"
+ caCertSubject.organizationName = "OpenBMC"
+ caCertSubject.organizationalUnitName = "bmcweb"
+ caCertSubject.commonName = "Test CA"
+ cert.set_issuer(caCertSubject)
+
+ cert.add_extensions(
+ [
+ crypto.X509Extension(
+ b"basicConstraints", True, b"CA:TRUE, pathlen:0"
+ ),
+ crypto.X509Extension(b"keyUsage", True, b"keyCertSign, cRLSign"),
+ crypto.X509Extension(
+ b"subjectKeyIdentifier", False, b"hash", subject=cert
+ ),
+ ]
+ )
+ cert.add_extensions(
+ [
+ crypto.X509Extension(
+ b"authorityKeyIdentifier", False, b"keyid:always", issuer=cert
+ )
+ ]
+ )
+
+ # sign CA cert with CA key
+ cert.sign(key, "sha256")
+ return key, cert
+
+
+def generateCert(commonName, extensions, caKey, caCert, serial):
+ # key
+ key = crypto.PKey()
+ key.generate_key(crypto.TYPE_RSA, 2048)
+
+ # cert
+ cert = crypto.X509()
+ serial
+ cert.set_serial_number(serial)
+ cert.set_version(2)
+ cert.set_pubkey(key)
+ cert.gmtime_adj_notBefore(0)
+ cert.gmtime_adj_notAfter(365 * 24 * 60 * 60)
+
+ certSubject = cert.get_subject()
+ certSubject.countryName = "US"
+ certSubject.stateOrProvinceName = "California"
+ certSubject.localityName = "San Francisco"
+ certSubject.organizationName = "OpenBMC"
+ certSubject.organizationalUnitName = "bmcweb"
+ certSubject.commonName = commonName
+ cert.set_issuer(caCert.get_issuer())
+
+ cert.add_extensions(extensions)
+ cert.add_extensions(
+ [
+ crypto.X509Extension(
+ b"authorityKeyIdentifier", False, b"keyid", issuer=caCert
+ )
+ ]
+ )
+
+ cert.sign(caKey, "sha256")
+ return key, cert
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--host", help="Host to connect to", required=True)
+ parser.add_argument(
+ "--username", help="Username to connect with", default="root"
+ )
+ parser.add_argument(
+ "--password",
+ help="Password for user in order to install certs over Redfish.",
+ default="0penBmc",
+ )
+ args = parser.parse_args()
+ host = args.host
+ username = args.username
+ password = args.password
+ if username == "root" and password == "0penBMC":
+ print(
+ """Note: Using default username 'root' and default password
+ '0penBmc'. Use --username and --password flags to change these,
+ respectively."""
+ )
+ serial = 1000
+
+ try:
+ print("Making certs directory.")
+ os.mkdir("certs")
+ except OSError as error:
+ if error.errno == 17:
+ print("certs directory already exists. Skipping...")
+ else:
+ print(error)
+ caKey, caCert = generateCACert(serial)
+ serial += 1
+ caKeyDump = crypto.dump_privatekey(crypto.FILETYPE_PEM, caKey)
+ caCertDump = crypto.dump_certificate(crypto.FILETYPE_PEM, caCert)
+ with open("certs/CA-cert.pem", "wb") as f:
+ f.write(caCertDump)
+ print("CA cert generated.")
+ with open("certs/CA-key.pem", "wb") as f:
+ f.write(caKeyDump)
+ print("CA key generated.")
+
+ clientExtensions = [
+ crypto.X509Extension(
+ b"keyUsage",
+ True,
+ b"""digitalSignature,
+ keyAgreement""",
+ ),
+ crypto.X509Extension(b"extendedKeyUsage", True, b"clientAuth"),
+ ]
+ clientKey, clientCert = generateCert(
+ username, clientExtensions, caKey, caCert, serial
+ )
+ serial += 1
+ clientKeyDump = crypto.dump_privatekey(crypto.FILETYPE_PEM, clientKey)
+ clientCertDump = crypto.dump_certificate(crypto.FILETYPE_PEM, clientCert)
+ with open("certs/client-key.pem", "wb") as f:
+ f.write(clientKeyDump)
+ print("Client key generated.")
+ with open("certs/client-cert.pem", "wb") as f:
+ f.write(clientCertDump)
+ print("Client cert generated.")
+
+ serverExtensions = [
+ crypto.X509Extension(
+ b"keyUsage",
+ True,
+ b"""digitalSignature,
+ keyAgreement""",
+ ),
+ crypto.X509Extension(b"extendedKeyUsage", True, b"serverAuth"),
+ ]
+ serverKey, serverCert = generateCert(
+ host, serverExtensions, caKey, caCert, serial
+ )
+ serial += 1
+ serverKeyDump = crypto.dump_privatekey(crypto.FILETYPE_PEM, serverKey)
+ serverCertDump = crypto.dump_certificate(crypto.FILETYPE_PEM, serverCert)
+ with open("certs/server-key.pem", "wb") as f:
+ f.write(serverKeyDump)
+ print("Server key generated.")
+ with open("certs/server-cert.pem", "wb") as f:
+ f.write(serverCertDump)
+ print("Server cert generated.")
+
+ caCertJSON = {}
+ caCertJSON["CertificateString"] = caCertDump.decode()
+ caCertJSON["CertificateType"] = "PEM"
+ caCertPath = "/redfish/v1/Managers/bmc/Truststore/Certificates"
+ replaceCertPath = "/redfish/v1/CertificateService/Actions/"
+ replaceCertPath += "CertificateService.ReplaceCertificate"
+ print("Attempting to install CA certificate to BMC.")
+ redfishObject = redfish.redfish_client(
+ base_url="https://" + host,
+ username=username,
+ password=password,
+ default_prefix="/redfish/v1",
+ )
+ redfishObject.login(auth="session")
+ response = redfishObject.post(caCertPath, body=caCertJSON)
+ if response.status == 500:
+ print(
+ "An existing CA certificate is likely already installed."
+ " Replacing..."
+ )
+ caCertificateUri = {}
+ caCertificateUri["@odata.id"] = caCertPath + "/1"
+ caCertJSON["CertificateUri"] = caCertificateUri
+ response = redfishObject.post(replaceCertPath, body=caCertJSON)
+ if response.status == 200:
+ print("Successfully replaced existing CA certificate.")
+ else:
+ raise Exception(
+ "Could not install or replace CA certificate."
+ "Please check if a certificate is already installed. If a"
+ "certificate is already installed, try performing a factory"
+ "restore to clear such settings."
+ )
+ elif response.status == 200:
+ print("Successfully installed CA certificate.")
+ else:
+ raise Exception("Could not install certificate: " + response.read)
+ serverCertJSON = {}
+ serverCertJSON["CertificateString"] = (
+ serverKeyDump.decode() + serverCertDump.decode()
+ )
+ serverCertificateUri = {}
+ serverCertificateUri[
+ "@odata.id"
+ ] = "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/1"
+ serverCertJSON["CertificateUri"] = serverCertificateUri
+ serverCertJSON["CertificateType"] = "PEM"
+ print("Replacing server certificate...")
+ response = redfishObject.post(replaceCertPath, body=serverCertJSON)
+ if response.status == 200:
+ print("Successfully replaced server certificate.")
+ else:
+ raise Exception("Could not replace certificate: " + response.read)
+ tlsPatchJSON = {"Oem": {"OpenBMC": {"AuthMethods": {"TLS": True}}}}
+ print("Ensuring TLS authentication is enabled.")
+ response = redfishObject.patch(
+ "/redfish/v1/AccountService", body=tlsPatchJSON
+ )
+ if response.status == 200:
+ print("Successfully enabled TLS authentication.")
+ else:
+ raise Exception("Could not enable TLS auth: " + response.read)
+ redfishObject.logout()
+ print("Testing redfish TLS authentication with generated certs.")
+ response = requests.get(
+ "https://" + host + "/redfish/v1/SessionService/Sessions",
+ verify=False,
+ cert=("certs/client-cert.pem", "certs/client-key.pem"),
+ )
+ response.raise_for_status()
+ print("Redfish TLS authentication success!")
+ print("Generating p12 cert file for browser authentication.")
+ pkcs12Cert = crypto.PKCS12()
+ pkcs12Cert.set_certificate(clientCert)
+ pkcs12Cert.set_privatekey(clientKey)
+ pkcs12Cert.set_ca_certificates([caCert])
+ pkcs12Cert.set_friendlyname(bytes(username, encoding="utf-8"))
+ with open("certs/client.p12", "wb") as f:
+ f.write(pkcs12Cert.export())
+ print(
+ "Client p12 cert file generated and stored in"
+ "./certs/client.p12."
+ )
+ print(
+ "Copy this file to a system with a browser and install the"
+ "cert into the browser."
+ )
+ print(
+ "You will then be able to test redfish and webui"
+ "authentication using this certificate."
+ )
+ print(
+ "Note: this p12 file was generated without a password, so it"
+ "can be imported easily."
+ )
+
+
+main()