diff --git a/deploy_stack.sh b/deploy_stack.sh index a9ffd66c73..8b09e62ddc 100755 --- a/deploy_stack.sh +++ b/deploy_stack.sh @@ -1,66 +1,127 @@ -#!/bin/sh +#!/usr/bin/env bash +set -e -if ! [ -x "$(command -v docker)" ]; then - echo 'Unable to find docker command, please install Docker (https://www.docker.com/) and retry' >&2 - exit 1 -fi +CHECK_MARK="\033[0;32m\xE2\x9C\x94\033[0m" +PARTY_POPPER="\xF0\x9F\x8E\x89" +DEBUG_LOG=${DEBUG_LOG:=0} +function debug() { + if [[ ${DEBUG_LOG} = "1" ]]; then + echo -e "$*"; + fi +} -export BASIC_AUTH="true" +check_required_software() { + echo -en " Required software check" -sha_cmd="shasum -a 256" -if ! command -v shasum >/dev/null; then - sha_cmd="sha256sum" -fi + if ! [[ -x "$(command -v docker)" ]]; then + echo 'Unable to find docker command, please install Docker (https://www.docker.com/) and retry' >&2 + exit 1 + fi -while [ ! $# -eq 0 ] + echo -e "\r${CHECK_MARK} Required software check" +} + +setup_http_signature() { + echo -en " Http encryption settings" + + rm signing.key > /dev/null 2>&1 || true && rm signing.key.pub > /dev/null 2>&1 || true + docker secret rm http-signing-private-key > /dev/null 2>&1 || true + docker secret rm http-signing-public-key > /dev/null 2>&1 || true + + ssh-keygen -t rsa -b 2048 -N "" -m PEM -f signing.key > /dev/null 2>&1 + openssl rsa -in ./signing.key -pubout -outform PEM -out signing.key.pub > /dev/null 2>&1 + + cat signing.key | docker secret create http-signing-private-key - > /dev/null 2>&1 || true + cat signing.key.pub | docker secret create http-signing-public-key - > /dev/null 2>&1 || true + + rm signing.key || true && rm signing.key.pub || true + echo -e "\r${CHECK_MARK} Http encryption settings" +} + +setup_basic_auth() { + export BASIC_AUTH="true" + sha_cmd="shasum -a 256" + if ! command -v shasum >/dev/null; then + sha_cmd="sha256sum" + fi + + # Secrets should be created even if basic-auth is disabled. + if (echo "admin" | docker secret create basic-auth-user - > /dev/null 2>&1) + then + debug "[Credentials]\n\tusername: admin created" + fi + + secret=$(head -c 16 /dev/urandom| $sha_cmd | cut -d " " -f 1) + if (echo "$secret" | docker secret create basic-auth-password - > /dev/null 2>&1) + then + echo -e "[Credentials]\n\tusername: admin \n\tpassword: ${secret}\n\techo -n \"${secret}\" | faas-cli login --username=admin --password-stdin" + else + debug "[Credentials]\n\talready exist, not creating" + fi + + if [[ ${BASIC_AUTH} = "true" ]]; + then + debug "\tenabling basic authentication for gateway.." + debug "" + else + debug "\tdisabling basic authentication for gateway.." + debug "" + fi + + echo -e "\r${CHECK_MARK} Basic authentication settings" +} + +deploy_docker_stack() { + arch=$(uname -m) + case "$arch" in + + "armv7l") echo -en " Deploying OpenFaaS core services for ARM" + composefile="docker-compose.armhf.yml" + ;; + "aarch64") echo -en " Deploying OpenFaaS core services for ARM64" + composefile="docker-compose.arm64.yml" + ;; + *) echo -en " Deploying OpenFaaS core services" + composefile="docker-compose.yml" + ;; + esac + + docker stack deploy func --compose-file ${composefile} > /dev/null + echo -e "\r${CHECK_MARK} Deploying OpenFaaS core services" +} + +main() { + check_required_software + setup_basic_auth + setup_http_signature + deploy_docker_stack + echo -e ${PARTY_POPPER}OpenFaaS docker swarm setup complete +} + +POSITIONAL=() +while [[ $# -gt 0 ]] do - case "$1" in - --no-auth | -n) - export BASIC_AUTH="false" - ;; - --help | -h) - echo "Usage: \n [default]\tdeploy the OpenFaaS core services\n --no-auth [-n]\tdisable basic authentication.\n --help\tdisplays this screen" - exit - ;; - esac - shift -done +key="$1" -# Secrets should be created even if basic-auth is disabled. -echo "Attempting to create credentials for gateway.." -echo "admin" | docker secret create basic-auth-user - -secret=$(head -c 16 /dev/urandom| $sha_cmd | cut -d " " -f 1) -echo "$secret" | docker secret create basic-auth-password - -if [ $? = 0 ]; -then - echo "[Credentials]\n username: admin \n password: $secret\n echo -n "$secret" | faas-cli login --username=admin --password-stdin" -else - echo "[Credentials]\n already exist, not creating" -fi - -if [ $BASIC_AUTH = "true" ]; -then - echo "" - echo "Enabling basic authentication for gateway.." - echo "" -else - echo "" - echo "Disabling basic authentication for gateway.." - echo "" -fi - -arch=$(uname -m) -case "$arch" in - -"armv7l") echo "Deploying OpenFaaS core services for ARM" - composefile="docker-compose.armhf.yml" - ;; -"aarch64") echo "Deploying OpenFaaS core services for ARM64" - composefile="docker-compose.arm64.yml" - ;; -*) echo "Deploying OpenFaaS core services" - composefile="docker-compose.yml" - ;; +case $key in + --no-auth | -n) + export BASIC_AUTH="false" + shift + ;; + --debug | -d) + DEBUG_LOG="1" + shift + ;; + --help | -h) + echo -e "Usage: +[default] deploy the OpenFaaS core services +--no-auth [-n] disable basic authentication. +--help displays this screen +--debug [-d] enable debug logging" + exit + ;; esac +done +set -- "${POSITIONAL[@]}" # restore positional parameters -docker stack deploy func --compose-file $composefile +main \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 7834a509a5..a2ce942498 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,6 +38,7 @@ services: secrets: - basic-auth-user - basic-auth-password + - http-signing-public-key # Docker Swarm provider faas-swarm: @@ -198,3 +199,5 @@ secrets: external: true basic-auth-password: external: true + http-signing-public-key: + external: true diff --git a/gateway/handlers/certificates_handler.go b/gateway/handlers/certificates_handler.go new file mode 100644 index 0000000000..d5919391dc --- /dev/null +++ b/gateway/handlers/certificates_handler.go @@ -0,0 +1,69 @@ +package handlers + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + + "github.com/gorilla/mux" +) + +var keyMap = map[string]string{ + "callback": "http-signing-public-key", +} + +type KeyType struct { + Id string `json:"id"` + PEM string `json:"pem"` +} + +func MakeCertificatesHandler() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + keyID := vars["id"] + secretKey, ok := keyMap[keyID] + if !ok { + w.WriteHeader(http.StatusNotFound) + message := fmt.Sprintf("Unable to find certificate %s.", keyID) + w.Write([]byte(message)) + log.Println(message) + return + } + + if _, err := os.Stat(fmt.Sprintf("/run/secrets/%s", secretKey)); os.IsNotExist(err) { + w.WriteHeader(http.StatusNotFound) + message := fmt.Sprintf("Unable to find secret for key %s.", keyID) + w.Write([]byte(message)) + log.Println(message) + return + } + + signingKey, err := ioutil.ReadFile(fmt.Sprintf("/run/secrets/%s", secretKey)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + message := fmt.Sprintf("error reading secret for key %s. %v", keyID, err) + w.Write([]byte(message)) + log.Println(message) + } + + key := &KeyType{ + Id: secretKey, + PEM: string(signingKey), + } + + bytesOut, marshalErr := json.Marshal(key) + if marshalErr != nil { + w.WriteHeader(http.StatusInternalServerError) + message := fmt.Sprintf("error marshalling json for key %s. %v", keyID, err) + w.Write([]byte(message)) + log.Println(message) + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(bytesOut) + } +} diff --git a/gateway/server.go b/gateway/server.go index d0d87acd75..9c02b0a8ff 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -209,7 +209,7 @@ func main() { metricsHandler := metrics.PrometheusHandler() r.Handle("/metrics", metricsHandler) r.HandleFunc("/healthz", handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer)).Methods(http.MethodGet) - + r.Handle("/certificates/{id}", handlers.MakeCertificatesHandler()) r.Handle("/", http.RedirectHandler("/ui/", http.StatusMovedPermanently)).Methods(http.MethodGet) tcpPort := 8080