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= "/restconf/operations/netconf-keystore:add-keystore-entry"
51 postPrivateKey= "/restconf/operations/netconf-keystore:add-private-key"
52 postTrustedCertificate= "/restconf/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 headers = {'Authorization':'Basic %s' % base64.b64encode(username + ":" + password),
64 'X-FromAppId': 'csit-sdnc',
65 'X-TransactionId': 'csit-sdnc',
66 'Accept':"application/json",
67 'Content-type':"application/json"}
70 def readFile(folder, file):
71 key = open(Path + "/" + folder + "/" + file, "r")
74 fileRead = "\n".join(fileRead.splitlines()[1:-1])
78 def readTrustedCertificate(folder, file):
82 key = open(folder + "/" + file, "r")
83 lines = key.readlines()
85 if not "BEGIN CERTIFICATE" in line and not "END CERTIFICATE" in line and startCa:
87 elif "BEGIN CERTIFICATE" in line:
89 elif "END CERTIFICATE" in line:
91 listCert.append(caPem)
96 def makeKeystoreKey(clientKey, count):
97 odl_private_key = "ODL_private_key_%d" %count
99 json_keystore_key='{{\"input\": {{ \"key-credential\": {{\"key-id\": \"{odl_private_key}\", \"private-key\" : ' \
100 '\"{clientKey}\",\"passphrase\" : \"\"}}}}}}'.format(
101 odl_private_key=odl_private_key,
104 return json_keystore_key
107 def makePrivateKey(clientKey, clientCrt, certList, count):
110 for cert in certList:
111 caPem += '\"%s\",' % cert
112 caPem = caPem.rsplit(',', 1)[0]
113 odl_private_key="ODL_private_key_%d" %count
115 json_private_key='{{\"input\": {{ \"private-key\":{{\"name\": \"{odl_private_key}\", \"data\" : ' \
116 '\"{clientKey}\",\"certificate-chain\":[\"{clientCrt}\",{caPem}]}}}}}}'.format(
117 odl_private_key=odl_private_key,
122 return json_private_key
125 def makeTrustedCertificate(certList, count):
127 json_cert_format = ""
128 for cert in certList:
129 cert_name = "xNF_CA_certificate_%d_%d" %(count, number)
130 json_cert_format += '{{\"name\": \"{trusted_name}\",\"certificate\":\"{cert}\"}},\n'.format(
131 trusted_name=cert_name,
135 json_cert_format = json_cert_format.rsplit(',', 1)[0]
136 json_trusted_cert='{{\"input\": {{ \"trusted-certificate\": [{certificates}]}}}}'.format(
137 certificates=json_cert_format)
138 return json_trusted_cert
141 def makeRestconfPost(conn, json_file, apiCall):
142 req = conn.request("POST", apiCall, json_file, headers=headers)
143 res = conn.getresponse()
145 if res.status != 200:
146 logging.error("Error here, response back wasnt 200: Response was : %d , %s" % (res.status, res.reason))
148 logging.debug("Response :%s Reason :%s ",res.status, res.reason)
151 def extractZipFiles(zipFileList, count):
152 for zipFolder in zipFileList:
153 with zipfile.ZipFile(Path + "/" + zipFolder.strip(),"r") as zip_ref:
154 zip_ref.extractall(Path)
155 folder = zipFolder.rsplit(".")[0]
156 processFiles(folder, count)
159 def processFiles(folder, count):
160 for file in os.listdir(Path + "/" + folder):
161 if os.path.isfile(Path + "/" + folder + "/" + file.strip()):
163 clientKey = readFile(folder, file.strip())
164 elif "trustedCertificate" in file:
165 certList = readTrustedCertificate(Path + "/" + folder, file.strip())
167 clientCrt = readFile(folder, file.strip())
169 logging.error("Could not find file %s" % file.strip())
170 shutil.rmtree(Path + "/" + folder)
171 post_content(clientKey, clientCrt, certList, count)
174 def post_content(clientKey, clientCrt, certList, count):
175 conn = httplib.HTTPConnection("localhost",odl_port)
178 json_keystore_key = makeKeystoreKey(clientKey, count)
179 logging.debug("Posting private key in to ODL keystore")
180 makeRestconfPost(conn, json_keystore_key, postKeystore)
183 json_trusted_cert = makeTrustedCertificate(certList, count)
184 logging.debug("Posting trusted cert list in to ODL")
185 makeRestconfPost(conn, json_trusted_cert, postTrustedCertificate)
187 if clientKey and clientCrt and certList:
188 json_private_key = makePrivateKey(clientKey, clientCrt, certList, count)
189 logging.debug("Posting the cert in to ODL")
190 makeRestconfPost(conn, json_private_key, postPrivateKey)
193 def makeHealthcheckCall(headers, timePassed):
195 # WAIT 10 minutes maximum and test every 30 seconds if HealthCheck API is returning 200
196 while timePassed < TIMEOUT:
198 conn = httplib.HTTPConnection("localhost",odl_port)
199 req = conn.request("POST", "/restconf/operations/SLI-API:healthcheck",headers=headers)
200 res = conn.getresponse()
202 if res.status == 200:
203 logging.debug("Healthcheck Passed in %d seconds." %timePassed)
207 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))
209 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))
210 timePassed = timeIncrement(timePassed)
212 if timePassed > TIMEOUT:
213 logging.error("TIME OUT: Healthcheck not passed in %d seconds... Could cause problems for testing activities..." %TIMEOUT)
217 def timeIncrement(timePassed):
219 timePassed = timePassed + INTERVAL
223 def get_pass(file_name):
225 with open(file_name, 'r') as file_obj:
226 password = file_obj.read().strip()
227 return "'{}'".format(password)
228 except Exception as e:
229 logging.error("Error occurred while fetching password : %s", e)
234 for file in jks_files:
235 if os.path.isfile(file):
236 logging.debug("Cleaning up the file %s", file)
240 def jks_to_p12(file, password):
241 """Converts jks format into p12"""
243 p12_file = file.replace('.jks', '.p12')
244 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)
245 logging.debug("Converting %s into p12 format", file)
249 except Exception as e:
250 logging.error("Error occurred while converting jks to p12 format : %s", e)
253 def make_cert_chain(cert_chain, pattern):
256 matches = re.findall(pattern, cert_chain, re.DOTALL | re.MULTILINE)
258 cert_list.append(cert.strip())
261 logging.debug(" Certificate Chain empty: %s " % cert_chain)
264 def process_jks_files(count):
266 logging.info("Processing JKS files found in %s directory " % Path)
268 if all([os.path.isfile(f) for f in jks_files]):
269 keystore_pass = get_pass(keystore_pass_file)
270 keystore_file_p12 = jks_to_p12(keystore_file, keystore_pass)
272 client_key_cmd = 'openssl pkcs12 -in {src_file} -nocerts -nodes -passin pass:{src_pass}'.format(
273 src_file=keystore_file_p12, src_pass=keystore_pass)
274 client_crt_cmd = 'openssl pkcs12 -in {src_file} -clcerts -nokeys -passin pass:{src_pass}'.format(
275 src_file=keystore_file_p12, src_pass=keystore_pass)
277 truststore_pass = get_pass(truststore_pass_file)
278 truststore_p12 = jks_to_p12(truststore_file, truststore_pass)
280 trust_cert_cmd = 'openssl pkcs12 -in {src_file} -cacerts -nokeys -passin pass:{src_pass} '.format(
281 src_file=truststore_p12, src_pass=truststore_pass)
283 key_pattern = r'(?<=-----BEGIN PRIVATE KEY-----).*?(?=-----END PRIVATE KEY-----)'
284 client_key = subprocess.check_output(client_key_cmd, shell=True)
286 client_key = make_cert_chain(client_key, key_pattern)[0]
287 logging.debug("Key Ok")
289 cert_pattern = r'(?<=-----BEGIN CERTIFICATE-----).*?(?=-----END CERTIFICATE-----)'
290 client_cert = subprocess.check_output(client_crt_cmd, shell=True)
292 client_cert = make_cert_chain(client_cert, cert_pattern)[0]
293 logging.debug("Client Cert Ok")
295 ca_cert = subprocess.check_output(trust_cert_cmd, shell=True)
297 ca_cert_list = make_cert_chain(ca_cert, cert_pattern)
298 logging.debug("CA Cert Ok")
300 if client_key and client_cert and ca_cert:
301 post_content(client_key, client_cert, ca_cert_list, count)
303 logging.debug("No JKS files found in %s directory" % Path)
304 except subprocess.CalledProcessError as err:
305 print("CalledProcessError Execution of OpenSSL command failed: %s" % err)
306 except Exception as e:
307 logging.error("UnExpected Error while processing JKS files at {0}, Caused by: {1}".format(Path, e))
310 def readCertProperties():
312 This function searches for manually copied zip file
313 containing certificates. This is required as part
314 of backward compatibility.
315 If not foud, it searches for jks certificates.
317 connected = makeHealthcheckCall(headers, timePassed)
321 if os.path.isfile(Path + "/certs.properties"):
322 with open(Path + "/certs.properties", "r") as f:
324 if not "*****" in line:
325 zipFileList.append(line)
327 extractZipFiles(zipFileList, count)
331 logging.debug("No certs.properties/zip files exist at: " + Path)
332 process_jks_files(count)