1 # ============LICENSE_START=======================================================
2 # Copyright (C) 2019 Nordix Foundation.
3 # ================================================================================
4 # extended by highstreet technologies GmbH (c) 2020
5 # ================================================================================
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
18 # SPDX-License-Identifier: Apache-2.0
19 # ============LICENSE_END=========================================================
34 odl_home = os.environ['ODL_HOME']
35 log_directory = odl_home + '/data/log/'
36 log_file = log_directory + 'installCerts.log'
37 with open(os.path.join(log_directory, 'installCerts.log'), 'w') as fp:
39 log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
40 if not os.path.exists(log_directory):
41 os.makedirs(log_directory)
42 logging.basicConfig(filename=log_file,level=logging.DEBUG,filemode='w',format=log_format)
43 print ('Start cert provisioning. Log file: ' + log_file);
46 if "ODL_CERT_DIR" in os.environ:
47 Path = os.environ['ODL_CERT_DIR']
51 username = os.environ['ODL_ADMIN_USERNAME']
52 password = os.environ['ODL_ADMIN_PASSWORD']
53 newpassword = os.environ.get('ODL_ADMIN_NEWPASSWORD')
58 postKeystore= "/rests/operations/netconf-keystore:add-keystore-entry"
59 postPrivateKey= "/rests/operations/netconf-keystore:add-private-key"
60 postTrustedCertificate= "/rests/operations/netconf-keystore:add-trusted-certificate"
62 truststore_pass_file = Path + '/truststore.pass'
63 truststore_file = Path + '/truststore.jks'
65 keystore_pass_file = Path + '/keystore.pass'
66 keystore_file = Path + '/keystore.jks'
68 jks_files = [truststore_pass_file, keystore_pass_file, keystore_file, truststore_file]
70 envOdlFeaturesBoot='ODL_FEATURES_BOOT'
71 # Strategy sli-api is default
73 certreadyUrl="/rests/operations/SLI-API:healthcheck"
75 if "SDNRWT" in os.environ:
76 sdnrWt = os.environ['SDNRWT']
79 certreadyUrl="/rests/data/network-topology:network-topology"
80 logging.info('ODL ready strategy with command %s and url %s', certreadyCmd, certreadyUrl)
83 cred_string = username + ":" + password
84 headers = {'Authorization':'Basic %s' % base64.b64encode(cred_string.encode()).decode(),
85 'X-FromAppId': 'csit-sdnc',
86 'X-TransactionId': 'csit-sdnc',
87 'Accept':"application/json",
88 'Content-type':"application/yang-data+json"}
90 def readFile(folder, file):
91 key = open(Path + "/" + folder + "/" + file, "r")
94 fileRead = "\n".join(fileRead.splitlines()[1:-1])
97 def readTrustedCertificate(folder, file):
101 key = open(folder + "/" + file, "r")
102 lines = key.readlines()
104 if not "BEGIN CERTIFICATE" in line and not "END CERTIFICATE" in line and startCa:
106 elif "BEGIN CERTIFICATE" in line:
108 elif "END CERTIFICATE" in line:
110 listCert.append(caPem)
114 def makeKeystoreKey(clientKey, count):
115 odl_private_key = "ODL_private_key_%d" %count
117 json_keystore_key='{{\"input\": {{ \"key-credential\": {{\"key-id\": \"{odl_private_key}\", \"private-key\" : ' \
118 '\"{clientKey}\",\"passphrase\" : \"\"}}}}}}'.format(
119 odl_private_key=odl_private_key,
122 return json_keystore_key
124 def makePrivateKey(clientKey, clientCrt, certList, count):
127 for cert in certList:
128 caPem += '\"%s\",' % cert
129 caPem = caPem.rsplit(',', 1)[0]
130 odl_private_key="ODL_private_key_%d" %count
132 json_private_key='{{\"input\": {{ \"private-key\":{{\"name\": \"{odl_private_key}\", \"data\" : ' \
133 '\"{clientKey}\",\"certificate-chain\":[\"{clientCrt}\",{caPem}]}}}}}}'.format(
134 odl_private_key=odl_private_key,
139 return json_private_key
141 def makeTrustedCertificate(certList, count):
143 json_cert_format = ""
144 for cert in certList:
145 cert_name = "xNF_CA_certificate_%d_%d" %(count, number)
146 json_cert_format += '{{\"name\": \"{trusted_name}\",\"certificate\":\"{cert}\"}},\n'.format(
147 trusted_name=cert_name,
151 json_cert_format = json_cert_format.rsplit(',', 1)[0]
152 json_trusted_cert='{{\"input\": {{ \"trusted-certificate\": [{certificates}]}}}}'.format(
153 certificates=json_cert_format)
154 return json_trusted_cert
157 def makeRestconfPost(conn, json_file, apiCall):
158 req = conn.request("POST", apiCall, json_file, headers=headers)
159 res = conn.getresponse()
161 if res.status != 200:
162 logging.error("Error here, response back wasnt 200: Response was : %d , %s" % (res.status, res.reason))
164 logging.debug("Response :%s Reason :%s ",res.status, res.reason)
166 def extractZipFiles(zipFileList, count):
167 for zipFolder in zipFileList:
168 with zipfile.ZipFile(Path + "/" + zipFolder.strip(),"r") as zip_ref:
169 zip_ref.extractall(Path)
170 folder = zipFolder.rsplit(".")[0]
171 processFiles(folder, count)
173 def processFiles(folder, count):
174 logging.info('Process folder: %d %s', count, folder)
175 for file in os.listdir(Path + "/" + folder):
176 if os.path.isfile(Path + "/" + folder + "/" + file.strip()):
178 clientKey = readFile(folder, file.strip())
179 elif "trustedCertificate" in file:
180 certList = readTrustedCertificate(Path + "/" + folder, file.strip())
182 clientCrt = readFile(folder, file.strip())
184 logging.error("Could not find file %s" % file.strip())
185 shutil.rmtree(Path + "/" + folder)
186 post_content(clientKey, clientCrt, certList, count)
188 def post_content(clientKey, clientCrt, certList, count):
189 logging.info('Post content: %d', count)
190 conn = http.client.HTTPConnection("localhost",odl_port)
193 json_keystore_key = makeKeystoreKey(clientKey, count)
194 logging.debug("Posting private key in to ODL keystore")
195 makeRestconfPost(conn, json_keystore_key, postKeystore)
198 json_trusted_cert = makeTrustedCertificate(certList, count)
199 logging.debug("Posting trusted cert list in to ODL")
200 makeRestconfPost(conn, json_trusted_cert, postTrustedCertificate)
202 if clientKey and clientCrt and certList:
203 json_private_key = makePrivateKey(clientKey, clientCrt, certList, count)
204 logging.debug("Posting the cert in to ODL")
205 makeRestconfPost(conn, json_private_key, postPrivateKey)
208 def makeHealthcheckCall(headers, timePassed):
210 # WAIT 10 minutes maximum and test every 30 seconds if HealthCheck API is returning 200
211 while timePassed < TIMEOUT:
213 conn = http.client.HTTPConnection("localhost",odl_port)
214 req = conn.request(certreadyCmd, certreadyUrl,headers=headers)
215 res = conn.getresponse()
217 httpStatus = res.status
218 if httpStatus == 200:
219 logging.debug("Healthcheck Passed in %d seconds." %timePassed)
223 logging.debug("Sleep: %d seconds before testing if Healthcheck worked. Total wait time up now is: %d seconds. Timeout is: %d seconds. Problem code was: %d" %(INTERVAL, timePassed, TIMEOUT, httpStatus))
225 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))
226 timePassed = timeIncrement(timePassed)
228 if timePassed > TIMEOUT:
229 logging.error("TIME OUT: Healthcheck not passed in %d seconds... Could cause problems for testing activities..." %TIMEOUT)
234 def timeIncrement(timePassed):
236 timePassed = timePassed + INTERVAL
240 def get_pass(file_name):
242 with open(file_name, 'r') as file_obj:
243 password = file_obj.read().strip()
244 return "'{}'".format(password)
245 except Exception as e:
246 logging.error("Error occurred while fetching password : %s", e)
250 for file in os.listdir(Path):
251 if os.path.isfile(Path + '/' + file):
252 logging.debug("Cleaning up the file %s", Path + '/'+ file)
253 os.remove(Path + '/'+ file)
256 def jks_to_p12(file, password):
257 """Converts jks format into p12"""
262 if (file.endswith('.jks')):
263 p12_file = file.replace('.jks', '.p12')
264 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)
265 logging.debug("Converting %s into p12 format", file)
269 except Exception as e:
270 logging.error("Error occurred while converting jks to p12 format : %s", e)
273 def make_cert_chain(cert_chain, pattern):
276 matches = re.findall(pattern, cert_chain, re.DOTALL | re.MULTILINE)
278 cert_list.append(cert.strip())
281 logging.debug(" Certificate Chain empty: %s " % cert_chain)
284 def process_jks_files(count):
286 logging.info("Processing JKS files found in %s directory " % Path)
288 if all([os.path.isfile(f) for f in jks_files]):
289 keystore_pass = get_pass(keystore_pass_file)
290 keystore_file_p12 = jks_to_p12(keystore_file, keystore_pass)
292 client_key_cmd = 'openssl pkcs12 -in {src_file} -nocerts -nodes -passin pass:{src_pass}'.format(
293 src_file=keystore_file_p12, src_pass=keystore_pass)
294 client_crt_cmd = 'openssl pkcs12 -in {src_file} -clcerts -nokeys -passin pass:{src_pass}'.format(
295 src_file=keystore_file_p12, src_pass=keystore_pass)
297 truststore_pass = get_pass(truststore_pass_file)
298 truststore_p12 = jks_to_p12(truststore_file, truststore_pass)
300 trust_cert_cmd = 'openssl pkcs12 -in {src_file} -cacerts -nokeys -passin pass:{src_pass} '.format(
301 src_file=truststore_p12, src_pass=truststore_pass)
303 key_pattern = r'(?<=-----BEGIN PRIVATE KEY-----).*?(?=-----END PRIVATE KEY-----)'
304 client_key = subprocess.check_output(client_key_cmd, shell=True)
306 client_key = make_cert_chain(client_key, key_pattern)[0]
307 logging.debug("Key Ok")
309 cert_pattern = r'(?<=-----BEGIN CERTIFICATE-----).*?(?=-----END CERTIFICATE-----)'
310 client_cert = subprocess.check_output(client_crt_cmd, shell=True)
312 client_cert = make_cert_chain(client_cert, cert_pattern)[0]
313 logging.debug("Client Cert Ok")
315 ca_cert = subprocess.check_output(trust_cert_cmd, shell=True)
317 ca_cert_list = make_cert_chain(ca_cert, cert_pattern)
318 logging.debug("CA Cert Ok")
320 if client_key and client_cert and ca_cert:
321 post_content(client_key, client_cert, ca_cert_list, count)
323 logging.debug("No JKS files found in %s directory" % Path)
324 except subprocess.CalledProcessError as err:
325 print("CalledProcessError Execution of OpenSSL command failed: %s" % err)
326 except Exception as e:
327 logging.error("UnExpected Error while processing JKS files at {0}, Caused by: {1}".format(Path, e))
329 def replaceAdminPassword(username, password, newpassword):
330 if newpassword is None:
331 logging.info('Not to replace password for user %s', username)
333 logging.info('Replace password for user %s', username)
335 jsondata = '{\"password\": \"{newpassword}\"}'.format(newpassword=newpassword)
336 url = '/auth/v1/users/{username}@sdn'.format(username=username)
337 loggin.info("Url %s data $s", url, jsondata)
338 conn = http.client.HTTPConnection("localhost",odl_port)
339 req = conn.request("PUT", url, jsondata, headers=headers)
340 res = conn.getresponse()
342 httpStatus = res.status
343 if httpStatus == 200:
344 logging.debug("New password provided successfully for user %s", username)
346 logging.debug("Password change was not possible. Problem code was: %d", httpStatus)
348 logging.error("Cannot execute REST call to set password.")
351 def readCertProperties():
353 This function searches for manually copied zip file
354 containing certificates. This is required as part
355 of backward compatibility.
356 If not foud, it searches for jks certificates.
358 connected = makeHealthcheckCall(headers, timePassed)
359 logging.info('Connected status: %s', connected)
361 replaceAdminPassword(username, password, newpassword)
363 if os.path.isfile(Path + "/certs.properties"):
364 with open(Path + "/certs.properties", "r") as f:
366 if not "*****" in line:
367 zipFileList.append(line)
369 extractZipFiles(zipFileList, count)
373 logging.debug("No certs.properties/zip files exist at: " + Path)
374 logging.info("Processing any available jks/p12 files under cert directory")
375 process_jks_files(count)
379 logging.info('Cert installation ending')