1 # ============LICENSE_START=======================================================
2 # Copyright (C) 2019 Nordix Foundation.
3 # ================================================================================
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 # SPDX-License-Identifier: Apache-2.0
17 # ============LICENSE_END=========================================================
33 log_file = '/opt/opendaylight/data/log/installCerts.log'
34 with open(os.path.join('/opt/opendaylight/data/log', 'installCerts.log'), 'w') as fp:
37 log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
38 logging.basicConfig(filename=log_file,level=logging.DEBUG,filemode='w',format=log_format)
44 username = os.environ['ODL_ADMIN_USERNAME']
45 password = os.environ['ODL_ADMIN_PASSWORD']
50 postKeystore= "/rests/operations/netconf-keystore:add-keystore-entry"
51 postPrivateKey= "/rests/operations/netconf-keystore:add-private-key"
52 postTrustedCertificate= "/rests/operations/netconf-keystore:add-trusted-certificate"
54 truststore_pass_file = Path + '/truststore.pass'
55 truststore_file = Path + '/truststore.jks'
57 keystore_pass_file = Path + '/keystore.pass'
58 keystore_file = Path + '/keystore.jks'
60 jks_files = [truststore_pass_file, keystore_pass_file, keystore_file, truststore_file]
63 cred_string = username + ":" + password
64 headers = {'Authorization':'Basic %s' % base64.b64encode(cred_string.encode()).decode(),
65 'X-FromAppId': 'csit-sdnc',
66 'X-TransactionId': 'csit-sdnc',
67 'Accept':"application/json",
68 'Content-type':"application/yang-data+json"}
71 def readFile(folder, file):
72 key = open(Path + "/" + folder + "/" + file, "r")
75 fileRead = "\n".join(fileRead.splitlines()[1:-1])
79 def readTrustedCertificate(folder, file):
83 key = open(folder + "/" + file, "r")
84 lines = key.readlines()
86 if not "BEGIN CERTIFICATE" in line and not "END CERTIFICATE" in line and startCa:
88 elif "BEGIN CERTIFICATE" in line:
90 elif "END CERTIFICATE" in line:
92 listCert.append(caPem)
97 def makeKeystoreKey(clientKey, count):
98 odl_private_key = "ODL_private_key_%d" %count
100 json_keystore_key='{{\"input\": {{ \"key-credential\": {{\"key-id\": \"{odl_private_key}\", \"private-key\" : ' \
101 '\"{clientKey}\",\"passphrase\" : \"\"}}}}}}'.format(
102 odl_private_key=odl_private_key,
105 return json_keystore_key
108 def makePrivateKey(clientKey, clientCrt, certList, count):
111 for cert in certList:
112 caPem += '\"%s\",' % cert
113 caPem = caPem.rsplit(',', 1)[0]
114 odl_private_key="ODL_private_key_%d" %count
116 json_private_key='{{\"input\": {{ \"private-key\":{{\"name\": \"{odl_private_key}\", \"data\" : ' \
117 '\"{clientKey}\",\"certificate-chain\":[\"{clientCrt}\",{caPem}]}}}}}}'.format(
118 odl_private_key=odl_private_key,
123 return json_private_key
126 def makeTrustedCertificate(certList, count):
128 json_cert_format = ""
129 for cert in certList:
130 cert_name = "xNF_CA_certificate_%d_%d" %(count, number)
131 json_cert_format += '{{\"name\": \"{trusted_name}\",\"certificate\":\"{cert}\"}},\n'.format(
132 trusted_name=cert_name,
136 json_cert_format = json_cert_format.rsplit(',', 1)[0]
137 json_trusted_cert='{{\"input\": {{ \"trusted-certificate\": [{certificates}]}}}}'.format(
138 certificates=json_cert_format)
139 return json_trusted_cert
142 def makeRestconfPost(conn, json_file, apiCall):
143 req = conn.request("POST", apiCall, json_file, headers=headers)
144 res = conn.getresponse()
146 if res.status != 200:
147 logging.error("Error here, response back wasnt 200: Response was : %d , %s" % (res.status, res.reason))
149 logging.debug("Response :%s Reason :%s ",res.status, res.reason)
152 def extractZipFiles(zipFileList, count):
153 for zipFolder in zipFileList:
154 with zipfile.ZipFile(Path + "/" + zipFolder.strip(),"r") as zip_ref:
155 zip_ref.extractall(Path)
156 folder = zipFolder.rsplit(".")[0]
157 processFiles(folder, count)
160 def processFiles(folder, count):
161 for file in os.listdir(Path + "/" + folder):
162 if os.path.isfile(Path + "/" + folder + "/" + file.strip()):
164 clientKey = readFile(folder, file.strip())
165 elif "trustedCertificate" in file:
166 certList = readTrustedCertificate(Path + "/" + folder, file.strip())
168 clientCrt = readFile(folder, file.strip())
170 logging.error("Could not find file %s" % file.strip())
171 shutil.rmtree(Path + "/" + folder)
172 post_content(clientKey, clientCrt, certList, count)
175 def post_content(clientKey, clientCrt, certList, count):
176 conn = http.client.HTTPConnection("localhost",odl_port)
179 json_keystore_key = makeKeystoreKey(clientKey, count)
180 logging.debug("Posting private key in to ODL keystore")
181 makeRestconfPost(conn, json_keystore_key, postKeystore)
184 json_trusted_cert = makeTrustedCertificate(certList, count)
185 logging.debug("Posting trusted cert list in to ODL")
186 makeRestconfPost(conn, json_trusted_cert, postTrustedCertificate)
188 if clientKey and clientCrt and certList:
189 json_private_key = makePrivateKey(clientKey, clientCrt, certList, count)
190 logging.debug("Posting the cert in to ODL")
191 makeRestconfPost(conn, json_private_key, postPrivateKey)
194 def makeHealthcheckCall(headers, timePassed):
196 # WAIT 10 minutes maximum and test every 30 seconds if HealthCheck API is returning 200
197 while timePassed < TIMEOUT:
199 conn = http.client.HTTPConnection("localhost",odl_port)
200 req = conn.request("POST", "/rests/operations/SLI-API:healthcheck",headers=headers)
201 res = conn.getresponse()
203 if res.status == 200:
204 logging.debug("Healthcheck Passed in %d seconds." %timePassed)
208 logging.debug("Sleep: %d seconds before testing if Healthcheck worked. Total wait time up now is: %d seconds. Timeout is: %d seconds" %(INTERVAL, timePassed, TIMEOUT))
210 logging.error("Cannot execute REST call. Sleep: %d seconds before testing if Healthcheck worked. Total wait time up now is: %d seconds. Timeout is: %d seconds" %(INTERVAL, timePassed, TIMEOUT))
211 timePassed = timeIncrement(timePassed)
213 if timePassed > TIMEOUT:
214 logging.error("TIME OUT: Healthcheck not passed in %d seconds... Could cause problems for testing activities..." %TIMEOUT)
218 def timeIncrement(timePassed):
220 timePassed = timePassed + INTERVAL
224 def get_pass(file_name):
226 with open(file_name, 'r') as file_obj:
227 password = file_obj.read().strip()
228 return "'{}'".format(password)
229 except Exception as e:
230 logging.error("Error occurred while fetching password : %s", e)
235 for file in jks_files:
236 if os.path.isfile(file):
237 logging.debug("Cleaning up the file %s", file)
241 def jks_to_p12(file, password):
242 """Converts jks format into p12"""
244 p12_file = file.replace('.jks', '.p12')
245 jks_cmd = 'keytool -importkeystore -srckeystore {src_file} -destkeystore {dest_file} -srcstoretype JKS -srcstorepass {src_pass} -deststoretype PKCS12 -deststorepass {dest_pass}'.format(src_file=file, dest_file=p12_file, src_pass=password, dest_pass=password)
246 logging.debug("Converting %s into p12 format", file)
250 except Exception as e:
251 logging.error("Error occurred while converting jks to p12 format : %s", e)
254 def make_cert_chain(cert_chain, pattern):
257 matches = re.findall(pattern, cert_chain, re.DOTALL | re.MULTILINE)
259 cert_list.append(cert.strip())
262 logging.debug(" Certificate Chain empty: %s " % cert_chain)
265 def process_jks_files(count):
267 logging.info("Processing JKS files found in %s directory " % Path)
269 if all([os.path.isfile(f) for f in jks_files]):
270 keystore_pass = get_pass(keystore_pass_file)
271 keystore_file_p12 = jks_to_p12(keystore_file, keystore_pass)
273 client_key_cmd = 'openssl pkcs12 -in {src_file} -nocerts -nodes -passin pass:{src_pass}'.format(
274 src_file=keystore_file_p12, src_pass=keystore_pass)
275 client_crt_cmd = 'openssl pkcs12 -in {src_file} -clcerts -nokeys -passin pass:{src_pass}'.format(
276 src_file=keystore_file_p12, src_pass=keystore_pass)
278 truststore_pass = get_pass(truststore_pass_file)
279 truststore_p12 = jks_to_p12(truststore_file, truststore_pass)
281 trust_cert_cmd = 'openssl pkcs12 -in {src_file} -cacerts -nokeys -passin pass:{src_pass} '.format(
282 src_file=truststore_p12, src_pass=truststore_pass)
284 key_pattern = r'(?<=-----BEGIN PRIVATE KEY-----).*?(?=-----END PRIVATE KEY-----)'
285 client_key = subprocess.check_output(client_key_cmd, shell=True)
287 client_key = make_cert_chain(client_key, key_pattern)[0]
288 logging.debug("Key Ok")
290 cert_pattern = r'(?<=-----BEGIN CERTIFICATE-----).*?(?=-----END CERTIFICATE-----)'
291 client_cert = subprocess.check_output(client_crt_cmd, shell=True)
293 client_cert = make_cert_chain(client_cert, cert_pattern)[0]
294 logging.debug("Client Cert Ok")
296 ca_cert = subprocess.check_output(trust_cert_cmd, shell=True)
298 ca_cert_list = make_cert_chain(ca_cert, cert_pattern)
299 logging.debug("CA Cert Ok")
301 if client_key and client_cert and ca_cert:
302 post_content(client_key, client_cert, ca_cert_list, count)
304 logging.debug("No JKS files found in %s directory" % Path)
305 except subprocess.CalledProcessError as err:
306 print("CalledProcessError Execution of OpenSSL command failed: %s" % err)
307 except Exception as e:
308 logging.error("UnExpected Error while processing JKS files at {0}, Caused by: {1}".format(Path, e))
311 def readCertProperties():
313 This function searches for manually copied zip file
314 containing certificates. This is required as part
315 of backward compatibility.
316 If not foud, it searches for jks certificates.
318 connected = makeHealthcheckCall(headers, timePassed)
322 if os.path.isfile(Path + "/certs.properties"):
323 with open(Path + "/certs.properties", "r") as f:
325 if not "*****" in line:
326 zipFileList.append(line)
328 extractZipFiles(zipFileList, count)
332 logging.debug("No certs.properties/zip files exist at: " + Path)
333 process_jks_files(count)