Skip to content

Commit

Permalink
Adds key creation for http signatures
Browse files Browse the repository at this point in the history
Resolves openfaas#1103

Signed-off-by: Edward Wilde <[email protected]>
  • Loading branch information
ewilde committed Dec 10, 2019
1 parent 3422bdc commit ae6b495
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 1 deletion.
14 changes: 14 additions & 0 deletions deploy_stack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ else
echo ""
fi

# Setup http signatures keys
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 "Http encryption settings enabled..\n"

arch=$(uname -m)
case "$arch" in

Expand Down
6 changes: 6 additions & 0 deletions docker-compose.arm64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ services:
secrets:
- basic-auth-user
- basic-auth-password
- http-signing-public-key

# auth service provide basic-auth plugin for system APIs
basic-auth-plugin:
Expand Down Expand Up @@ -150,6 +151,7 @@ services:
secrets:
- basic-auth-user
- basic-auth-password
- http-signing-private-key

# End services

Expand Down Expand Up @@ -232,3 +234,7 @@ secrets:
external: true
basic-auth-password:
external: true
http-signing-public-key:
external: true
http-signing-private-key:
external: true
6 changes: 6 additions & 0 deletions docker-compose.armhf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ services:
secrets:
- basic-auth-user
- basic-auth-password
- http-signing-public-key

# Docker Swarm provider
faas-swarm:
Expand Down Expand Up @@ -149,6 +150,7 @@ services:
secrets:
- basic-auth-user
- basic-auth-password
- http-signing-private-key

# End services

Expand Down Expand Up @@ -231,3 +233,7 @@ secrets:
external: true
basic-auth-password:
external: true
http-signing-public-key:
external: true
http-signing-private-key:
external: true
6 changes: 6 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ services:
secrets:
- basic-auth-user
- basic-auth-password
- http-signing-public-key

# auth service provide basic-auth plugin for system APIs
basic-auth-plugin:
Expand Down Expand Up @@ -149,6 +150,7 @@ services:
secrets:
- basic-auth-user
- basic-auth-password
- http-signing-private-key

# End services

Expand Down Expand Up @@ -230,3 +232,7 @@ secrets:
external: true
basic-auth-password:
external: true
http-signing-public-key:
external: true
http-signing-private-key:
external: true
89 changes: 89 additions & 0 deletions gateway/handlers/certificates_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package handlers

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path"

"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"`
}

type SecretsReader interface {
Read(key string) (string, error)
}

type FileSecretsReader struct {
SecretMountPath string
}

func (f *FileSecretsReader) Read(key string) (string, error) {
if len(f.SecretMountPath) == 0 {
return "", fmt.Errorf("invalid SecretMountPath specified for reading secrets used for certificates")
}

certificatePath := path.Join(f.SecretMountPath, key)
if _, err := os.Stat(certificatePath); os.IsNotExist(err) {
return "", fmt.Errorf("unable to find secret %s", key)
}

value, err := ioutil.ReadFile(certificatePath)
if err != nil {
return "", fmt.Errorf("error reading find secret %s", certificatePath)
}

return string(value), nil
}

func MakeCertificatesHandler(reader SecretsReader) 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
}

publicKey, err := reader.Read(secretKey)
if err != nil {
w.WriteHeader(http.StatusNotFound)
message := fmt.Sprintf("Unable to find secret for key %s.", keyID)
w.Write([]byte(message))
log.Println(message)
return
}

key := &KeyType{
Id: secretKey,
PEM: publicKey,
}

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)
}
}
98 changes: 98 additions & 0 deletions gateway/handlers/certificates_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package handlers

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"

"github.com/gorilla/mux"
)

func TestMakeCertificatesHandler(t *testing.T) {
tests := []struct {
name string
keyID string
eval func(r *http.Response) error
reader SecretsReader
}{
{
name: "Get certificate",
keyID: "callback",
eval: func(r *http.Response) error {
if r.StatusCode != 200 {
return fmt.Errorf("expected 200")
}

body, _ := ioutil.ReadAll(r.Body)
keyData := &KeyType{}
if err := json.Unmarshal(body, keyData); err != nil {
return fmt.Errorf("error unmarshalling result")
}

if keyData.PEM != testPublicKey {
return fmt.Errorf("PEM want %s got %s", testPublicKey, keyData.PEM)
}
return nil
},
reader: &TestSecretsReader{readCallBack: func(key string) (s string, e error) {
return testPublicKey, nil
}},
},
{

name: "Unable to find certificate",
keyID: "missingID",
eval: func(r *http.Response) error {
if r.StatusCode != 404 {
return fmt.Errorf("expected 404")
}

return nil
},
reader: &TestSecretsReader{readCallBack: func(key string) (s string, e error) {
return "", fmt.Errorf("key not found")
}},
},
}

r := mux.NewRouter()
ts := httptest.NewServer(r)
defer ts.Close()

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r.HandleFunc("/certificates/{id}", MakeCertificatesHandler(tt.reader))

url := ts.URL + "/certificates/" + tt.keyID
resp, err := http.Get(url)
if err != nil {
t.Errorf("MakeCertificatesHandler() call = %v", err)
}

if err := tt.eval(resp); err != nil {
t.Errorf("MakeCertificatesHandler() eval = %v", err)
}
})
}
}

type TestSecretsReader struct {
readCallBack func(key string) (string, error)
}

func (r *TestSecretsReader) Read(key string) (string, error) {
return r.readCallBack(key)
}

const testPublicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7pEUKQ28pI5N3g/zG6OJ
100N/DV2Q8Ob+gzRjd7HjXgVgZyjS3nA8FAYrxTLSihcIhXuQrYxyk2vp6YMNmSB
fOptkdmj4UgLYskfeqEt8JjS6ExBxSWEDgr1IXOPPDP61on8F65/ZYGnp2JF2wHY
k0OeD4ppNUV+mIHj/wXf7VLHGflwFQH/+mfUn+tVQRgX7hTadcYmGJ+1XP0py4kU
gJDHfw8eBsFurHWr2mXu3BdraSKKf1G9i+SifmOUUul6mBONmlvzQdKtDCr48o1H
QndRHcMWjKhlBhKz4qrmqku8oGBh6iHhGVVYf8D3mU1nzyjH4rOUXZwzj+SaqgGk
vQIDAQAB
-----END PUBLIC KEY-----`
2 changes: 1 addition & 1 deletion gateway/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func main() {

r.HandleFunc("/healthz",
handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, nilURLTransformer, serviceAuthInjector)).Methods(http.MethodGet)

r.Handle("/certificates/{id}", handlers.MakeCertificatesHandler(&handlers.FileSecretsReader{SecretMountPath: config.SecretMountPath}))
r.Handle("/", http.RedirectHandler("/ui/", http.StatusMovedPermanently)).Methods(http.MethodGet)

tcpPort := 8080
Expand Down

0 comments on commit ae6b495

Please sign in to comment.