pip install -e /root/o2
+
cat <<EOF>>/etc/hosts
127.0.0.1 api
127.0.0.1 postgres
127.0.0.1 redis
EOF
+
flask run --host=0.0.0.0 --port=80
sleep infinity
labels:
app: o2api
spec:
+ serviceAccountName: {{ .Values.o2ims.serviceaccountname }}
imagePullSecrets:
- name: {{ .Values.o2ims.imagePullSecrets }}
{{- if .Values.o2ims.affinity }}
--- /dev/null
+# Service Account for o2ims
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ .Values.o2ims.serviceaccountname }}
+ namespace: orano2
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: {{ .Values.o2ims.serviceaccountname }}
+subjects:
+- kind: ServiceAccount
+ namespace: orano2
+ name: {{ .Values.o2ims.serviceaccountname }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
namespace: orano2
o2ims:
- imagePullSecrets: admin-orano2-registry-secret
+ serviceaccountname: admin
image:
repository: registry.local:9001/admin/o2imsdms
tag: 0.1.1
# export API_HOST_EXTERNAL_FLOATING=$(echo ${OS_AUTH_URL} | sed -e s,`echo ${OS_AUTH_URL} | grep :// | sed -e's,^\(.*//\).*,\1,g'`,,g | cut -d/ -f1 | sed -e 's,:.*,,g')
export API_HOST_EXTERNAL_FLOATING=<INF external_oam_floating_address e.g.: 128.10.10.10>
+ # please specify the smo service account yaml file
+ export SMO_SERVICEACCOUNT=<your input here eg.: smo>
+ # service account and binding for smo yaml file
+
+ cat <<EOF >smo-serviceaccount.yaml
+ apiVersion: rbac.authorization.k8s.io/v1
+ kind: Role
+ metadata:
+ namespace: default
+ name: pod-reader
+ rules:
+ - apiGroups: [""] # "" indicates the core API group
+ resources: ["pods"]
+ verbs: ["get", "watch", "list"]
+ ---
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: ${SMO_SERVICEACCOUNT}
+ namespace: default
+ ---
+ apiVersion: rbac.authorization.k8s.io/v1
+ kind: RoleBinding
+ metadata:
+ name: read-pods
+ namespace: default
+ roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: pod-reader
+ subjects:
+ - kind: ServiceAccount
+ name: ${SMO_SERVICEACCOUNT}
+ namespace: default
+
+ EOF
+
+ kubectl apply -f smo-serviceaccount.yaml
+
+ #export the smo account token data
+ export SMO_TOKEN_DATA=$(kubectl -n default describe secret $(kubectl -n default get secret | grep ${SMO_SERVICEACCOUNT} | awk '{print $1}') | grep "token:" | awk '{print $2}')
+
cat <<EOF>o2service-override.yaml
o2ims:
imagePullSecrets: admin-orano2-registry-secret
OS_PASSWORD: "${OS_PASSWORD}"
K8S_KUBECONFIG: "/opt/k8s_kube.conf"
API_HOST_EXTERNAL_FLOATING: "${API_HOST_EXTERNAL_FLOATING}"
+
EOF
helm install o2service o2/charts/ -f o2service-override.yaml
helm list |grep o2service
- kubectl -n ${NAMESPACE} get pods |grep o2service
- kubectl -n ${NAMESPACE} get services |grep o2service
+ kubectl -n ${NAMESPACE} get pods |grep o2api
+ kubectl -n ${NAMESPACE} get services |grep o2api
2.4 Verify INF O2 service
-# Copyright (C) 2021 Wind River Systems, Inc.
+# Copyright (C) 2021-2022 Wind River Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
from o2dms.api import configure_namespace as dms_route_configure_namespace
from o2ims.adapter.clients.alarm_dict_client import load_alarm_definition
+from o2common.authmw import authmiddleware
+from o2common.authmw import authprov
+from o2common.config.config import get_review_url
+from o2common.helper import o2logging
# apibase = config.get_o2ims_api_base()
+auth = True
app = Flask(__name__)
+logger = o2logging.get_logger(__name__)
+
+
+def _get_k8s_url():
+ try:
+ token_review_url = get_review_url()
+ return token_review_url
+ except Exception:
+ raise Exception('Get k8s token review url failed')
+
+
+if auth:
+ # perform service account identity&privilege check.
+ _get_k8s_url()
+ ad = authprov.auth_definer('ad')
+ ad.sanity_check()
+ app.wsgi_app = authmiddleware.authmiddleware(app.wsgi_app)
+
app.config.SWAGGER_UI_DOC_EXPANSION = 'list'
api = Api(app, version='1.0.0',
title='INF O2 Services API',
--- /dev/null
+# Copyright (C) 2022 Wind River Systems, Inc.
+#
+# 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.
--- /dev/null
+# Copyright (C) 2022 Wind River Systems, Inc.
+#
+# 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.
+
+from werkzeug.wrappers import Request, Response
+from o2common.helper import o2logging
+from o2common.authmw.authprov import auth_definer
+
+logger = o2logging.get_logger(__name__)
+
+
+class AuthRequiredExp(Exception):
+ def __init__(self, value):
+ self.value = value
+
+ def dictize(self):
+ return {
+ 'WWW-Authenticate': '{}'.format(self.value)}
+
+
+class AuthFailureExp(Exception):
+ def __init__(self, value):
+ self.value = value
+
+ def dictize(self):
+ return {
+ 'WWW-Authenticate': '{}'.format(self.value)}
+
+
+def _response_wrapper(environ, start_response, header):
+ res = Response(headers=header,
+ mimetype='text/plain', status=401)
+ return res(environ, start_response)
+
+
+class authmiddleware():
+
+ '''
+ Auth WSGI middleware
+ '''
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ logger.info(__name__ + 'authentication middleware')
+ req = Request(environ, populate_request=True, shallow=True)
+ try:
+ auth_header = req.headers['Authorization']
+
+ if auth_header:
+ auth_token = auth_header.split(" ")[1]
+
+ ad = auth_definer('oauth')
+ # invoke underlying auth mdw to make k8s/keystone api
+ ret = ad.authenticate(auth_token)
+ if ret is True:
+ logger.info(
+ "auth success with oauth token: " + auth_token)
+ return self.app(environ, start_response)
+ else:
+ raise AuthFailureExp(
+ 'Bearer realm="Authentication Failed"')
+ else:
+ raise AuthRequiredExp('Bearer realm="Authentication Required"')
+ except AuthRequiredExp as ex:
+ return _response_wrapper(environ, start_response, ex.dictize())
+ except AuthFailureExp as ex:
+ return _response_wrapper(environ, start_response, ex.dictize())
+ except Exception:
+ hint = 'Bearer realm="Authentication Required"'
+ return _response_wrapper(environ, start_response,
+ AuthRequiredExp(hint).dictize())
--- /dev/null
+# Copyright (C) 2022 Wind River Systems, Inc.
+#
+# 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 ssl
+from o2common.helper import o2logging
+import urllib.request
+import urllib.parse
+import json
+
+from o2common.config.config import get_auth_provider, get_review_url
+from o2common.config.config import get_reviewer_token
+
+ssl._create_default_https_context = ssl._create_unverified_context
+logger = o2logging.get_logger(__name__)
+
+# read the conf from config file
+auth_prv_conf = get_auth_provider()
+
+try:
+ token_review_url = get_review_url()
+except Exception:
+ raise Exception('Get k8s token review url failed')
+
+
+class K8SAuthenticaException(Exception):
+ def __init__(self, value):
+ self.value = value
+
+
+class K8SAuthorizationException(Exception):
+ def __init__(self, value):
+ self.value = value
+
+
+class auth_definer():
+
+ def __init__(self, name):
+ super().__init__()
+ self.name = name
+ if auth_prv_conf == 'k8s':
+ self.obj = k8s_auth_provider('k8s')
+ else:
+ self.obj = keystone_auth_provider('keystone')
+
+ def tokenissue(self):
+ return self.obj.tokenissue()
+
+ def sanity_check(self):
+ return self.obj.sanity_check()
+
+ # call k8s api
+ def authenticate(self, token):
+ return self.obj.authenticate(token)
+
+ def __repr__(self) -> str:
+ return "<auth_definer: name = %s>" % self.name
+
+
+class k8s_auth_provider(auth_definer):
+
+ def __init__(self, name):
+ self.name = name
+
+ def tokenissue(self, **args2):
+ pass
+
+ def sanity_check(self):
+ try:
+ self.authenticate('faketoken')
+ except Exception as ex:
+ logger.critical(
+ 'Failed to bootstrap oauth middleware with exp: ' + str(ex))
+ raise Exception(str(ex))
+
+ def authenticate(self, token):
+ reviewer_token = get_reviewer_token()
+ tokenreview = {
+ "kind": "TokenReview",
+ "apiVersion": "authentication.k8s.io/v1",
+ "metadata": {
+ "creationTimestamp": None
+ },
+ "spec": {
+ "token": ""+token
+ },
+ "status": {
+ "user": {}
+ }
+ }
+ datas = json.dumps(tokenreview)
+ binary_data = datas.encode('utf-8')
+ # 'post' method
+ header = {'Authorization': 'Bearer '+reviewer_token,
+ 'Content-Type': 'application/json'}
+ try:
+ req = urllib.request.Request(
+ token_review_url, data=binary_data, headers=header)
+ response = urllib.request.urlopen(req)
+ data = json.load(response)
+ if data['status']['authenticated'] is True:
+ logger.info("Authenticated.")
+ return True
+ except Exception as ex:
+ strex = str(ex)
+ logger.warning(
+ "Invoke K8s API Service Exception happened:" + strex)
+ if '403' in strex:
+ raise K8SAuthorizationException(
+ 'No privilege to perform oauth token check.')
+ elif '401' in strex:
+ raise K8SAuthenticaException(
+ 'Self Authentication failure.')
+ return False
+
+ def tokenrevoke(self, **args2):
+ return True
+
+
+class keystone_auth_provider(auth_definer):
+ def __init__(self, name):
+ self.name = name
+
+ def tokenissue(self, *args1, **args2):
+ pass
+
+ def authenticate(self, *args1, **args2):
+ return False
+
+ def sanity_check(self):
+ pass
+
+ def tokenrevoke(self, *args1, **args2):
+ return False
-# Copyright (C) 2021 Wind River Systems, Inc.
+# Copyright (C) 2021-2022 Wind River Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
if events_yaml_name is not None and os.path.isfile(events_yaml_name):
return events_yaml_name
return "/configs/events.yaml"
+
+# get k8s host from env:
+
+
+def get_k8s_host():
+ k8s_host = os.environ.get("KUBERNETES_SERVICE_HOST")
+ if k8s_host is None:
+ raise Exception('Get k8s host failed.')
+ return k8s_host
+
+# get k8s host port from env:
+
+
+def get_k8s_port():
+ k8s_port = os.environ.get("KUBERNETES_SERVICE_PORT_HTTPS", '443')
+ return k8s_port
+
+# token review url
+
+
+def get_review_url():
+ try:
+ api = '/apis/authentication.k8s.io/v1/tokenreviews'
+ return "{0}{1}:{2}{3}".format(
+ 'https://', get_k8s_host(), get_k8s_port(), api)
+ except Exception:
+ raise Exception('Get k8s review url failed')
+
+# get reviewer token
+
+
+def get_reviewer_token():
+ # token path default is below.
+ token_path = '/var/run/secrets/kubernetes.io/serviceaccount/token'
+ with open(token_path, 'r') as f:
+ ctt = f.read()
+ return ctt
+
+
+def get_auth_provider():
+ return 'k8s'
count1 = fakewatcher.fakeOcloudWatcherCounter
testedworker.start()
- time.sleep(20)
+ time.sleep(1)
assert fakewatcher.fakeOcloudWatcherCounter > count1
# assumed hacking: probe has stopped the sched task
count3 = fakewatcher.fakeOcloudWatcherCounter
- time.sleep(3)
+ time.sleep(1)
assert fakewatcher.fakeOcloudWatcherCounter == count3