From 2ef695324a99a347da6e735be30cf77b9d5844a9 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 24 Jun 2021 14:35:21 -0400 Subject: [PATCH] Migrate genesis to new TPM strategy Have addons for genesis implement the same TPM usage model as the suse/redhat stateless. --- .../common/opt/confluent/bin/apiclient | 64 ++++++++++--------- .../initramfs/opt/confluent/bin/rungenesis | 40 ++++++++++++ 2 files changed, 74 insertions(+), 30 deletions(-) diff --git a/confluent_osdeploy/common/opt/confluent/bin/apiclient b/confluent_osdeploy/common/opt/confluent/bin/apiclient index 5f60c7f3..181171fb 100644 --- a/confluent_osdeploy/common/opt/confluent/bin/apiclient +++ b/confluent_osdeploy/common/opt/confluent/bin/apiclient @@ -8,34 +8,28 @@ import socket import subprocess import ssl import sys +import time + +class InvalidApiKey(Exception): + pass def get_apikey(nodename, mgr): - sealnew = True + apikey = "" if os.path.exists('/etc/confluent/confluent.apikey'): - return open('/etc/confluent/confluent.apikey').read().strip() - apikey = subprocess.check_output(['/opt/confluent/bin/clortho', nodename, mgr]) - if not isinstance(apikey, str): - apikey = apikey.decode('utf8') - if apikey.startswith('SEALED:'): - sealnew = False - with open('/etc/confluent/confluent.sealedapikey', 'w+') as apiout: - apiout.write(apikey[7:]) - with open('/etc/confluent/confluent.sealedapikey') as inp: - sp = subprocess.Popen(['/usr/bin/clevis-decrypt-tpm2'], - stdin=inp, stdout=subprocess.PIPE) - apikey = sp.communicate()[0] - if not isinstance(apikey, str): - apikey = apikey.decode('utf8') + apikey = open('/etc/confluent/confluent.apikey').read().strip() + if apikey: + return apikey + while not apikey: + apikey = subprocess.check_output(['/opt/confluent/bin/clortho', nodename, mgr]) + if not isinstance(apikey, str): + apikey = apikey.decode('utf8') + if apikey.startswith('SEALED:'): + apikey = "" + sys.stderr.write( + "Failed getting API token, check deployment.apiarmed attribute on {}\n".format(nodename)) + time.sleep(10) with open('/etc/confluent/confluent.apikey', 'w+') as apiout: apiout.write(apikey) - if sealnew and os.path.exists('/usr/bin/clevis-encrypt-tpm2'): - try: - with open('/etc/confluent/confluent.apikey') as apin: - sealed = subprocess.check_output( - ['/usr/bin/clevis-encrypt-tpm2', '{}'], stdin=apin) - print(HTTPSClient().grab_url('/confluent-api/self/saveapikey', sealed).decode()) - except Exception: - sys.stderr.write('Unable to persist API key through TPM2 sealing\n') apikey = apikey.strip() os.chmod('/etc/confluent/confluent.apikey', 0o600) return apikey @@ -79,6 +73,8 @@ class HTTPSClient(client.HTTPConnection, object): if mgtiface: self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface client.HTTPConnection.__init__(self, host, port) + self.host = host + self.node = node self.connect() def set_header(self, key, val): @@ -107,13 +103,21 @@ class HTTPSClient(client.HTTPConnection, object): method = 'POST' else: method = 'GET' - self.request(method, url, data, headers=self.stdheaders) - rsp = self.getresponse() - if rsp.status >= 200 and rsp.status < 300: - if returnrsp: - return rsp.status, rsp - else: - return rsp.status, rsp.read() + authed = False + while not authed: + authed = True + self.request(method, url, data, headers=self.stdheaders) + rsp = self.getresponse() + if rsp.status >= 200 and rsp.status < 300: + if returnrsp: + return rsp.status, rsp + else: + return rsp.status, rsp.read() + if rsp.status == 401: + authed = False + rsp.read() + self.stdheaders['CONFLUENT_APIKEY'] = get_apikey( + self.node, self.host) raise Exception(rsp.read()) if __name__ == '__main__': diff --git a/confluent_osdeploy/genesis/initramfs/opt/confluent/bin/rungenesis b/confluent_osdeploy/genesis/initramfs/opt/confluent/bin/rungenesis index f12cdb3f..6c8a0180 100644 --- a/confluent_osdeploy/genesis/initramfs/opt/confluent/bin/rungenesis +++ b/confluent_osdeploy/genesis/initramfs/opt/confluent/bin/rungenesis @@ -55,7 +55,47 @@ done cd / nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}') hostname $nodename +tpmdir=$(mktemp -d) +cd $tpmdir +lasthdl="" +oldumask=$(umask) +umask 0077 +for hdl in $(tpm2_getcap handles-persistent|awk '{print $2}'); do + tpm2_startauthsession --policy-session --session=session.ctx + tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy + unsealeddata=$(tpm2_unseal --auth=session:session.ctx -Q -c $hdl 2>/dev/null) + tpm2_flushcontext session.ctx + if [[ $unsealeddata == "CONFLUENT_APIKEY:"* ]]; then + confluent_apikey=${unsealeddata#CONFLUENT_APIKEY:} + echo $confluent_apikey > /etc/confluent/confluent.apikey + if [ -n "$lasthdl" ]; then + tpm2_evictcontrol -c $lasthdl + fi + lasthdl=$hdl + fi +done +cd - +rm -rf $tpmdir /usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg +umask $oldumask +new_apikey=$(cat /etc/confluent/confluent.apikey) +if [ "$new_apikey" != "$confluent_apikey" ]; then + if [ -n "$lasthdl" ]; then + tpm2_evictcontrol -c $lasthdl + fi + tmpdir=$(mktemp -d) + cd $tmpdir + tpm2_startauthsession --session=session.ctx + tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy + tpm2_createprimary -G ecc -Q --key-context=prim.ctx + (echo -n "CONFLUENT_APIKEY:$new_apikey") | tpm2_create -Q --policy=pcr15.sha256.policy --public=data.pub --private=data.priv -i - -C prim.ctx + tpm2_load -Q --parent-context=prim.ctx --public=data.pub --private=data.priv --name=confluent.apikey --key-context=data.ctx + tpm2_evictcontrol -Q -c data.ctx + tpm2_flushcontext session.ctx + cd - + rm -rf $tmpdir +fi +tpm2_pcrextend 15:sha256=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2 2> /dev/null ifidx=$(cat /tmp/confluent.ifidx) ifname=$(ip link |grep ^$ifidx:|awk '{print $2}') ifname=${ifname%:}