mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-08 16:06:47 +00:00
Adds secrets support and binding of secrets at runtime to functions. Files are written in plain-text to a 0644 permission folder which can only be read by root and the containers requesting the secret through the OpenFaaS API. Tested by deploying an alpine function using "cat" as its fprocess. Happy to revisit at a later date and look into encryption at rest. This should be on-par with using Kubernetes in its default unencrypted state. Fixes: #29 Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
112 lines
2.6 KiB
Go
112 lines
2.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
|
|
"github.com/containerd/containerd"
|
|
"github.com/openfaas/faas-provider/types"
|
|
)
|
|
|
|
const secretFilePermission = 0644
|
|
|
|
func MakeSecretHandler(c *containerd.Client, mountPath string) func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
err := os.MkdirAll(mountPath, secretFilePermission)
|
|
if err != nil {
|
|
log.Printf("Creating path: %s, error: %s\n", mountPath, err)
|
|
}
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Body != nil {
|
|
defer r.Body.Close()
|
|
}
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
listSecrets(c, w, r, mountPath)
|
|
case http.MethodPost:
|
|
createSecret(c, w, r, mountPath)
|
|
case http.MethodPut:
|
|
createSecret(c, w, r, mountPath)
|
|
case http.MethodDelete:
|
|
deleteSecret(c, w, r, mountPath)
|
|
default:
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func listSecrets(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) {
|
|
files, err := ioutil.ReadDir(mountPath)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
secrets := []types.Secret{}
|
|
for _, f := range files {
|
|
secrets = append(secrets, types.Secret{Name: f.Name()})
|
|
}
|
|
|
|
bytesOut, _ := json.Marshal(secrets)
|
|
w.Write(bytesOut)
|
|
}
|
|
|
|
func createSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) {
|
|
secret, err := parseSecret(r)
|
|
if err != nil {
|
|
log.Printf("[secret] error %s", err.Error())
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
err = ioutil.WriteFile(path.Join(mountPath, secret.Name), []byte(secret.Value), secretFilePermission)
|
|
|
|
if err != nil {
|
|
log.Printf("[secret] error %s", err.Error())
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func parseSecret(r *http.Request) (types.Secret, error) {
|
|
secret := types.Secret{}
|
|
bytesOut, err := ioutil.ReadAll(r.Body)
|
|
if err != nil {
|
|
return secret, err
|
|
}
|
|
|
|
err = json.Unmarshal(bytesOut, &secret)
|
|
return secret, err
|
|
}
|
|
|
|
func deleteSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) {
|
|
secret, err := parseSecret(r)
|
|
if err != nil {
|
|
log.Printf("[secret] error %s", err.Error())
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
log.Printf("[secret] error %s", err.Error())
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
err = os.Remove(path.Join(mountPath, secret.Name))
|
|
|
|
if err != nil {
|
|
log.Printf("[secret] error %s", err.Error())
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|