diff --git a/projects/minishift/README.md b/projects/minishift/README.md new file mode 100644 index 0000000..bee0e7c --- /dev/null +++ b/projects/minishift/README.md @@ -0,0 +1,61 @@ +## Minishift Deployment Guide + +Use the files in this folder to create the applications using Minishift. + +- Ensure that you have configured Minishift to have enough memory. You can set the memory resources using: + + `$ minishift config set memory 9000` + +- Once this is set, you can start Minishift using the following command: + + `$ minishift start` + +- If `minishift start` ran successfully, you will be provided with the server URL in the command output, similar to the following: + + ``` + OpenShift server started. + + The server is accessible via web console at: + https://192.168.42.115:8443 + + You are logged in as: + User: developer + Password: + + To login as administrator: + oc login -u system:admin + + ``` + +- You can now login as admin using the `oc login ` command. + + `$ oc login https://192.168.42.115:8443` + +- You'll be asked to enter credentials one at a time. `admin` and `admin` can be used for the username and password. + +- You can specify a project name using the following name, or use the default `mobile-security` naming scheme. + + `$ oc new-project ` + +- The last step before creating the projects in Openshift is to update the `minishift.sh` file to ensure that the routes for the Keycloak and API Server as exposed on your Openshift instance. You will need to update the following `192.168.42.115` address to match your server URL provided above. You can also provide a different address suffix. + + ``` + export APP_ROUTES_SUFFIX=nip.io + export APP_ROUTES_SERVER_IP=192.168.42.115 + ``` + +- Once this is done, run `$ ./minishift.sh` and wait for it to complete. This will create a project called `mobile-security` (or the project you created) and it will create both a Keycloak and a NodeJS API Server app in that project. The `secure-app` realm will be created automatically in the Keycloak server also. + +- The credentials for the Keycloak server will be available under the `Environment` tab in the Keycloak deployment in Openshift. It will also be printed to the console like the following: + +``` +--> Deploying template "mobile-security/api-server" for "./minishift.json" to project mobile-security + + * With parameters: + * Keycloak admin username=admin + * Keycloak admin password=XXXXXXXX # generated + * Mongodb username=secure-app + * Mongodb password=XXXXXXXX # generated + * Mongodb database name=secure-app + * Mongodb admin password=XXXXXXXX # generated +``` diff --git a/projects/minishift/minishift.json b/projects/minishift/minishift.json new file mode 100644 index 0000000..b1140bc --- /dev/null +++ b/projects/minishift/minishift.json @@ -0,0 +1,428 @@ +{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "name": "api-server" + }, + "objects": [{ + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "keycloak-openshift", + "labels": { + "app": "keycloak-openshift" + } + }, + "spec": { + "ports": [{ + "name": "8080-tcp", + "protocol": "TCP", + "port": 8080, + "targetPort": 8080 + }], + "selector": { + "deploymentconfig": "keycloak-openshift" + }, + "type": "ClusterIP", + "sessionAffinity": "None" + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Route", + "apiVersion": "v1", + "metadata": { + "name": "keycloak-openshift", + "labels": { + "app": "keycloak-openshift" + } + }, + "spec": { + "host": "keycloak.${APP_ROUTES_SERVER_IP}.${APP_ROUTES_SUFFIX}", + "to": { + "kind": "Service", + "name": "keycloak-openshift", + "weight": 100 + }, + "port": { + "targetPort": "8080-tcp" + }, + "tls": { + "termination": "edge", + "insecureEdgeTerminationPolicy": "Allow" + }, + "wildcardPolicy": "None" + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "keycloak-openshift", + "generation": 1, + "labels": { + "app": "keycloak-openshift" + } + }, + "spec": { + "strategy": { + "type": "Rolling", + "resources": {}, + "activeDeadlineSeconds": 21600 + }, + "triggers": [{ + "type": "ConfigChange" + }], + "replicas": 1, + "selector": { + "app": "keycloak-openshift", + "deploymentconfig": "keycloak-openshift" + }, + "template": { + "metadata": { + "labels": { + "app": "keycloak-openshift", + "deploymentconfig": "keycloak-openshift" + } + }, + "spec": { + "containers": [{ + "name": "keycloak-openshift", + "image": "docker.io/weilee/keycloak-openshift:1.0", + "ports": [{ + "containerPort": 8080, + "protocol": "TCP" + }], + "env": [{ + "name": "KEYCLOAK_USER", + "value": "${KEYCLOAK_USER}" + }, + { + "name": "KEYCLOAK_PASSWORD", + "value": "${KEYCLOAK_PASSWORD}" + }, + { + "name": "CERT_LOOKUP_PROVIDER", + "value": "haproxy" + }, + { + "name": "PROXY_ADDRESS_FORWARDING", + "value": "true" + } + ], + "resources": {}, + "imagePullPolicy": "IfNotPresent" + }] + } + } + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "mongodb", + "labels": { + "app": "mongodb-persistent", + "template": "mongodb-persistent-template" + } + }, + "spec": { + "ports": [{ + "name": "mongo", + "protocol": "TCP", + "port": 27017, + "targetPort": 27017 + }], + "selector": { + "name": "mongodb" + }, + "type": "ClusterIP", + "sessionAffinity": "None" + }, + "status": { + "loadBalancer": {} + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "name": "mongodb" + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "10Gi" + } + } + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "mongodb", + "generation": 1, + "labels": { + "app": "mongodb-persistent", + "template": "mongodb-persistent-template" + } + }, + "spec": { + "strategy": { + "type": "Recreate" + }, + "triggers": [{ + "type": "ConfigChange" + }], + "replicas": 1, + "test": false, + "selector": { + "name": "mongodb" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "name": "mongodb" + } + }, + "spec": { + "volumes": [{ + "name": "mongodb-data", + "persistentVolumeClaim": { + "claimName": "mongodb" + } + }], + "containers": [{ + "name": "mongodb", + "image": "docker.io/centos/mongodb-32-centos7:latest", + "ports": [{ + "containerPort": 27017, + "protocol": "TCP" + }], + "env": [{ + "name": "MONGODB_USER", + "value": "${MONGODB_USER}" + }, + { + "name": "MONGODB_PASSWORD", + "value": "${MONGODB_PASSWORD}" + }, + { + "name": "MONGODB_DATABASE", + "value": "${MONGODB_DATABASE}" + }, + { + "name": "MONGODB_ADMIN_PASSWORD", + "value": "${MONGODB_ADMIN_PASSWORD}" + } + ], + "resources": { + "limits": { + "memory": "512Mi" + } + }, + "volumeMounts": [{ + "name": "mongodb-data", + "mountPath": "/var/lib/mongodb/data" + }], + "livenessProbe": { + "tcpSocket": { + "port": 27017 + }, + "initialDelaySeconds": 30, + "timeoutSeconds": 1, + "periodSeconds": 10, + "successThreshold": 1, + "failureThreshold": 3 + }, + "readinessProbe": { + "exec": { + "command": [ + "/bin/sh", + "-i", + "-c", + "mongo 127.0.0.1:27017/$MONGODB_DATABASE -u $MONGODB_USER -p $MONGODB_PASSWORD --eval=\"quit()\"" + ] + }, + "initialDelaySeconds": 3, + "timeoutSeconds": 1, + "periodSeconds": 10, + "successThreshold": 1, + "failureThreshold": 3 + }, + "imagePullPolicy": "IfNotPresent", + "securityContext": { + "capabilities": {}, + "privileged": false + } + }] + } + } + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "api-server", + "labels": { + "app": "api-server" + } + }, + "spec": { + "ports": [{ + "name": "8080-tcp", + "protocol": "TCP", + "port": 8080, + "targetPort": 8080 + }], + "selector": { + "deploymentconfig": "api-server" + }, + "type": "ClusterIP", + "sessionAffinity": "None" + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "Route", + "apiVersion": "v1", + "metadata": { + "name": "api-server", + "labels": { + "app": "api-server" + } + }, + "spec": { + "host": "api.${APP_ROUTES_SERVER_IP}.${APP_ROUTES_SUFFIX}", + "to": { + "kind": "Service", + "name": "api-server", + "weight": 100 + }, + "port": { + "targetPort": "8080-tcp" + }, + "tls": { + "termination": "edge" + }, + "wildcardPolicy": "None" + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "api-server", + "labels": { + "app": "api-server" + } + }, + "spec": { + "strategy": { + "type": "Rolling" + }, + "triggers": [{ + "type": "ConfigChange" + }], + "replicas": 1, + "selector": { + "app": "api-server", + "deploymentconfig": "api-server" + }, + "template": { + "metadata": { + "labels": { + "app": "api-server", + "deploymentconfig": "api-server" + } + }, + "spec": { + "containers": [{ + "name": "api-server", + "image": "docker.io/feedhenry/mobile-security:latest", + "ports": [{ + "containerPort": 8080, + "protocol": "TCP" + }], + "env": [{ + "name": "MONGO_DB_URI", + "value": "mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@mongodb/${MONGODB_DATABASE}" + }, + { + "name": "KEYCLOAK_URL", + "value": "https://keycloak.${APP_ROUTES_SERVER_IP}.${APP_ROUTES_SUFFIX}:8080/auth" + } + ], + "resources": {}, + "imagePullPolicy": "Always" + }] + } + } + } + } + ], + "parameters": [{ + "name": "KEYCLOAK_USER", + "displayName": "Keycloak admin username", + "description": "The name of the keycloak admin user", + "value": "admin" + }, + { + "name": "KEYCLOAK_PASSWORD", + "displayName": "Keycloak admin password", + "description": "The name of the keycloak admin password", + "generate": "expression", + "from": "[a-zA-Z0-9]{40}", + "required": true + }, + { + "name": "MONGODB_USER", + "displayName": "Mongodb username", + "description": "The name of the mongodb user", + "value": "secure-app" + }, + { + "name": "MONGODB_PASSWORD", + "displayName": "Mongodb password", + "description": "The name of the Mongodb password", + "generate": "expression", + "from": "[a-zA-Z0-9]{40}", + "required": true + }, + { + "name": "MONGODB_DATABASE", + "displayName": "Mongodb database name", + "description": "The name of the mongodb database", + "value": "secure-app" + }, + { + "name": "MONGODB_ADMIN_PASSWORD", + "displayName": "Mongodb admin password", + "description": "The name of the Mongodb admin password", + "generate": "expression", + "from": "[a-zA-Z0-9]{40}", + "required": true + }, + { + "name": "APP_ROUTES_SUFFIX", + "displayName": "App Routes Suffix", + "description": "The Suffix for the App Routes" + }, + { + "name": "APP_ROUTES_SERVER_IP", + "displayName": "App Routes IP Address", + "description": "The IP Address of the App Routes" + } + ] +} diff --git a/projects/minishift/minishift.sh b/projects/minishift/minishift.sh new file mode 100755 index 0000000..868b9e1 --- /dev/null +++ b/projects/minishift/minishift.sh @@ -0,0 +1,39 @@ +#!/bin/bash +export PROJECT_NAME=mobile-security +export APP_ROUTES_SUFFIX=nip.io +export APP_ROUTES_SERVER_IP=192.168.42.115 + +# Create the project & services +oc new-project $PROJECT_NAME +oc new-app -f ./minishift.json --param APP_ROUTES_SUFFIX=$APP_ROUTES_SUFFIX --param APP_ROUTES_SERVER_IP=$APP_ROUTES_SERVER_IP +sleep 1 + +# Wait for Keycloak to run to import realm data +KEYCLOAK_ROUTE=`oc get routes -l app=keycloak-openshift -o jsonpath={.items[0].spec.host}` +echo "Keycloak route is $KEYCLOAK_ROUTE" +KEYCLOAK_AUTH_URL="https://${KEYCLOAK_ROUTE}/auth/" +NEXT_WAIT_TIME=0 +echo "Waiting for Keycloak to return 200" +until $(curl --output /dev/null --silent --head --fail -k $KEYCLOAK_AUTH_URL) || [ $NEXT_WAIT_TIME -eq 120 ]; do + oc get po + echo "Still waiting for Keycloak to return 200" + ((NEXT_WAIT_TIME++)) + sleep 5 # sleep 5 seconds for a max of 120 times (600 seconds = 10 minutes) +done + +KEYCLOAK_POD_NAME=`oc get pods -l app=keycloak-openshift -o jsonpath={.items[0].metadata.name}` +echo "Keycloak pod name is $KEYCLOAK_POD_NAME" + +echo "Read Keycloak admin username and password" +KEYCLOAK_ADMIN_USERNAME=`oc env pods/$KEYCLOAK_POD_NAME --list | grep KEYCLOAK_USER | cut -d"=" -f2` +KEYCLOAK_ADMIN_PASSWORD=`oc env pods/$KEYCLOAK_POD_NAME --list | grep KEYCLOAK_PASSWORD | cut -d"=" -f2` + +echo "Importing Keycloak realm file" +# Sync the data file +oc rsync ../keycloak $KEYCLOAK_POD_NAME:/tmp/ + +# Do import +oc exec $KEYCLOAK_POD_NAME -- /opt/jboss/keycloak/bin/kcadm.sh config credentials --server http://localhost:8080/auth --realm master --user $KEYCLOAK_ADMIN_USERNAME --password $KEYCLOAK_ADMIN_PASSWORD --config /tmp/kcadm.config +oc exec $KEYCLOAK_POD_NAME -- /opt/jboss/keycloak/bin/kcadm.sh create realms -f /tmp/keycloak/secure-app-realm.json --config /tmp/kcadm.config + +echo "Keycloak realm created"