2
0
mirror of https://github.com/xcat2/confluent.git synced 2024-11-24 18:41:55 +00:00

Provide components for cert management with modern XCC

Refresh getcsr and installcert to handle latest firmware.

Also add ability to have pre-existing CSR, and trust the SAN on the way through.

If this becomes more properly a feature, then would likely impose a SAN
on certs, similar to the SSH principals, rather than deferring to the CSR
to get it right.
This commit is contained in:
Jarrod Johnson 2024-04-01 12:13:21 -04:00
parent 77bb03e96c
commit 34804b2d5f
3 changed files with 112 additions and 33 deletions

View File

@ -206,7 +206,7 @@ def create_simple_ca(keyout, certout):
finally:
os.remove(tmpconfig)
def create_certificate(keyout=None, certout=None):
def create_certificate(keyout=None, certout=None, csrout=None):
if not keyout:
keyout, certout = get_certificate_paths()
if not keyout:
@ -214,9 +214,10 @@ def create_certificate(keyout=None, certout=None):
assure_tls_ca()
shortname = socket.gethostname().split('.')[0]
longname = shortname # socket.getfqdn()
subprocess.check_call(
['openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
keyout])
if not csrout:
subprocess.check_call(
['openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
keyout])
san = ['IP:{0}'.format(x) for x in get_ip_addresses()]
# It is incorrect to put IP addresses as DNS type. However
# there exists non-compliant clients that fail with them as IP
@ -229,21 +230,34 @@ def create_certificate(keyout=None, certout=None):
os.close(tmphdl)
tmphdl, extconfig = tempfile.mkstemp()
os.close(tmphdl)
tmphdl, csrout = tempfile.mkstemp()
os.close(tmphdl)
needcsr = False
if csrout is None:
needcsr = True
tmphdl, csrout = tempfile.mkstemp()
os.close(tmphdl)
shutil.copy2(sslcfg, tmpconfig)
serialnum = '0x' + ''.join(['{:02x}'.format(x) for x in bytearray(os.urandom(20))])
try:
with open(tmpconfig, 'a') as cfgfile:
cfgfile.write('\n[SAN]\nsubjectAltName={0}'.format(san))
with open(extconfig, 'a') as cfgfile:
cfgfile.write('\nbasicConstraints=CA:false\nsubjectAltName={0}'.format(san))
subprocess.check_call([
'openssl', 'req', '-new', '-key', keyout, '-out', csrout, '-subj',
'/CN={0}'.format(longname),
'-extensions', 'SAN', '-config', tmpconfig
])
if needcsr:
with open(tmpconfig, 'a') as cfgfile:
cfgfile.write('\n[SAN]\nsubjectAltName={0}'.format(san))
with open(extconfig, 'a') as cfgfile:
cfgfile.write('\nbasicConstraints=CA:false\nsubjectAltName={0}'.format(san))
subprocess.check_call([
'openssl', 'req', '-new', '-key', keyout, '-out', csrout, '-subj',
'/CN={0}'.format(longname),
'-extensions', 'SAN', '-config', tmpconfig
])
else:
# when used manually, allow the csr SAN to stand
# may add explicit subj/SAN argument, in which case we would skip copy
with open(tmpconfig, 'a') as cfgfile:
cfgfile.write('\ncopy_extensions=copy\n')
with open(extconfig, 'a') as cfgfile:
cfgfile.write('\nbasicConstraints=CA:false\n')
if os.path.exists('/etc/confluent/tls/cakey.pem'):
# simple style CA in effect, make a random serial number and
# hope for the best, and accept inability to backdate the cert
serialnum = '0x' + ''.join(['{:02x}'.format(x) for x in bytearray(os.urandom(20))])
subprocess.check_call([
'openssl', 'x509', '-req', '-in', csrout,
'-CA', '/etc/confluent/tls/cacert.pem',
@ -252,20 +266,40 @@ def create_certificate(keyout=None, certout=None):
'-extfile', extconfig
])
else:
# we moved to a 'proper' CA, mainly for access to backdating
# start of certs for finicky system clocks
# this also provides a harder guarantee of serial uniqueness, but
# not of practical consequence (160 bit random value is as good as
# guaranteed unique)
# downside is certificate generation is serialized
cacfgfile = '/etc/confluent/tls/ca/openssl.cfg'
if needcsr:
tmphdl, tmpcafile = tempfile.mkstemp()
shutil.copy2(cacfgfile, tmpcafile)
os.close(tmphdl)
cacfgfile = tmpcafile
# with realcalock: # if we put it in server, we must lock it
subprocess.check_call([
'openssl', 'ca', '-config', '/etc/confluent/tls/ca/openssl.cfg',
'openssl', 'ca', '-config', cacfgfile,
'-in', csrout, '-out', certout, '-batch', '-notext',
'-startdate', '19700101010101Z', '-enddate', '21000101010101Z',
'-extfile', extconfig
])
finally:
os.remove(tmpconfig)
os.remove(csrout)
os.remove(extconfig)
if needcsr:
os.remove(csrout)
print(extconfig) # os.remove(extconfig)
if __name__ == '__main__':
import sys
outdir = os.getcwd()
keyout = os.path.join(outdir, 'key.pem')
certout = os.path.join(outdir, 'cert.pem')
create_certificate(keyout, certout)
certout = os.path.join(outdir, sys.argv[2] + 'cert.pem')
csrout = None
try:
csrout = sys.argv[1]
except IndexError:
csrout = None
create_certificate(keyout, certout, csrout)

View File

@ -12,11 +12,40 @@ ap.add_argument('--state', help='State or Province')
ap.add_argument('--city', help='City or Locality')
ap.add_argument('--org', help='Organization name')
ap.add_argument('--name', help='Common/Host Name')
ap.add_argument('outcsr', help='CSR filename to save')
args = ap.parse_args()
c = cmd.Command(args.xcc, os.environ['XCCUSER'], os.environ['XCCPASS'],
verifycallback=lambda x: True)
params = [
overview = c._do_web_request('/redfish/v1/')
cs = overview.get('CertificateService', {}).get('@odata.id', None)
if cs:
csinfo = c._do_web_request(cs)
gcsr = csinfo.get('Actions', {}).get('#CertificateService.GenerateCSR', {}).get('target', None)
if gcsr:
#https://n241-bmc/redfish/v1/Managers/1/NetworkProtocol HTTPS
#/redfish/v1/Managers/1/NetworkProtocol/HTTPS/Certificates
#/redfish/v1/CertificateService/CertificateLocations
csrargs = {
'City': args.city,
'State': args.state,
'Organization': args.org,
'Country': args.country,
'CommonName': args.name,
'KeyPairAlgorithm': 'TPM_ALG_ECDH',
'KeyCurveId': 'TPM_ECC_NIST_P384',
'CertificateCollection': { '@odata.id': '/redfish/v1/Managers/1/NetworkProtocol/HTTPS/Certificates'}
}
csrinfo = c._do_web_request(gcsr, csrargs)
if 'CSRString' in csrinfo:
with open(args.outcsr, 'w') as csrout:
csrout.write(csrinfo['CSRString'])
sys.exit(0)
else:
params = [
'0', # 'serviceType'
args.country,
args.state,
@ -32,15 +61,16 @@ params = [
'',
'',
'',
]
wc = c.oem.wc
rsp, status = wc.grab_json_response_with_status('/api/function', {'Sec_GenKeyAndCSR': ','.join(params)})
rsp, status = wc.grab_json_response_with_status('/api/dataset', {'CSR_Format': '1'})
rsp, status = wc.grab_json_response_with_status('/api/function', {'Sec_DownloadCSRANDCert': '0,4,0'})
wc.request('GET', '/download/{0}'.format(rsp['FileName']))
rsp = wc.getresponse()
csr = rsp.read()
if rsp.getheader('Content-Encoding', None) == 'gzip':
csr = gzip.GzipFile(fileobj=io.BytesIO(csr)).read()
print(csr)
]
wc = c.oem.wc
rsp, status = wc.grab_json_response_with_status('/api/function', {'Sec_GenKeyAndCSR': ','.join(params)})
rsp, status = wc.grab_json_response_with_status('/api/dataset', {'CSR_Format': '1'})
rsp, status = wc.grab_json_response_with_status('/api/function', {'Sec_DownloadCSRANDCert': '0,4,0'})
wc.request('GET', '/download/{0}'.format(rsp['FileName']))
rsp = wc.getresponse()
csr = rsp.read()
if rsp.getheader('Content-Encoding', None) == 'gzip':
csr = gzip.GzipFile(fileobj=io.BytesIO(csr)).read()
print(csr)

View File

@ -8,8 +8,23 @@ ap.add_argument('xcc', help='XCC address')
ap.add_argument('cert', help='Certificate in PEM format')
args = ap.parse_args()
cert = open(args.cert, 'r').read()
c = cmd.Command(args.xcc, os.environ['XCCUSER'], os.environ['XCCPASS'],
verifycallback=lambda x: True)
overview = c._do_web_request('/redfish/v1/')
cs = overview.get('CertificateService', {}).get('@odata.id', None)
if cs:
csinfo = c._do_web_request(cs)
gcsr = csinfo.get('Actions', {}).get('#CertificateService.ReplaceCertificate', {}).get('target', None)
if gcsr:
repcertargs = {
'CertificateUri': { '@odata.id': '/redfish/v1/Managers/1/NetworkProtocol/HTTPS/Certificates/1' },
'CertificateType': 'PEM',
'CertificateString': cert }
print(repr(c._do_web_request(gcsr, repcertargs)))
sys.exit(0)
#CertificateService.ReplaceCertificate
wc = c.oem.wc
cert = open(args.cert, 'rb').read()
res = wc.grab_json_response_with_status('/api/function', {'Sec_ImportCert': '0,1,0,0,,{0}'.format(cert)})