Skip to content

Commit

Permalink
Add namespaces dropdown field (#36)
Browse files Browse the repository at this point in the history
* get namespaces via rest call; add rbac rules to helm chart

* add local tests

* Enable select field for selecting the namespace generated by the rest-api

* update npm and pip dependencies

* fix pylint issues

* fix missing EOF; remove comment line

* fix missing EOF; remove comment line

* Add optional display name for the application; update helm chart; update README

* fix README

Co-authored-by: Jan Herber <jaydee>
  • Loading branch information
Jaydee94 authored Feb 14, 2021
1 parent 261ae2b commit efa99a7
Show file tree
Hide file tree
Showing 21 changed files with 2,574 additions and 1,490 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ chart/kubeseal-webgui/values-local.yaml
test.sh
test.yaml
*.pyc
.dockerignore
2 changes: 1 addition & 1 deletion Dockerfile.api
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM abihf/wget:latest AS kubeseal-downloader
RUN wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.11.0/kubeseal-linux-amd64 && \
mv kubeseal-linux-amd64 /tmp/kubeseal

FROM python:3.8-alpine3.12
FROM python:3.9-alpine

ENV PORT 5000
ENV HOST 0.0.0.0
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ Mount the public certificate of your sealed secrets controller to `/kubeseal-web

Please use the [helm chart](https://github.com/Jaydee94/kubeseal-webgui/tree/master/chart/kubeseal-webgui) which is included in this repository.

## Upgrade from 2.0.X to 2.1.0

When upgrading to `2.1.0` make sure that you also update the helm chart for installing kubeseal-webgui.
The application reads namespaces from current kubernetes cluster and needs to have access to list them.
If your default serviceaccount has this RBAC rule already you could disable `serviceaccount.create` in the `values.yaml` of the helm chart.

### Get Public-Cert from sealed-secrets controller

(Login to your kubernetes cluster first)
Expand Down
7 changes: 5 additions & 2 deletions api/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" Module containing the API for encoding sensitive data via kubeseal-cli. """
"""Module containing the API for encoding sensitive data via kubeseal-cli."""
from os import urandom, environ
import sys
import logging
Expand All @@ -7,6 +7,7 @@
from flask_cors import CORS
import json_log_formatter
from .kubeseal import KubesealEndpoint
from .kubernetes import KubernetesNamespacesEndpoint

# Setup JSON handler for logging
formatter = json_log_formatter.JSONFormatter()
Expand All @@ -25,7 +26,7 @@


def create_app(test_config=None):
""" Initializes Flask application module. """
"""Initializes Flask application module."""
app = Flask(__name__)

if test_config is None:
Expand All @@ -39,8 +40,10 @@ def create_app(test_config=None):
raise RuntimeError("Error: Environment variable ORIGIN_URL empty.")

CORS(app, resources={r"/secrets/*": {"origins": environ['ORIGIN_URL']}})
CORS(app, resources={r"/namespaces/*": {"origins": environ['ORIGIN_URL']}})

api = Api(app)
api.add_resource(KubesealEndpoint, '/secrets')
api.add_resource(KubernetesNamespacesEndpoint, '/namespaces')

return app
35 changes: 35 additions & 0 deletions api/app/kubernetes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Provides REST-API and kubeseal-cli specific functionality."""
import logging
import json

from kubernetes import client, config
from flask_restful import Resource, abort

LOGGER = logging.getLogger("kubeseal-webgui")


class KubernetesNamespacesEndpoint(Resource):
"""Provides REST-API for sealing sensitive data."""

@classmethod
def get(cls):
"""References GET method. Used for retrieving cluster namespaces."""
try:
return get_incluster_namespaces()
except RuntimeError:
abort(500)


def get_incluster_namespaces():
"""Function for retrieving all namespaces from current kubernetes cluster as JSON-Array"""
config.load_incluster_config()
namespaces_list = []

v1 = client.CoreV1Api()
LOGGER.info("Resolving in-cluster Namespaces")
namespaces = v1.list_namespace()
for ns in namespaces.items:
namespaces_list.append(ns.metadata.name)

LOGGER.debug("Namespaces list %s", namespaces_list)
return json.dumps(namespaces_list)
10 changes: 5 additions & 5 deletions api/app/kubeseal.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" Provides REST-API and kubeseal-cli specific functionality. """
"""Provides REST-API and kubeseal-cli specific functionality."""
import logging
import subprocess
import base64
Expand All @@ -10,11 +10,11 @@


class KubesealEndpoint(Resource):
""" Provides REST-API for sealing sensitive data. """
"""Provides REST-API for sealing sensitive data."""

@classmethod
def get(cls):
""" References POST method. Used for health checks. """
"""References POST method. Used for health checks."""
return "Use POST HTTP request to seal secret."

@classmethod
Expand Down Expand Up @@ -43,7 +43,7 @@ def post(cls):


def run_kubeseal(cleartext_secrets, secret_namespace, secret_name):
""" Wrapper function for checking input and initiating kubeseal-cli call """
"""Wrapper function for checking input and initiating kubeseal-cli call"""
if secret_namespace is None or secret_namespace == "":
error_message = "secret_namespace was not given"
LOGGER.error(error_message)
Expand Down Expand Up @@ -72,7 +72,7 @@ def run_kubeseal(cleartext_secrets, secret_namespace, secret_name):


def run_kubeseal_command(cleartext_secret_tuple, secret_namespace, secret_name):
""" Function for calling kubeseal-cli in subprocess. """
"""Function for calling kubeseal-cli in subprocess."""
LOGGER.info(f"Sealing secret '{secret_name}.{cleartext_secret_tuple['key']}' \
for namespace '{secret_namespace}'.")
cleartext_secret = decode_base64_string(cleartext_secret_tuple['value'])
Expand Down
13 changes: 7 additions & 6 deletions api/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
-i https://pypi.org/simple
click==7.1.1
click==7.1.2
cssmin==0.2.0
dominate==2.5.1
dominate==2.6.0
flask-assets==2.0
flask-bootstrap==3.3.7.1
Flask-Cors==3.0.9
Flask-Cors==3.0.10
flask-wtf==0.14.3
Flask-RESTful==0.3.8
flask==1.1.2
pytest==6.0.1
pytest==6.2.2
pylint==2.6.0
itsdangerous==1.1.0
json_log_formatter==0.3.0
jinja2==2.11.1
jinja2==2.11.3
markupsafe==1.1.1
visitor==0.1.3
webassets==2.0
werkzeug==1.0.1
wtforms==2.2.1
wtforms==2.3.3
kubernetes==12.0.1
4 changes: 2 additions & 2 deletions chart/kubeseal-webgui/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
apiVersion: v1
name: kubeseal-webgui
description: A Helm chart for installing kubeseal-webgui
version: 2.0.0
appVersion: 2.0.0
version: 2.1.0
appVersion: 2.1.0
17 changes: 17 additions & 0 deletions chart/kubeseal-webgui/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{{- if .Values.serviceaccount.create }}
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: kubeseal-webgui-list-namespaces
labels:
{{- include "kubeseal-webgui.labels" . | nindent 4 }}
{{- with .Values.route.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["list"]
{{- end }}

21 changes: 21 additions & 0 deletions chart/kubeseal-webgui/templates/clusterrolebinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{{- if .Values.serviceaccount.create }}
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: kubeseal-webgui-list-namespaces
labels:
{{- include "kubeseal-webgui.labels" . | nindent 4 }}
{{- with .Values.route.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
subjects:
- kind: ServiceAccount
name: kubeseal-webgui
namespace: {{ .Release.Namespace }}
roleRef:
kind: ClusterRole
name: kubeseal-webgui-list-namespaces
apiGroup: rbac.authorization.k8s.io
{{- end }}

3 changes: 2 additions & 1 deletion chart/kubeseal-webgui/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ data:
{{- toYaml .Values.sealedSecrets.cert | nindent 4 }}
config.json: |-
{
"api_url": {{.Values.api.url | quote }}
"api_url": {{ .Values.api.url | quote }},
"display_name": {{ .Values.displayName | quote }}
}
3 changes: 3 additions & 0 deletions chart/kubeseal-webgui/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ spec:
labels:
{{- include "kubeseal-webgui.labels" . | nindent 8 }}
spec:
{{- if .Values.serviceaccount.create }}
serviceAccountName: kubeseal-webgui
{{- end }}
containers:
- name: "api"
image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag }}"
Expand Down
13 changes: 13 additions & 0 deletions chart/kubeseal-webgui/templates/serviceaccount.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{- if .Values.serviceaccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: kubeseal-webgui
labels:
{{- include "kubeseal-webgui.labels" . | nindent 4 }}
{{- with .Values.route.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
{{- end }}

11 changes: 9 additions & 2 deletions chart/kubeseal-webgui/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,24 @@ api:
accessControlAllowOrigin: "*"
image:
repository: kubesealwebgui/api
tag: 2.0.0
tag: 2.1.0
ui:
image:
repository: kubesealwebgui/ui
tag: 2.0.0
tag: 2.1.0
image:
pullPolicy: IfNotPresent

nameOverride: ""
fullnameOverride: ""

# Optionally setup a display name for your kubeseal-webgui instance.
displayName: ""

# Set this value to false if you already have a default serviceaccount who is allowed to list namespaces.
serviceaccount:
create: true

# Setup resources for the pod
resources:
limits:
Expand Down
2 changes: 1 addition & 1 deletion dev/build-api-image.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#! /bin/sh

BASEDIR=$(dirname "$0")
docker build $BASEDIR/../api/ -t api:2.0.0 -t kubesealwebgui/api:2.0.0
docker build $BASEDIR/.. -f $BASEDIR/../Dockerfile.api -t api:2.1.0 -t kubesealwebgui/api:2.1.0
2 changes: 1 addition & 1 deletion dev/build-ui-image.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#! /bin/sh

BASEDIR=$(dirname "$0")
docker build $BASEDIR/../ui/ -t ui:2.0.0 -t kubesealwebgui/ui:2.0.0
docker build $BASEDIR/.. -f $BASEDIR/../Dockerfile.ui -t ui:2.1.0 -t kubesealwebgui/ui:2.1.0
21 changes: 21 additions & 0 deletions dev/local-dev-minikube.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#! /bin/sh

BASEDIR=$(dirname "$0")
minikube start && eval $(minikube docker-env)

helm template kubesealwebgui $BASEDIR/../chart/kubeseal-webgui/ | kubectl delete -f -

$BASEDIR/build-api-image.sh
$BASEDIR/build-ui-image.sh

helm template kubesealwebgui $BASEDIR/../chart/kubeseal-webgui/ | kubectl apply -f -

sleep 2

NEW_POD=$(kubectl get pods --field-selector status.phase=Pending -o custom-columns=":metadata.name")
echo " "
echo "Use the following commands to locally port-forward to the kubesealwebgui pod"
echo "-------------------------------------------"
printf "kubectl port-forward ${NEW_POD} -p 5000:5000\n"
printf "kubectl port-forward ${NEW_POD} -p 8080:8080\n"
echo " "
29 changes: 29 additions & 0 deletions dev/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const fetch = require('node-fetch');
async function get_namespaces(){
const url = 'http://localhost:5000/namespaces'
const res = await fetch(url);
const data = await res.json();//assuming data is json
var namespaces = JSON.parse(data);
renderNamespaces(namespaces);
pushNamespaceOptions(namespaces);
}

function renderNamespaces(ns){
ns.forEach(element => {
console.log(element);
});
}

function pushNamespaceOptions(namespaces) {
var namespaceOptions = [];
namespaces.forEach(element => {
namespaceOptions.push({
text: element,
value: element,
})
});
console.log(namespaceOptions)
}

get_namespaces();

Loading

0 comments on commit efa99a7

Please sign in to comment.