Read secrets from variable path

This change enables secrets to be read from any mount on disk
rather than hard-coding a certain location which suits Swarm or
K8s. The default value if not specified will look in the Swarm
location of /run/secrets/

README.md (docs) updated and set to off by default.

Signed-off-by: Alex Ellis (VMware) <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis (VMware)
2018-05-22 14:46:29 +01:00
committed by Alex Ellis
parent a38931ce69
commit 8133414183
9 changed files with 91 additions and 25 deletions

View File

@ -15,6 +15,8 @@ services:
faas_nats_port: 4222
direct_functions: "true" # Functions are invoked directly over the overlay network
direct_functions_suffix: ""
basic_auth: "false"
secret_mount_path: "/run/secrets/"
deploy:
resources:
# limits: # uncomment to enable limits

View File

@ -16,6 +16,8 @@ services:
faas_nats_port: 4222
direct_functions: "true" # Functions are invoked directly over the overlay network
direct_functions_suffix: ""
basic_auth: "false"
secret_mount_path: "/run/secrets/"
deploy:
resources:
# limits: # Enable if you want to limit memory usage

View File

@ -54,3 +54,5 @@ The gateway can be configured through the following environment variables:
| `faas_promethus_port` | Port to connect to Prometheus. Default: `9090` |
| `direct_functions` | `true` or `false` - functions are invoked directly over overlay network without passing through provider |
| `direct_functions_suffix` | Provide a DNS suffix for invoking functions directly over overlay network |
| `basic_auth` | Set to `true` or `false` to enable embedded basic auth on the /system and /ui endpoints (recommended) |
| `secret_mount_path` | Set a location where you have mounted `basic-auth-user` and `basic-auth-password`, default: `/run/secrets/`. |

View File

@ -2,10 +2,12 @@ package handlers
import (
"net/http"
"github.com/openfaas/faas/gateway/types"
)
// DecorateWithBasicAuth enforces basic auth as a middleware with given credentials
func DecorateWithBasicAuth(next http.HandlerFunc, credentials *BasicAuthCredentials) http.HandlerFunc {
func DecorateWithBasicAuth(next http.HandlerFunc, credentials *types.BasicAuthCredentials) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, password, ok := r.BasicAuth()
@ -21,9 +23,3 @@ func DecorateWithBasicAuth(next http.HandlerFunc, credentials *BasicAuthCredenti
next.ServeHTTP(w, r)
}
}
// BasicAuthCredentials for credentials
type BasicAuthCredentials struct {
User string
Password string
}

View File

@ -5,6 +5,8 @@ import (
"net/http"
"net/http/httptest"
"testing"
"github.com/openfaas/faas/gateway/types"
)
func Test_AuthWithValidPassword_Gives200(t *testing.T) {
@ -18,7 +20,7 @@ func Test_AuthWithValidPassword_Gives200(t *testing.T) {
wantPassword := "password"
r := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
r.SetBasicAuth(wantUser, wantPassword)
wantCredentials := &BasicAuthCredentials{
wantCredentials := &types.BasicAuthCredentials{
User: wantUser,
Password: wantPassword,
}
@ -47,7 +49,7 @@ func Test_AuthWithInvalidPassword_Gives403(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
r.SetBasicAuth(wantUser, wantPassword)
wantCredentials := &BasicAuthCredentials{
wantCredentials := &types.BasicAuthCredentials{
User: wantUser,
Password: "",
}

View File

@ -5,10 +5,8 @@ package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
"github.com/gorilla/mux"
@ -35,24 +33,17 @@ func main() {
log.Printf("Binding to external function provider: %s", config.FunctionsProviderURL)
var credentials *handlers.BasicAuthCredentials
var credentials *types.BasicAuthCredentials
if config.UseBasicAuth {
userPath := "/var/secrets/basic_auth_user"
user, userErr := ioutil.ReadFile(userPath)
if userErr != nil {
log.Panicf("Unable to load %s", userPath)
var readErr error
reader := types.ReadBasicAuthFromDisk{
SecretMountPath: config.SecretMountPath,
}
credentials, readErr = reader.Read()
userPassword := "/var/secrets/basic_auth_password"
password, passErr := ioutil.ReadFile(userPassword)
if passErr != nil {
log.Panicf("Unable to load %s", userPassword)
}
credentials = &handlers.BasicAuthCredentials{
User: strings.TrimSpace(string(user)),
Password: strings.TrimSpace(string(password)),
if readErr != nil {
log.Panicf(readErr.Error())
}
}

View File

@ -0,0 +1,49 @@
package types
import (
"fmt"
"io/ioutil"
"path"
"strings"
)
// BasicAuthCredentials for credentials
type BasicAuthCredentials struct {
User string
Password string
}
type ReadBasicAuth interface {
Read() (error, *BasicAuthCredentials)
}
type ReadBasicAuthFromDisk struct {
SecretMountPath string
}
func (r *ReadBasicAuthFromDisk) Read() (*BasicAuthCredentials, error) {
var credentials *BasicAuthCredentials
if len(r.SecretMountPath) == 0 {
return nil, fmt.Errorf("invalid SecretMountPath specified for reading secrets")
}
userPath := path.Join(r.SecretMountPath, "basic-auth-user")
user, userErr := ioutil.ReadFile(userPath)
if userErr != nil {
return nil, fmt.Errorf("unable to load %s", userPath)
}
userPassword := path.Join(r.SecretMountPath, "basic-auth-password")
password, passErr := ioutil.ReadFile(userPassword)
if passErr != nil {
return nil, fmt.Errorf("Unable to load %s", userPassword)
}
credentials = &BasicAuthCredentials{
User: strings.TrimSpace(string(user)),
Password: strings.TrimSpace(string(password)),
}
return credentials, nil
}

View File

@ -107,6 +107,12 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
cfg.UseBasicAuth = parseBoolValue(hasEnv.Getenv("basic_auth"))
secretPath := hasEnv.Getenv("secret_mount_path")
if len(secretPath) == 0 {
secretPath = "/run/secrets/"
}
cfg.SecretMountPath = secretPath
return cfg
}
@ -145,6 +151,9 @@ type GatewayConfig struct {
// If set, reads secrets from file-system for enabling basic auth.
UseBasicAuth bool
// SecretMountPath specifies where to read secrets from for embedded basic auth
SecretMountPath string
}
// UseNATS Use NATSor not

View File

@ -209,11 +209,18 @@ func TestRead_BasicAuthDefaults(t *testing.T) {
t.Logf("config.UseBasicAuth, want: %t, got: %t\n", false, config.UseBasicAuth)
t.Fail()
}
wantSecretsMount := "/run/secrets/"
if config.SecretMountPath != wantSecretsMount {
t.Logf("config.SecretMountPath, want: %s, got: %s\n", wantSecretsMount, config.SecretMountPath)
t.Fail()
}
}
func TestRead_BasicAuth_SetTrue(t *testing.T) {
defaults := NewEnvBucket()
defaults.Setenv("basic_auth", "true")
defaults.Setenv("secret_mount_path", "/etc/openfaas/")
readConfig := ReadConfig{}
@ -223,4 +230,10 @@ func TestRead_BasicAuth_SetTrue(t *testing.T) {
t.Logf("config.UseBasicAuth, want: %t, got: %t\n", true, config.UseBasicAuth)
t.Fail()
}
wantSecretsMount := "/etc/openfaas/"
if config.SecretMountPath != wantSecretsMount {
t.Logf("config.SecretMountPath, want: %s, got: %s\n", wantSecretsMount, config.SecretMountPath)
t.Fail()
}
}