Encryption/Decryption utility 26/100726/4
authorIkram Ikramullah <ikram@research.att.com>
Thu, 23 Jan 2020 16:53:16 +0000 (11:53 -0500)
committerdhebeha <dhebeha.mj71@wipro.com>
Fri, 24 Jan 2020 16:25:18 +0000 (21:55 +0530)
Putting in the encryption/decryption utility. Hit cipher-util inside
the virutalenv to see its usage. The work is still in progress.

Issue-ID: OPTFRA-683

Signed-off-by: dhebeha <dhebeha.mj71@wipro.com>
Change-Id: I7a4845c1318db8d14f1efbb0f98e785adf214e06

15 files changed:
api_paste.ini [new file with mode: 0644]
conductor.conf
conductor/conductor/api/adapters/aaf/aaf_authentication.py
conductor/conductor/cmd/encryptionUtil.py [new file with mode: 0644]
conductor/conductor/common/music/api.py
conductor/conductor/common/utils/basic_auth_util.py
conductor/conductor/common/utils/cipherUtils.py [new file with mode: 0644]
conductor/conductor/data/plugins/inventory_provider/aai.py
conductor/conductor/data/plugins/service_controller/sdnc.py
conductor/conductor/data/plugins/vim_controller/multicloud.py
conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py
conductor/conductor/tests/unit/music/test_api.py
conductor/requirements.txt
conductor/setup.cfg
preload_secrets.yaml

diff --git a/api_paste.ini b/api_paste.ini
new file mode 100644 (file)
index 0000000..4299f46
--- /dev/null
@@ -0,0 +1,26 @@
+# Conductor API WSGI Pipeline
+# Define the filters that make up the pipeline for processing WSGI requests
+# Note: This pipeline is PasteDeploy's term rather than Conductor's pipeline
+# used for processing samples
+
+# Remove authtoken from the pipeline if you don't want to use keystone authentication
+[pipeline:main]
+pipeline = cors http_proxy_to_wsgi api-server
+#pipeline = cors http_proxy_to_wsgi request_id authtoken api-server
+
+[app:api-server]
+paste.app_factory = conductor.api.app:app_factory
+
+#[filter:authtoken]
+#paste.filter_factory = keystonemiddleware.auth_token:filter_factory
+
+#[filter:request_id]
+#paste.filter_factory = oslo_middleware:RequestId.factory
+
+[filter:cors]
+paste.filter_factory = oslo_middleware.cors:filter_factory
+oslo_config_project = conductor
+
+[filter:http_proxy_to_wsgi]
+paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory
+oslo_config_project = conductor
index b4f09b1..79ec774 100755 (executable)
 #fatal_deprecations = false
 
 
+[auth]
+aapkey = h@ss3crtky400fdntc#001
+
 [aaf_api]
 
 #
index a85ac11..fb0b9ab 100644 (file)
@@ -24,7 +24,7 @@ import os
 
 from conductor.common import rest
 from conductor.i18n import _LE, _LI
-from conductor import __file__ as conductor_root
+from conductor.common.utils import cipherUtils
 
 from oslo_log import log
 LOG = log.getLogger(__name__)
@@ -83,7 +83,7 @@ def clear_cache():
 def authenticate(uid, passwd):
     aafUser = None
     username = CONF.conductor_api.username
-    password = CONF.conductor_api.password
+    password = cipherUtils.AESCipher.get_instance().decrypt(CONF.conductor_api.password)
     if username == uid and password == passwd:
         aafUser = CONF.aaf_api.aaf_conductor_user
     else:
@@ -159,7 +159,7 @@ def remote_api(aafUser):
         "server_url": server_url,
         "retries": CONF.aaf_api.aaf_retries,
         "username": CONF.aaf_api.username,
-        "password": CONF.aaf_api.password,
+        "password": cipherUtils.AESCipher.get_instance().decrypt(CONF.aaf_api.password),
         "log_debug": LOG.debug,
         "read_timeout": CONF.aaf_api.aaf_timeout,
         "cert_file": CONF.aaf_api.aaf_cert_file,
diff --git a/conductor/conductor/cmd/encryptionUtil.py b/conductor/conductor/cmd/encryptionUtil.py
new file mode 100644 (file)
index 0000000..179d366
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2018 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+import sys
+from conductor.common.utils import cipherUtils
+
+
+def main():
+
+    if len(sys.argv) != 4:
+        print("Invalid input - usage --> (options(encrypt/decrypt) input-value with-key)")
+        return
+
+    enc_dec = sys.argv[1]
+    valid_option_values = ['encrypt', 'decrypt']
+    if enc_dec not in valid_option_values:
+        print("Invalid input - usage --> (options(encrypt/decrypt) input-value with-key)")
+        print("Option value can only be one of {}".format(valid_option_values))
+        print("You entered '{}'".format(enc_dec))
+        return
+
+    input_string = sys.argv[2]
+    with_key = sys.argv[3]
+
+    print("You've requested '{}' to be '{}ed' using key '{}'".format(input_string, enc_dec, with_key))
+    print("You can always perform the reverse operation (encrypt/decrypt) using the same key"
+          "to be certain you get the same results back'")
+
+    util = cipherUtils.AESCipher.get_instance(with_key)
+    #util = CipherUtil.AESCipher(with_key)
+
+    if enc_dec.lower() == 'encrypt':
+        result = util.encrypt(input_string)
+    else:
+        result = util.decrypt(input_string)
+
+    print("Your result: {}".format(result))
+
index 0ca4301..5929b58 100644 (file)
@@ -30,6 +30,7 @@ from oslo_log import log
 from conductor.common import rest
 from conductor.common.utils import basic_auth_util
 from conductor.i18n import _LE, _LI  # pylint: disable=W0212
+from conductor.common.utils import cipherUtils
 
 LOG = log.getLogger(__name__)
 
@@ -137,22 +138,23 @@ class MusicAPI(object):
         }
         self.rest = rest.REST(**kwargs)
 
+        music_pwd = cipherUtils.AESCipher.get_instance().decrypt(CONF.music_api.aafpass)
         # Set one parameter for connection mode
         # Currently depend on music version
-        if (CONF.music_api.enable_https_mode):
+        if CONF.music_api.enable_https_mode:
             self.rest.server_url = 'https://{}:{}/{}'.format(
                 host, port, version, path.rstrip('/').lstrip('/'))
             self.rest.session.verify = CONF.music_api.certificate_authority_bundle_file
 
-        if(CONF.music_api.music_new_version):
-            MUSIC_version = CONF.music_api.music_version.split(".")
+        if CONF.music_api.music_new_version:
+            music_version = CONF.music_api.music_version.split(".")
 
             self.rest.session.headers['content-type'] = 'application/json'
-            self.rest.session.headers['X-minorVersion'] = MUSIC_version[1]
-            self.rest.session.headers['X-patchVersion'] = MUSIC_version[2]
+            self.rest.session.headers['X-minorVersion'] = music_version[1]
+            self.rest.session.headers['X-patchVersion'] = music_version[2]
             self.rest.session.headers['ns'] = CONF.music_api.aafns
             self.rest.session.headers['userId'] = CONF.music_api.aafuser
-            self.rest.session.headers['password'] = CONF.music_api.aafpass
+            self.rest.session.headers['password'] = music_pwd
             self.rest.session.headers['Authorization'] = basic_auth_util.encode(CONF.music_api.aafuser,
                                                                                 CONF.music_api.aafpass)
 
index a94418f..d4cdbac 100644 (file)
@@ -28,7 +28,7 @@ LOG = log.getLogger(__name__)
 def encode(user_id, password):
     """ Provide the basic authencation encoded value in an 'Authorization' Header """
 
-    user_pass = user_id + ':' + password
+    user_pass = str(user_id) + ":" + str(password)
     base64_val = base64.b64encode(user_pass)
     authorization_val = _LE("Basic {}".format(base64_val))
 
diff --git a/conductor/conductor/common/utils/cipherUtils.py b/conductor/conductor/common/utils/cipherUtils.py
new file mode 100644 (file)
index 0000000..6ee6c58
--- /dev/null
@@ -0,0 +1,77 @@
+#
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+import base64
+import hashlib
+from Crypto import Random
+from Crypto.Cipher import AES
+from oslo_config import cfg
+
+CONF = cfg.CONF
+
+cipher_opts = [
+    cfg.StrOpt('appkey',
+               default='ch00se@g003ntropy',
+               help='Master key to secure other secrets')
+]
+
+CONF.register_opts(cipher_opts, group='auth')
+
+
+class AESCipher(object):
+    __instance = None
+
+    @staticmethod
+    def get_instance(key = None):
+        if AESCipher.__instance is None:
+            print ('Creating the singleton instance')
+            AESCipher(key)
+        return AESCipher.__instance
+
+    def __init__(self, key=None):
+        if AESCipher.__instance is not None:
+            raise Exception("This class is a singleton!")
+        else:
+            AESCipher.__instance = self
+
+        self.bs = 32
+        if key is None:
+            key = CONF.auth.appkey.encode()
+
+        self.key = hashlib.sha256(key.encode()).digest()
+
+    def encrypt(self, raw):
+        raw = self._pad(raw)
+        iv = Random.new().read(AES.block_size)
+        cipher = AES.new(self.key, AES.MODE_CBC, iv)
+        return base64.b64encode(iv + cipher.encrypt(raw))
+
+    def decrypt(self, enc):
+        enc = base64.b64decode(enc)
+        iv = enc[:AES.block_size]
+        cipher = AES.new(self.key, AES.MODE_CBC, iv)
+        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
+
+    def _pad(self, s):
+        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
+
+    @staticmethod
+    def _unpad(s):
+        return s[:-ord(s[len(s)-1:])]
+
index 26b390a..f49d526 100644 (file)
@@ -23,11 +23,10 @@ import uuid
 import copy
 
 import json
-from oslo_config import cfg
-from oslo_log import log
 
 from conductor.common import rest
 from conductor.data.plugins import constants
+from conductor.common.utils import cipherUtils
 from conductor.data.plugins.inventory_provider import base
 from conductor.data.plugins.inventory_provider import hpa_utils
 from conductor.data.plugins.triage_translator.triage_translator import TraigeTranslator
@@ -111,7 +110,7 @@ class AAI(base.InventoryProviderBase):
         self.timeout = self.conf.aai.aai_rest_timeout
         self.retries = self.conf.aai.aai_retries
         self.username = self.conf.aai.username
-        self.password = self.conf.aai.password
+        self.password = cipherUtils.AESCipher.get_instance().decrypt(self.conf.aai.password)
         self.triage_translator=TraigeTranslator()
 
         # Cache is initially empty
index 5e4e8a0..1571b41 100644 (file)
@@ -23,6 +23,7 @@ from oslo_config import cfg
 from oslo_log import log
 
 from conductor.common import rest
+from conductor.common.utils import cipherUtils
 from conductor.data.plugins.service_controller import base
 from conductor.i18n import _LE
 
@@ -66,7 +67,7 @@ class SDNC(base.ServiceControllerBase):
         self.conf = CONF
 
         self.base = self.conf.sdnc.server_url.rstrip('/')
-        self.password = self.conf.sdnc.password
+        self.password = cipherUtils.AESCipher.get_instance().decrypt(self.conf.sdnc.password)
         self.timeout = self.conf.sdnc.sdnc_rest_timeout
         self.verify = False
         self.retries = self.conf.sdnc.sdnc_retries
index 5c2b5f7..b5c7a66 100644 (file)
@@ -115,7 +115,7 @@ class MULTICLOUD(base.VimControllerBase):
             "read_timeout": self.timeout,
         }
         self.rest = rest.REST(**kwargs)
-        if(self.conf.multicloud.enable_https_mode):
+        if self.conf.multicloud.enable_https_mode:
             self.rest.server_url = self.base[:4]+'s'+self.base[4:]
             self.rest.session.verify =self.conf.multicloud.certificate_authority_bundle_file    
 
index 9d1245d..5b9984a 100644 (file)
@@ -30,7 +30,7 @@ from oslo_config import cfg
 class TestAAI(unittest.TestCase):
 
     def setUp(self):
-
+        cfg.CONF.set_override('password', '4HyU6sI+Tw0YMXgSHr5sJ5C0UTkeBaxXoxQqWuSVFugls7sQnaAXp4zMfJ8FKFrH', 'aai')
         CONF = cfg.CONF
         CONF.register_opts(aai.AAI_OPTS, group='aai')
         self.conf = CONF
index 90bd57d..285d0d7 100644 (file)
@@ -23,12 +23,12 @@ from conductor.common import rest
 from conductor.common.music.api import MusicAPI
 from oslo_config import cfg
 
-
 class TestMusicApi(unittest.TestCase):
 
     def setUp(self):
         cfg.CONF.set_override('debug', True, 'music_api')
         cfg.CONF.set_override('certificate_authority_bundle_file', '../AAF_RootCA.cer', 'music_api')
+        cfg.CONF.set_override('aafpass', 'U5AudaTTC/DYQ29Q7qiXx/iwsgwV+dV7v7/Q5TDBh1URPwqAGECnbVmUkOynLjTY', 'music_api')
         self.mock_lock_id = mock.patch.object(MusicAPI, '_lock_id_create',
                                               return_value='12345678')
         self.mock_lock_acquire = mock.patch.object(MusicAPI,
index d6d413d..6ad13e2 100644 (file)
@@ -25,4 +25,5 @@ stevedore>=1.9.0 # Apache-2.0, also required by oslo.config
 WebOb>=1.2.3 # MIT
 onapsmsclient>=0.0.4
 Flask>=0.11.1
-prometheus-client>=0.3.1
\ No newline at end of file
+prometheus-client>=0.3.1
+pycrypto==2.6.1
index 5152285..9d2298d 100644 (file)
@@ -63,6 +63,7 @@ console_scripts =
     conductor-data = conductor.cmd.data:main
     conductor-solver = conductor.cmd.solver:main
     conductor-reservation = conductor.cmd.reservation:main
+    cipher-util = conductor.cmd.encryptionUtil:main
 
 conductor.inventory_provider.plugin =
     aai = conductor.data.plugins.inventory_provider.aai:AAI
index 3ec1b92..62a00f2 100755 (executable)
@@ -6,22 +6,22 @@ secrets:
 - name: aai
   values:
     username: oof@oof.onap.org
-    password: demo123456!
+    password: 4HyU6sI+Tw0YMXgSHr5sJ5C0UTkeBaxXoxQqWuSVFugls7sQnaAXp4zMfJ8FKFrH
 - name: conductor_api
   values:
     username: admin1
-    password: plan.15
+    password: /90ON7wZM2SfItrVXix57TpCyMMR/EUxrhE9Vbo62yJyNfcrpEfsYEZKqTOXHmkn
 - name: sdnc
   values:
     username: admin
-    password: Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
+    password: jI4D86X3lZIZILHLpHIWVsJeYODWpcEHlkfswE3pHF0UKjwp36euiwILMyV1jmyNjFF37we3n9VKZFc2UYO6Xsrfe2wBfwhod9Ft4rZEqFo
 - name: music_api
   values:
     aafuser: conductor
-    aafpass: c0nduct0r
+    aafpass: U5AudaTTC/DYQ29Q7qiXx/iwsgwV+dV7v7/Q5TDBh1URPwqAGECnbVmUkOynLjTY
     aafns: conductor
 - name: aaf_api
   values:
     username: aaf_admin@people.osaaf.org
-    password: demo123456!
+    password: IE4lBg4NmcIikB7RLjnXbyVeU2jxuKUWqX1ZfEzCcBAe0wDV87xFGTYTNoTzptXn
     aaf_conductor_user: oof@oof.onap.org