1#!/usr/bin/env python3 2""" 3Script to generate certificates for a CA, server, and client allowing for 4client authentication using mTLS certificates. This can then be used to test 5mTLS client authentication for Redfish and WebUI. 6""" 7 8import argparse 9import datetime 10import errno 11import ipaddress 12import os 13import socket 14import time 15from typing import Optional 16 17import asn1 18import httpx 19from cryptography import x509 20from cryptography.hazmat.primitives import hashes, serialization 21from cryptography.hazmat.primitives.asymmetric import ec 22from cryptography.hazmat.primitives.serialization import ( 23 load_pem_private_key, 24 pkcs12, 25) 26from cryptography.x509.oid import NameOID 27 28replaceCertPath = "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate" 29# https://oidref.com/1.3.6.1.4.1.311.20.2.3 30upnObjectIdentifier = "1.3.6.1.4.1.311.20.2.3" 31 32 33class RedfishSessionContext: 34 def __init__(self, client, username="root", password="0penBmc"): 35 self.client = client 36 self.session_uri = None 37 self.x_auth_token = None 38 self.username = username 39 self.password = password 40 41 def __enter__(self): 42 r = self.client.post( 43 "/redfish/v1/SessionService/Sessions", 44 json={ 45 "UserName": self.username, 46 "Password": self.password, 47 "Context": f"pythonscript::{os.path.basename(__file__)}", 48 }, 49 headers={"content-type": "application/json"}, 50 ) 51 r.raise_for_status() 52 self.x_auth_token = r.headers["x-auth-token"] 53 self.session_uri = r.headers["location"] 54 return self 55 56 def __exit__(self, type, value, traceback): 57 if not self.session_uri: 58 return 59 r = self.client.delete(self.session_uri) 60 r.raise_for_status() 61 62 63def get_hostname(redfish_session, username, password, url): 64 service_root = redfish_session.get("/redfish/v1/") 65 service_root.raise_for_status() 66 67 manager_uri = service_root.json()["Links"]["ManagerProvidingService"][ 68 "@odata.id" 69 ] 70 71 manager_response = redfish_session.get(manager_uri) 72 manager_response.raise_for_status() 73 74 network_protocol_uri = manager_response.json()["NetworkProtocol"][ 75 "@odata.id" 76 ] 77 78 network_protocol_response = redfish_session.get(network_protocol_uri) 79 network_protocol_response.raise_for_status() 80 81 hostname = network_protocol_response.json()["HostName"] 82 83 return hostname 84 85 86def generateCA(): 87 private_key = ec.generate_private_key(ec.SECP256R1()) 88 public_key = private_key.public_key() 89 builder = x509.CertificateBuilder() 90 91 name = x509.Name( 92 [ 93 x509.NameAttribute(NameOID.ORGANIZATION_NAME, "OpenBMC"), 94 x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "bmcweb"), 95 x509.NameAttribute(NameOID.COMMON_NAME, "Test CA"), 96 ] 97 ) 98 builder = builder.subject_name(name) 99 builder = builder.issuer_name(name) 100 101 builder = builder.not_valid_before( 102 datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) 103 ) 104 builder = builder.not_valid_after( 105 datetime.datetime(2070, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) 106 ) 107 builder = builder.serial_number(x509.random_serial_number()) 108 builder = builder.public_key(public_key) 109 110 basic_constraints = x509.BasicConstraints(ca=True, path_length=None) 111 builder = builder.add_extension(basic_constraints, critical=True) 112 113 usage = x509.KeyUsage( 114 content_commitment=False, 115 crl_sign=True, 116 data_encipherment=False, 117 decipher_only=False, 118 digital_signature=False, 119 encipher_only=False, 120 key_agreement=False, 121 key_cert_sign=True, 122 key_encipherment=False, 123 ) 124 builder = builder.add_extension(usage, critical=False) 125 126 auth_key = x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key) 127 128 builder = builder.add_extension(auth_key, critical=False) 129 130 root_cert = builder.sign( 131 private_key=private_key, algorithm=hashes.SHA256() 132 ) 133 134 return private_key, root_cert 135 136 137def signCsr(csr, ca_key): 138 csr.sign(ca_key, algorithm=hashes.SHA256()) 139 return 140 141 142def generate_client_key_and_cert( 143 ca_cert, 144 ca_key, 145 common_name: Optional[str] = None, 146 upn: Optional[str] = None, 147): 148 private_key = ec.generate_private_key(ec.SECP256R1()) 149 public_key = private_key.public_key() 150 builder = x509.CertificateBuilder() 151 152 cert_names = [ 153 x509.NameAttribute(NameOID.COUNTRY_NAME, "US"), 154 x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"), 155 x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"), 156 x509.NameAttribute(NameOID.ORGANIZATION_NAME, "OpenBMC"), 157 x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "bmcweb"), 158 ] 159 if common_name is not None: 160 cert_names.append(x509.NameAttribute(NameOID.COMMON_NAME, common_name)) 161 162 builder = builder.subject_name(x509.Name(cert_names)) 163 164 builder = builder.issuer_name(ca_cert.subject) 165 builder = builder.public_key(public_key) 166 builder = builder.serial_number(x509.random_serial_number()) 167 builder = builder.not_valid_before( 168 datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) 169 ) 170 builder = builder.not_valid_after( 171 datetime.datetime(2070, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) 172 ) 173 174 usage = x509.KeyUsage( 175 content_commitment=False, 176 crl_sign=False, 177 data_encipherment=False, 178 decipher_only=False, 179 digital_signature=True, 180 encipher_only=False, 181 key_agreement=True, 182 key_cert_sign=False, 183 key_encipherment=False, 184 ) 185 builder = builder.add_extension(usage, critical=False) 186 187 exusage = x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH]) 188 builder = builder.add_extension(exusage, critical=True) 189 190 auth_key = x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key) 191 builder = builder.add_extension(auth_key, critical=False) 192 193 if upn is not None: 194 encoder = asn1.Encoder() 195 encoder.start() 196 encoder.write(upn, asn1.Numbers.UTF8String) 197 198 builder = builder.add_extension( 199 x509.SubjectAlternativeName( 200 [ 201 x509.OtherName( 202 x509.ObjectIdentifier(upnObjectIdentifier), 203 encoder.output(), 204 ) 205 ] 206 ), 207 critical=False, 208 ) 209 210 signed = builder.sign(private_key=ca_key, algorithm=hashes.SHA256()) 211 212 return private_key, signed 213 214 215def generateServerCert(url, ca_key, ca_cert, csr): 216 builder = x509.CertificateBuilder() 217 218 builder = builder.subject_name(csr.subject) 219 builder = builder.issuer_name(ca_cert.subject) 220 builder = builder.public_key(csr.public_key()) 221 builder = builder.serial_number(x509.random_serial_number()) 222 builder = builder.not_valid_before( 223 datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) 224 ) 225 builder = builder.not_valid_after( 226 datetime.datetime(2070, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) 227 ) 228 229 usage = x509.KeyUsage( 230 content_commitment=False, 231 crl_sign=False, 232 data_encipherment=False, 233 decipher_only=False, 234 digital_signature=True, 235 encipher_only=False, 236 key_agreement=False, 237 key_cert_sign=True, 238 key_encipherment=True, 239 ) 240 builder = builder.add_extension(usage, critical=True) 241 242 exusage = x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.SERVER_AUTH]) 243 builder = builder.add_extension(exusage, critical=True) 244 245 san_list = [x509.DNSName("localhost")] 246 try: 247 value = ipaddress.ip_address(url) 248 san_list.append(x509.IPAddress(value)) 249 except ValueError: 250 san_list.append(x509.DNSName(url)) 251 252 altname = x509.SubjectAlternativeName(san_list) 253 builder = builder.add_extension(altname, critical=True) 254 basic_constraints = x509.BasicConstraints(ca=False, path_length=None) 255 builder = builder.add_extension(basic_constraints, critical=True) 256 257 builder = builder.add_extension( 258 x509.SubjectKeyIdentifier.from_public_key(ca_key.public_key()), 259 critical=False, 260 ) 261 authkeyident = x509.AuthorityKeyIdentifier.from_issuer_public_key( 262 ca_key.public_key() 263 ) 264 builder = builder.add_extension(authkeyident, critical=False) 265 266 signed = builder.sign(private_key=ca_key, algorithm=hashes.SHA256()) 267 268 return signed 269 270 271def generateCsr( 272 redfish_session, 273 commonName, 274 manager_uri, 275): 276 try: 277 socket.inet_aton(commonName) 278 commonName = "IP: " + commonName 279 except socket.error: 280 commonName = "DNS: " + commonName 281 282 CSRRequest = { 283 "CommonName": commonName, 284 "City": "San Fransisco", 285 "Country": "US", 286 "Organization": "", 287 "OrganizationalUnit": "", 288 "State": "CA", 289 "CertificateCollection": { 290 "@odata.id": f"{manager_uri}/NetworkProtocol/HTTPS/Certificates", 291 }, 292 "AlternativeNames": [ 293 commonName, 294 "DNS: localhost", 295 "IP: 127.0.0.1", 296 ], 297 } 298 299 response = redfish_session.post( 300 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR", 301 json=CSRRequest, 302 ) 303 response.raise_for_status() 304 305 csrString = response.json()["CSRString"] 306 csr = x509.load_pem_x509_csr(csrString.encode()) 307 if not csr.is_signature_valid: 308 raise Exception("CSR was not valid") 309 return csr 310 311 312def install_ca_cert(redfish_session, ca_cert_dump, manager_uri): 313 ca_certJSON = { 314 "CertificateString": ca_cert_dump.decode(), 315 "CertificateType": "PEM", 316 } 317 ca_certPath = f"{manager_uri}/Truststore/Certificates" 318 print("Attempting to install CA certificate to BMC.") 319 320 response = redfish_session.post(ca_certPath, json=ca_certJSON) 321 if response.status_code == 500: 322 print( 323 "An existing CA certificate is likely already installed." 324 " Replacing..." 325 ) 326 ca_certJSON["CertificateUri"] = { 327 "@odata.id": ca_certPath + "/1", 328 } 329 330 response = redfish_session.post(replaceCertPath, json=ca_certJSON) 331 if response.status_code == 200: 332 print("Successfully replaced existing CA certificate.") 333 else: 334 raise Exception( 335 "Could not install or replace CA certificate." 336 "Please check if a certificate is already installed. If a" 337 "certificate is already installed, try performing a factory" 338 "restore to clear such settings." 339 ) 340 response.raise_for_status() 341 print("Successfully installed CA certificate.") 342 343 344def install_server_cert(redfish_session, manager_uri, server_cert_dump): 345 346 server_cert_json = { 347 "CertificateString": server_cert_dump.decode(), 348 "CertificateUri": { 349 "@odata.id": f"{manager_uri}/NetworkProtocol/HTTPS/Certificates/1", 350 }, 351 "CertificateType": "PEM", 352 } 353 354 print("Replacing server certificate...") 355 response = redfish_session.post(replaceCertPath, json=server_cert_json) 356 if response.status_code == 200: 357 print("Successfully replaced server certificate.") 358 else: 359 raise Exception(f"Could not replace certificate: {response.json()}") 360 361 tls_patch_json = {"Oem": {"OpenBMC": {"AuthMethods": {"TLS": True}}}} 362 print("Ensuring TLS authentication is enabled.") 363 response = redfish_session.patch( 364 "/redfish/v1/AccountService", json=tls_patch_json 365 ) 366 if response.status_code == 200: 367 print("Successfully enabled TLS authentication.") 368 else: 369 raise Exception("Could not enable TLS auth: " + response.read) 370 371 372def generate_pk12(certs_dir, key, client_cert, username): 373 print("Generating p12 cert file for browser authentication.") 374 p12 = pkcs12.serialize_key_and_certificates( 375 username.encode(), 376 key, 377 client_cert, 378 None, 379 serialization.NoEncryption(), 380 ) 381 with open(os.path.join(certs_dir, "client.p12"), "wb") as f: 382 f.write(p12) 383 384 385def test_mtls_auth(url, certs_dir, use_http2): 386 with httpx.Client( 387 base_url=f"https://{url}", 388 verify=os.path.join(certs_dir, "CA-cert.cer"), 389 cert=( 390 os.path.join(certs_dir, "client-cert.pem"), 391 os.path.join(certs_dir, "client-key.pem"), 392 ), 393 http2=use_http2, 394 ) as client: 395 print("Testing mTLS auth with CommonName") 396 response = client.get( 397 "/redfish/v1/SessionService/Sessions", 398 ) 399 response.raise_for_status() 400 401 print("Changing CertificateMappingAttribute to use UPN") 402 patch_json = { 403 "MultiFactorAuth": { 404 "ClientCertificate": { 405 "CertificateMappingAttribute": "UserPrincipalName" 406 } 407 } 408 } 409 response = client.patch( 410 "/redfish/v1/AccountService", 411 json=patch_json, 412 ) 413 response.raise_for_status() 414 415 with httpx.Client( 416 base_url=f"https://{url}", 417 verify=os.path.join(certs_dir, "CA-cert.cer"), 418 cert=( 419 os.path.join(certs_dir, "upn-client-cert.pem"), 420 os.path.join(certs_dir, "upn-client-key.pem"), 421 ), 422 http2=use_http2, 423 ) as client: 424 print("Retesting mTLS auth with UPN") 425 response = client.get( 426 "/redfish/v1/SessionService/Sessions", 427 ) 428 response.raise_for_status() 429 430 print("Changing CertificateMappingAttribute to use CommonName") 431 patch_json = { 432 "MultiFactorAuth": { 433 "ClientCertificate": { 434 "CertificateMappingAttribute": "CommonName" 435 } 436 } 437 } 438 response = client.patch( 439 "/redfish/v1/AccountService", 440 json=patch_json, 441 ) 442 response.raise_for_status() 443 444 445def setup_server_cert( 446 redfish_session, 447 ca_cert_dump, 448 certs_dir, 449 client_key, 450 client_cert, 451 username, 452 url, 453 server_intermediate_key, 454 server_intermediate_cert, 455): 456 service_root = redfish_session.get("/redfish/v1/") 457 service_root.raise_for_status() 458 459 manager_uri = service_root.json()["Links"]["ManagerProvidingService"][ 460 "@odata.id" 461 ] 462 463 install_ca_cert(redfish_session, ca_cert_dump, manager_uri) 464 generate_pk12(certs_dir, client_key, client_cert, username) 465 466 csr = generateCsr( 467 redfish_session, 468 url, 469 manager_uri, 470 ) 471 serverCert = generateServerCert( 472 url, 473 server_intermediate_key, 474 server_intermediate_cert, 475 csr, 476 ) 477 server_cert_dump = serverCert.public_bytes( 478 encoding=serialization.Encoding.PEM 479 ) 480 481 # If using intermediate certificate, save server cert without intermediate for debugging 482 if ( 483 isinstance(server_intermediate_cert, x509.Certificate) 484 and server_intermediate_cert.subject != server_intermediate_cert.issuer 485 ): 486 with open(os.path.join(certs_dir, "server-cert-only.pem"), "wb") as f: 487 f.write(server_cert_dump) 488 print("Server cert (without intermediate) saved for debugging.") 489 intermediate_cert_dump = server_intermediate_cert.public_bytes( 490 encoding=serialization.Encoding.PEM 491 ) 492 server_cert_dump = server_cert_dump + intermediate_cert_dump 493 494 with open(os.path.join(certs_dir, "server-cert.pem"), "wb") as f: 495 f.write(server_cert_dump) 496 print("Server cert generated.") 497 498 install_server_cert(redfish_session, manager_uri, server_cert_dump) 499 500 print("Make sure setting CertificateMappingAttribute to CommonName") 501 patch_json = { 502 "MultiFactorAuth": { 503 "ClientCertificate": {"CertificateMappingAttribute": "CommonName"} 504 } 505 } 506 response = redfish_session.patch( 507 "/redfish/v1/AccountService", json=patch_json 508 ) 509 response.raise_for_status() 510 511 512def generateIntermediateCA(ca_key, ca_cert, name_prefix): 513 private_key = ec.generate_private_key(ec.SECP256R1()) 514 public_key = private_key.public_key() 515 builder = x509.CertificateBuilder() 516 517 name = x509.Name( 518 [ 519 x509.NameAttribute(NameOID.COUNTRY_NAME, "US"), 520 x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"), 521 x509.NameAttribute(NameOID.LOCALITY_NAME, "Santa Clara"), 522 x509.NameAttribute(NameOID.ORGANIZATION_NAME, "OpenBMC"), 523 x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "bmcweb"), 524 x509.NameAttribute(NameOID.COMMON_NAME, "Test Intermediate"), 525 ] 526 ) 527 builder = builder.subject_name(name) 528 builder = builder.issuer_name(ca_cert.subject) 529 530 builder = builder.not_valid_before( 531 datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) 532 ) 533 builder = builder.not_valid_after( 534 datetime.datetime(2070, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) 535 ) 536 537 builder = builder.serial_number(x509.random_serial_number()) 538 builder = builder.public_key(public_key) 539 540 basic_constraints = x509.BasicConstraints(ca=True, path_length=0) 541 builder = builder.add_extension(basic_constraints, critical=True) 542 543 usage = x509.KeyUsage( 544 content_commitment=False, 545 crl_sign=True, 546 data_encipherment=False, 547 decipher_only=False, 548 digital_signature=False, 549 encipher_only=False, 550 key_agreement=False, 551 key_cert_sign=True, 552 key_encipherment=False, 553 ) 554 builder = builder.add_extension(usage, critical=True) 555 556 auth_key = x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key) 557 builder = builder.add_extension(auth_key, critical=False) 558 559 intermediate_cert = builder.sign( 560 private_key=ca_key, algorithm=hashes.SHA256() 561 ) 562 563 return private_key, intermediate_cert 564 565 566def generate_and_load_certs( 567 url, username, password, use_http2, use_intermediate=False 568): 569 certs_dir = os.path.expanduser("~/certs") 570 print(f"Writing certs to {certs_dir}") 571 try: 572 print("Making certs directory.") 573 os.mkdir(certs_dir) 574 except OSError as error: 575 if error.errno != errno.EEXIST: 576 raise 577 578 ca_cert_filename = os.path.join(certs_dir, "CA-cert.cer") 579 ca_key_filename = os.path.join(certs_dir, "CA-key.pem") 580 if not os.path.exists(ca_cert_filename): 581 ca_key, ca_cert = generateCA() 582 583 ca_key_dump = ca_key.private_bytes( 584 encoding=serialization.Encoding.PEM, 585 format=serialization.PrivateFormat.TraditionalOpenSSL, 586 encryption_algorithm=serialization.NoEncryption(), 587 ) 588 ca_cert_dump = ca_cert.public_bytes( 589 encoding=serialization.Encoding.PEM 590 ) 591 592 with open(ca_cert_filename, "wb") as f: 593 f.write(ca_cert_dump) 594 print("CA cert generated.") 595 with open(ca_key_filename, "wb") as f: 596 f.write(ca_key_dump) 597 print("CA key generated.") 598 599 with open(ca_cert_filename, "rb") as ca_cert_file: 600 ca_cert_dump = ca_cert_file.read() 601 ca_cert = x509.load_pem_x509_certificate(ca_cert_dump) 602 603 with open(ca_key_filename, "rb") as ca_key_file: 604 ca_key_dump = ca_key_file.read() 605 ca_key = load_pem_private_key(ca_key_dump, None) 606 607 # Generate intermediate certificates if requested 608 if use_intermediate: 609 # Generate client intermediate 610 client_intermediate_key, client_intermediate_cert = ( 611 generateIntermediateCA(ca_key, ca_cert, "client") 612 ) 613 # ... save client intermediate certs ... 614 615 # Generate server intermediate 616 server_intermediate_key, server_intermediate_cert = ( 617 generateIntermediateCA(ca_key, ca_cert, "server") 618 ) 619 else: 620 client_intermediate_key = ca_key 621 client_intermediate_cert = ca_cert 622 server_intermediate_key = ca_key 623 server_intermediate_cert = ca_cert 624 625 # Update client cert generation to use intermediate 626 client_key, client_cert = generate_client_key_and_cert( 627 ca_cert=client_intermediate_cert, 628 ca_key=client_intermediate_key, 629 common_name=username, 630 ) 631 client_key_dump = client_key.private_bytes( 632 encoding=serialization.Encoding.PEM, 633 format=serialization.PrivateFormat.TraditionalOpenSSL, 634 encryption_algorithm=serialization.NoEncryption(), 635 ) 636 637 with open(os.path.join(certs_dir, "client-key.pem"), "wb") as f: 638 f.write(client_key_dump) 639 print("Client key generated.") 640 client_cert_dump = client_cert.public_bytes( 641 encoding=serialization.Encoding.PEM 642 ) 643 644 # Save client certificate without intermediate for debugging 645 if use_intermediate: 646 with open(os.path.join(certs_dir, "client-cert-only.pem"), "wb") as f: 647 f.write(client_cert_dump) 648 print("Client cert (without intermediate) saved for debugging.") 649 client_cert_dump = ( 650 client_cert_dump 651 + client_intermediate_cert.public_bytes( 652 encoding=serialization.Encoding.PEM 653 ) 654 ) # Append intermediate to create chain 655 656 with open(os.path.join(certs_dir, "client-cert.pem"), "wb") as f: 657 f.write(client_cert_dump) 658 print("Client cert generated.") 659 660 print(f"Connecting to {url}") 661 with httpx.Client( 662 base_url=f"https://{url}", 663 verify=False, 664 follow_redirects=False, 665 http2=use_http2, 666 ) as redfish_session: 667 with RedfishSessionContext( 668 redfish_session, username, password 669 ) as rf_session: 670 redfish_session.headers["X-Auth-Token"] = rf_session.x_auth_token 671 672 hostname = get_hostname(redfish_session, username, password, url) 673 print(f"Hostname: {hostname}") 674 675 upn_client_key, upn_client_cert = generate_client_key_and_cert( 676 ca_cert, 677 ca_key, 678 upn=f"{username}@{hostname}", 679 ) 680 upn_client_key_dump = upn_client_key.private_bytes( 681 encoding=serialization.Encoding.PEM, 682 format=serialization.PrivateFormat.TraditionalOpenSSL, 683 encryption_algorithm=serialization.NoEncryption(), 684 ) 685 with open( 686 os.path.join(certs_dir, "upn-client-key.pem"), "wb" 687 ) as f: 688 f.write(upn_client_key_dump) 689 print("UPN client key generated.") 690 691 upn_client_cert_dump = upn_client_cert.public_bytes( 692 encoding=serialization.Encoding.PEM 693 ) 694 with open( 695 os.path.join(certs_dir, "upn-client-cert.pem"), "wb" 696 ) as f: 697 f.write(upn_client_cert_dump) 698 print("UPN client cert generated.") 699 700 setup_server_cert( 701 redfish_session, 702 ca_cert_dump, 703 certs_dir, 704 client_key, 705 client_cert, 706 username, 707 url, 708 server_intermediate_key, 709 server_intermediate_cert, 710 ) 711 712 print("Testing redfish TLS authentication with generated certs.") 713 time.sleep(2) 714 test_mtls_auth(url, certs_dir, use_http2) 715 print("Redfish TLS authentication success!") 716 717 718def main(): 719 parser = argparse.ArgumentParser() 720 parser.add_argument( 721 "--username", 722 help="Username to connect with", 723 default="root", 724 ) 725 parser.add_argument( 726 "--password", 727 help="Password for user in order to install certs over Redfish.", 728 default="0penBmc", 729 ) 730 parser.add_argument( 731 "--use-intermediate", 732 action="store_true", 733 default=False, 734 help="Generate and use an intermediate certificate", 735 ) 736 parser.add_argument( 737 "--http2", 738 action=argparse.BooleanOptionalAction, 739 default=False, 740 help="Use HTTP2 for testing", 741 ) 742 parser.add_argument("host", help="Host to connect to") 743 744 args = parser.parse_args() 745 generate_and_load_certs( 746 args.host, 747 args.username, 748 args.password, 749 args.http2, 750 args.use_intermediate, 751 ) 752 753 754if __name__ == "__main__": 755 main() 756