mirror of
https://github.com/openfaas/faas.git
synced 2025-06-19 04:26:35 +00:00
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:
committed by
Alex Ellis
parent
a38931ce69
commit
8133414183
@ -15,6 +15,8 @@ services:
|
|||||||
faas_nats_port: 4222
|
faas_nats_port: 4222
|
||||||
direct_functions: "true" # Functions are invoked directly over the overlay network
|
direct_functions: "true" # Functions are invoked directly over the overlay network
|
||||||
direct_functions_suffix: ""
|
direct_functions_suffix: ""
|
||||||
|
basic_auth: "false"
|
||||||
|
secret_mount_path: "/run/secrets/"
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
# limits: # uncomment to enable limits
|
# limits: # uncomment to enable limits
|
||||||
|
@ -16,6 +16,8 @@ services:
|
|||||||
faas_nats_port: 4222
|
faas_nats_port: 4222
|
||||||
direct_functions: "true" # Functions are invoked directly over the overlay network
|
direct_functions: "true" # Functions are invoked directly over the overlay network
|
||||||
direct_functions_suffix: ""
|
direct_functions_suffix: ""
|
||||||
|
basic_auth: "false"
|
||||||
|
secret_mount_path: "/run/secrets/"
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
# limits: # Enable if you want to limit memory usage
|
# limits: # Enable if you want to limit memory usage
|
||||||
|
@ -54,3 +54,5 @@ The gateway can be configured through the following environment variables:
|
|||||||
| `faas_promethus_port` | Port to connect to Prometheus. Default: `9090` |
|
| `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` | `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 |
|
| `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/`. |
|
||||||
|
@ -2,10 +2,12 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/openfaas/faas/gateway/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecorateWithBasicAuth enforces basic auth as a middleware with given credentials
|
// 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) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
user, password, ok := r.BasicAuth()
|
user, password, ok := r.BasicAuth()
|
||||||
@ -21,9 +23,3 @@ func DecorateWithBasicAuth(next http.HandlerFunc, credentials *BasicAuthCredenti
|
|||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicAuthCredentials for credentials
|
|
||||||
type BasicAuthCredentials struct {
|
|
||||||
User string
|
|
||||||
Password string
|
|
||||||
}
|
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/openfaas/faas/gateway/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_AuthWithValidPassword_Gives200(t *testing.T) {
|
func Test_AuthWithValidPassword_Gives200(t *testing.T) {
|
||||||
@ -18,7 +20,7 @@ func Test_AuthWithValidPassword_Gives200(t *testing.T) {
|
|||||||
wantPassword := "password"
|
wantPassword := "password"
|
||||||
r := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
|
r := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
|
||||||
r.SetBasicAuth(wantUser, wantPassword)
|
r.SetBasicAuth(wantUser, wantPassword)
|
||||||
wantCredentials := &BasicAuthCredentials{
|
wantCredentials := &types.BasicAuthCredentials{
|
||||||
User: wantUser,
|
User: wantUser,
|
||||||
Password: wantPassword,
|
Password: wantPassword,
|
||||||
}
|
}
|
||||||
@ -47,7 +49,7 @@ func Test_AuthWithInvalidPassword_Gives403(t *testing.T) {
|
|||||||
r := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
|
r := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
|
||||||
r.SetBasicAuth(wantUser, wantPassword)
|
r.SetBasicAuth(wantUser, wantPassword)
|
||||||
|
|
||||||
wantCredentials := &BasicAuthCredentials{
|
wantCredentials := &types.BasicAuthCredentials{
|
||||||
User: wantUser,
|
User: wantUser,
|
||||||
Password: "",
|
Password: "",
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
@ -35,24 +33,17 @@ func main() {
|
|||||||
|
|
||||||
log.Printf("Binding to external function provider: %s", config.FunctionsProviderURL)
|
log.Printf("Binding to external function provider: %s", config.FunctionsProviderURL)
|
||||||
|
|
||||||
var credentials *handlers.BasicAuthCredentials
|
var credentials *types.BasicAuthCredentials
|
||||||
|
|
||||||
if config.UseBasicAuth {
|
if config.UseBasicAuth {
|
||||||
userPath := "/var/secrets/basic_auth_user"
|
var readErr error
|
||||||
user, userErr := ioutil.ReadFile(userPath)
|
reader := types.ReadBasicAuthFromDisk{
|
||||||
if userErr != nil {
|
SecretMountPath: config.SecretMountPath,
|
||||||
log.Panicf("Unable to load %s", userPath)
|
|
||||||
}
|
}
|
||||||
|
credentials, readErr = reader.Read()
|
||||||
|
|
||||||
userPassword := "/var/secrets/basic_auth_password"
|
if readErr != nil {
|
||||||
password, passErr := ioutil.ReadFile(userPassword)
|
log.Panicf(readErr.Error())
|
||||||
if passErr != nil {
|
|
||||||
log.Panicf("Unable to load %s", userPassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
credentials = &handlers.BasicAuthCredentials{
|
|
||||||
User: strings.TrimSpace(string(user)),
|
|
||||||
Password: strings.TrimSpace(string(password)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
49
gateway/types/load_credentials.go
Normal file
49
gateway/types/load_credentials.go
Normal 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
|
||||||
|
}
|
@ -107,6 +107,12 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
|||||||
|
|
||||||
cfg.UseBasicAuth = parseBoolValue(hasEnv.Getenv("basic_auth"))
|
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
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +151,9 @@ type GatewayConfig struct {
|
|||||||
|
|
||||||
// If set, reads secrets from file-system for enabling basic auth.
|
// If set, reads secrets from file-system for enabling basic auth.
|
||||||
UseBasicAuth bool
|
UseBasicAuth bool
|
||||||
|
|
||||||
|
// SecretMountPath specifies where to read secrets from for embedded basic auth
|
||||||
|
SecretMountPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseNATS Use NATSor not
|
// UseNATS Use NATSor not
|
||||||
|
@ -209,11 +209,18 @@ func TestRead_BasicAuthDefaults(t *testing.T) {
|
|||||||
t.Logf("config.UseBasicAuth, want: %t, got: %t\n", false, config.UseBasicAuth)
|
t.Logf("config.UseBasicAuth, want: %t, got: %t\n", false, config.UseBasicAuth)
|
||||||
t.Fail()
|
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) {
|
func TestRead_BasicAuth_SetTrue(t *testing.T) {
|
||||||
defaults := NewEnvBucket()
|
defaults := NewEnvBucket()
|
||||||
defaults.Setenv("basic_auth", "true")
|
defaults.Setenv("basic_auth", "true")
|
||||||
|
defaults.Setenv("secret_mount_path", "/etc/openfaas/")
|
||||||
|
|
||||||
readConfig := ReadConfig{}
|
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.Logf("config.UseBasicAuth, want: %t, got: %t\n", true, config.UseBasicAuth)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wantSecretsMount := "/etc/openfaas/"
|
||||||
|
if config.SecretMountPath != wantSecretsMount {
|
||||||
|
t.Logf("config.SecretMountPath, want: %s, got: %s\n", wantSecretsMount, config.SecretMountPath)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user