diff --git a/README.md b/README.md index 3ba154d..6c1a6fe 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,9 @@ Follow the instructions [in the dashboard-dev-overrides repository](https://open.greenhost.net/stackspin/dashboard-dev-overrides#dashboard-dev-overrides) in order to set up a development-capable cluster. The end-points for the Dashboard, -as well as Kratos and Hydra, will point to `localhost` in that cluster. -As a result, you can run those components locally, and still log into Stackspin -applications that run on the cluster. +as well as Kratos and Hydra, will point to `http://stackspin_proxy:8081` in that cluster. +As a result, you can run components using the `docker-compose` file in +this repository, and still log into Stackspin applications that run on the cluster. ## Setting up the local development environment @@ -63,115 +63,42 @@ After this process is finished, the following will run locally: - The [dashboard-backend](https://open.greenhost.net/stackspin/dashboard-backend) -The following will be available on localhost through a proxy and port-forwards: +The following will be available locally through a proxy and port-forwards: -- Hydra -- Kratos +- Hydra admin +- Kratos admin and public - The MariaDB database connections These need to be available locally, because Kratos wants to run on the same domain as the front-end that serves the login interface. -### 1. Setup port forwards +### 1. Setup hosts file -To be able to work on the dashboard, -we have to configure our development system to access all the remote services and endpoints. - -A helper script is available in this directory -to setup and redirect the relevant ports locally. -It will open ports 8000, 8080, 4445, 3306 to get access to all APIs. -To use it, you'll need `kubectl` access to the cluster: - -1. Install `kubectl` (available through `snap` on Linux) -2. Download the kubeconfig: `scp root@stackspin.example.com:/etc/rancher/k3s/k3s.yaml kube_config_stackspin.example.com.yaml` -3. Set `kubectl` to use the kubeconfig: `export KUBECONFIG=$PWD/kube_config_stackspin.example.com.yaml`. -4. Test if it works: - - ``` - kubectl get ingress -n stackspin - ``` - - Should return something like: - - ``` - NAME CLASS HOSTS ADDRESS PORTS AGE - hydra-public sso.stackspin.example.com 213.108.110.5 80, 443 39d - dashboard dashboard.stackspin.example.com 213.108.110.5 80, 443 150d - kube-prometheus-stack-grafana grafana.stackspin.example.com 213.108.110.5 80, 443 108d - kube-prometheus-stack-alertmanager alertmanager.stackspin.example.com 213.108.110.5 80, 443 108d - kube-prometheus-stack-prometheus prometheus.stackspin.example.com 213.108.110.5 80, 443 108d - ``` -5. Run the script to forward ports of the services to your local setup: - - ``` - ./set-port-forward.sh - ``` - - As long as the script runs, your connection stays open. - End the script by pressing `ctrl + c` and your port-forwards will end as well. - -### 2. Configure a local proxy - -Because of strict CORS headers, -we have to map the public Kratos API and login app, -which we will run locally with a local proxy. - -This can be done with any proxy server, here we use `nginx`. -Be sure you have NGINX installed and listening on port 80 locally: -`sudo apt-get install nginx` should be enough. - -Now configure NGINX with this configuration in `/etc/nginx/sites-enabled/default` +The application will run on `http://stackspin_proxy`. Add the following line to +`/etc/hosts` to be able to access that from your browser: ``` -server { - listen 80 default_server; - listen [::]:80 default_server; - - root /var/www/html; - - index index.html; - - server_name _; - - # Flask app and dashboard-backend - location / { - proxy_pass http://127.0.0.1:5000/; - proxy_redirect default; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - # Kratos APIs - location /kratos/ { - proxy_pass http://127.0.0.1:8080/; - proxy_redirect default; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } -} +127.0.0.1 stackspin_proxy ``` -Reload your NGINX: +### 2. Kubernetes access + +The script needs you to have access to the Kubernetes cluster that runs +Stackspin. Point the `KUBECONFIG` environment variable to a kubectl config. That +kubeconfig will be mounted inside docker containers, so also make sure your +Docker user can read it. + +### 3. Run it all + +Now, run this script that sets a few environment variables based on what is in +your cluster secrets, and starts `docker-compose` to start a reverse proxy as +well as the flask application in this repository. ``` -sudo systemctl reload nginx.service +./run_app.sh ``` -### 3. Run FLASK app +### 4. Front-end developmenet -Now it is time to start the flask app. -Please make sure you are using Python 3 in your environment. -And install the required dependencies: - -``` -pip3 install -r requirements.txt -``` - -Then copy `run_app.sh` to `run_app.local.sh` and change the secrets defined in it. - -You can now start the app by running - -``` -./run_app.local.sh -``` - -Lastly, start the [dashboard front-end app](https://open.greenhost.net/stackspin/dashboard/#yarn-start) +Start the [dashboard front-end app](https://open.greenhost.net/stackspin/dashboard/#yarn-start). diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..34de6fd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,75 @@ +version: '3' +services: + stackspin_proxy: + image: nginx:1.22.0 + ports: + - "8081:8081" + volumes: + - ./proxy/default.conf:/etc/nginx/conf.d/default.conf + depends_on: + - kube_port_kratos_public + - flask_app + flask_app: + build: . + environment: + - FLASK_APP=app.py + - FLASK_ENV=development + - HYDRA_CLIENT_ID=dashboard-local + + # Domain-specific URL settings + - HYDRA_AUTHORIZATION_BASE_URL=https://sso.$DOMAIN/oauth2/auth + - TOKEN_URL=https://sso.$DOMAIN/oauth2/token + - HYDRA_PUBLIC_URL=https://sso.$DOMAIN + + # Local path overrides + - KRATOS_PUBLIC_URL=http://stackspin_proxy:8081/kratos + - KRATOS_ADMIN_URL=http://kube_port_kratos_admin:8000 + - HYDRA_ADMIN_URL=http://kube_port_hydra_admin:4445 + - LOGIN_PANEL_URL=http://stackspin_proxy:8081/web/ + - DATABASE_URL=mysql+pymysql://stackspin:$DATABASE_PASSWORD@kube_port_mysql/stackspin + + # ENV variables that are deployment-specific + - SECRET_KEY=$FLASK_SECRET_KEY + - HYDRA_CLIENT_SECRET=$HYDRA_CLIENT_SECRET + # - OAUTHLIB_INSECURE_TRANSPORT=1 + ports: + - "5000:5000" + volumes: + - .:/app + depends_on: + - kube_port_mysql + entrypoint: ["bash", "-c", "flask run --host $$(hostname -i)"] + kube_port_kratos_admin: + image: bitnami/kubectl:1.24.1 + user: "${KUBECTL_UID}:${KUBECTL_GID}" + expose: + - 8000 + volumes: + - "$KUBECONFIG:/.kube/config" + entrypoint: ["bash", "-c", "kubectl -n stackspin port-forward --address $$(hostname -i) service/kratos-admin 8000:80"] + kube_port_hydra_admin: + image: bitnami/kubectl:1.24.1 + user: "${KUBECTL_UID}:${KUBECTL_GID}" + expose: + - 4445 + volumes: + - "$KUBECONFIG:/.kube/config" + entrypoint: ["bash", "-c", "kubectl -n stackspin port-forward --address $$(hostname -i) service/hydra-admin 4445:4445"] + kube_port_kratos_public: + image: bitnami/kubectl:1.24.1 + user: "${KUBECTL_UID}:${KUBECTL_GID}" + ports: + - "8080:8080" + expose: + - 8080 + volumes: + - "$KUBECONFIG:/.kube/config" + entrypoint: ["bash", "-c", "kubectl -n stackspin port-forward --address $$(hostname -i) service/kratos-public 8080:80"] + kube_port_mysql: + image: bitnami/kubectl:1.24.1 + user: "${KUBECTL_UID}:${KUBECTL_GID}" + expose: + - 3306 + volumes: + - "$KUBECONFIG:/.kube/config" + entrypoint: ["bash", "-c", "kubectl -n stackspin port-forward --address $$(hostname -i) service/single-sign-on-database-mariadb 3306:3306"] diff --git a/proxy/default.conf b/proxy/default.conf new file mode 100644 index 0000000..42a6fdf --- /dev/null +++ b/proxy/default.conf @@ -0,0 +1,26 @@ +# Default server configuration +# +server { + listen 8081 default_server; + listen [::]:8081 default_server; + + root /var/www/html; + + index index.html; + + server_name _; + + # Flask app + location / { + proxy_pass http://flask_app:5000/; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # Kratos Public + location /kratos/ { + proxy_pass http://kube_port_kratos_public:8080/; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} diff --git a/run_app.sh b/run_app.sh index ff58aa0..b4d203d 100755 --- a/run_app.sh +++ b/run_app.sh @@ -1,39 +1,32 @@ +#!/usr/bin/env bash -GREEN='\033[0;32m' -RED='\033[1;31m' -NC='\033[0m' # No Color +set -euo pipefail -# Check if kratos port is open -if nc -z localhost 8000; -then - echo -e "${GREEN}Great! It looks like the Kratos Admin port is available${NC}" -else - echo -e "${RED}**********************************************************${NC}" - echo -e "${RED}WARNING! It looks like the Kratos Admin port NOT available${NC}" - echo -e "${RED}please run in a seperate terminal: ${NC}" - echo -e "${RED} ${NC}" - echo -e "${RED}./set-port-forward.sh ${NC}" - echo -e "${RED} ${NC}" - echo -e "${RED}We will continue to start the app after 5 seconds. ${NC}" - echo -e "${RED}**********************************************************${NC}" - sleep 5 +export DATABASE_PASSWORD=$(kubectl get secret -n flux-system stackspin-single-sign-on-variables -o jsonpath --template '{.data.dashboard_database_password}' | base64 -d) +export DOMAIN=$(kubectl get secret -n flux-system stackspin-cluster-variables -o jsonpath --template '{.data.domain}' | base64 -d) +export HYDRA_CLIENT_SECRET=$(kubectl get secret -n flux-system stackspin-dashboard-local-oauth-variables -o jsonpath --template '{.data.client_secret}' | base64 -d) +export FLASK_SECRET_KEY=$(kubectl get secret -n flux-system stackspin-dashboard-variables -o jsonpath --template '{.data.backend_secret_key}' | base64 -d) + + +if [[ -z "$DATABASE_PASSWORD" ]]; then + echo "Could not find database password in stackspin-single-sign-on-variables secret" + exit 1 fi -export FLASK_APP=app.py -export FLASK_ENV=development -export SECRET_KEY="e38hq!@0n64g@qe6)5csk41t=ljo2vllog(%k7njnm4b@kh42c" -export HYDRA_CLIENT_ID="dashboard-local" -export HYDRA_CLIENT_SECRET="gDSEuakxzybHBHJocnmtDOLMwlWWEvPh" -export HYDRA_AUTHORIZATION_BASE_URL="https://sso.init.stackspin.net/oauth2/auth" -export TOKEN_URL="https://sso.init.stackspin.net/oauth2/token" +if [[ -z "$DOMAIN" ]]; then + echo "Could not find domain name in stackspin-cluster-variables secret" + exit 1 +fi -# Login facilitator paths -export KRATOS_PUBLIC_URL=http://localhost/kratos -export KRATOS_ADMIN_URL=http://localhost:8000 -export HYDRA_PUBLIC_URL="https://sso.init.stackspin.net" -export HYDRA_ADMIN_URL=http://localhost:4445 -export LOGIN_PANEL_URL=http://localhost/web/ -#export DATABASE_URL="mysql+pymysql://stackspin:stackspin@localhost/stackspin?charset=utf8mb4" -export DATABASE_URL="mysql+pymysql://stackspin:OZBSDkMdbdvEIOomnwpOqLdaiHDKbzWY@localhost/stackspin" +if [[ -z "$FLASK_SECRET_KEY" ]]; then + echo "Could not find backend_secret_key in stackspin-dashboard-variables secret" + exit 1 +fi -flask run +if [[ -z "$HYDRA_CLIENT_SECRET" ]]; then + echo "Could not find client_secret in stackspin-dashboard-local-oauth-variables secret" + echo "make sure you add this secret following instructions in the dashboard-dev-overrides repository" + exit 1 +fi + +KUBECTL_UID=${UID:-1001} KUBECTL_GID=${GID:-0} docker compose up diff --git a/set-port-forward.sh b/set-port-forward.sh deleted file mode 100755 index 2577a33..0000000 --- a/set-port-forward.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -echo " -kratos admin port will be at localhost: 8000 -kratos public port will be at localhost: 8080 -hydra admin port will be at localhost: 4445 -mysql port will be at localhost: 3306 -" - -# Kill all processes when this script ends -trap "exit" INT TERM ERR -trap "kill 0" EXIT - -# Add forwarded ports for all processes -kubectl port-forward -n stackspin service/kratos-admin 8000:80 & -kubectl port-forward -n stackspin service/kratos-public 8080:80 & -kubectl port-forward -n stackspin service/hydra-admin 4445:4445 & -kubectl port-forward -n stackspin service/single-sign-on-database-mariadb 3306:3306