forked from openfaas/nats-queue-worker
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolves openfaas#48 Signed-off-by: Edward Wilde <[email protected]>
- Loading branch information
Showing
71 changed files
with
7,296 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package http | ||
|
||
import ( | ||
"bytes" | ||
"crypto" | ||
"crypto/rsa" | ||
"crypto/sha256" | ||
"crypto/x509" | ||
"encoding/pem" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"os" | ||
"time" | ||
|
||
"github.com/go-fed/httpsig" | ||
) | ||
|
||
|
||
var privateKey *rsa.PrivateKey | ||
func init() { | ||
var err error | ||
var signingKey []byte | ||
|
||
if _, err := os.Stat("/run/secrets/http-signing-private-key"); os.IsNotExist(err) { | ||
log.Println("Warning callback messages will not be signed missing private key: /run/secrets/http-signing-private-key") | ||
return // backwards compatibility, if no signing key given we won't sign messages | ||
} | ||
|
||
signingKey, err = ioutil.ReadFile("/run/secrets/http-signing-private-key") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
privateKey, err = loadPrivateKey(signingKey) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func SignMessage(request *http.Request) error { | ||
if privateKey == nil { | ||
return nil // backwards compatibility, if no signing key given we won't sign messages | ||
} | ||
|
||
return signMessageWithKey(privateKey, request) | ||
} | ||
|
||
func signMessageWithKey(privateKey crypto.PrivateKey, request *http.Request) error { | ||
if _, ok := request.Header["Date"]; !ok { | ||
request.Header["Date"] = []string{time.Now().Format(http.TimeFormat)} | ||
} | ||
|
||
if _, ok := request.Header["Digest"]; !ok { | ||
body, err := ioutil.ReadAll(request.Body) | ||
if err != nil { | ||
return fmt.Errorf("error reading body. %v", err) | ||
} | ||
|
||
// And now set a new body, which will simulate the same data we read: | ||
request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) | ||
|
||
request.Header["Digest"] = []string{fmt.Sprintf("%x", sha256.Sum256(body))} | ||
} | ||
|
||
|
||
// The "Date" and "Digest" headers must already be set on r, as well as r.URL. | ||
headersToSign := []string{httpsig.RequestTarget, "date", "content-type", "digest", "content-length"} | ||
preferences := []httpsig.Algorithm{httpsig.RSA_SHA256} | ||
|
||
signer, _, err := httpsig.NewSigner(preferences, headersToSign, httpsig.Signature) | ||
if err != nil { | ||
return fmt.Errorf("error creating request signer. %v", err) | ||
} | ||
|
||
if err:= signer.SignRequest(privateKey, "callback", request); err != nil { | ||
return fmt.Errorf("error siging request. %v", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func loadPrivateKey(keyData []byte) (*rsa.PrivateKey, error) { | ||
pem, _ := pem.Decode(keyData) | ||
if pem.Type != "RSA PRIVATE KEY" { | ||
return nil, fmt.Errorf("RSA private key is of the wrong type: %s", pem.Type) | ||
} | ||
|
||
return x509.ParsePKCS1PrivateKey(pem.Bytes) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package http | ||
|
||
import ( | ||
"bytes" | ||
"crypto/rsa" | ||
"fmt" | ||
"net/http" | ||
"testing" | ||
) | ||
|
||
var( | ||
testRSAKey *rsa.PrivateKey | ||
) | ||
|
||
func init() { | ||
var err error | ||
testRSAKey , err = loadPrivateKey([]byte(testPrivateKey)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func TestSignMessage(t *testing.T) { | ||
type args struct { | ||
request *http.Request | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
wantErr bool | ||
eval func(r *http.Request) error | ||
}{ | ||
{ | ||
name: "request is missing date and digest header", | ||
args: struct{ request *http.Request | ||
}{ | ||
request: createRequest("POST", "http://callback.com", `{ "name": "foo"}`), | ||
}, | ||
wantErr: false, | ||
eval: func(r *http.Request) error { | ||
signature := r.Header["Signature"] | ||
digest := r.Header["Digest"] | ||
if len (signature) == 0 { | ||
return fmt.Errorf("signature not present") | ||
} | ||
|
||
if len (digest) == 0 { | ||
return fmt.Errorf("digest not present") | ||
} | ||
|
||
return nil | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if err := signMessageWithKey(testRSAKey, tt.args.request); (err != nil) != tt.wantErr { | ||
t.Errorf("SignMessage() error = %v, wantErr %v", err, tt.wantErr) | ||
} | ||
|
||
if err:= tt.eval(tt.args.request); err != nil { | ||
t.Errorf("error validating signature. %v", err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestLoadPrivateKey(t *testing.T) { | ||
key, err := loadPrivateKey([]byte(testPrivateKey)) | ||
if err != nil { | ||
t.Errorf("error loading private key from PEM. %v", err) | ||
} | ||
|
||
publicKey := key.Public() | ||
if publicKey == nil { | ||
t.Errorf("error creating public key from private") | ||
} | ||
} | ||
|
||
func createRequest(method string, url string, body string) *http.Request { | ||
r, _ := http.NewRequest(method, url, bytes.NewBuffer([]byte(body))) | ||
return r | ||
} | ||
|
||
const testPrivateKey = `-----BEGIN RSA PRIVATE KEY----- | ||
MIIEowIBAAKCAQEA7pEUKQ28pI5N3g/zG6OJ100N/DV2Q8Ob+gzRjd7HjXgVgZyj | ||
S3nA8FAYrxTLSihcIhXuQrYxyk2vp6YMNmSBfOptkdmj4UgLYskfeqEt8JjS6ExB | ||
xSWEDgr1IXOPPDP61on8F65/ZYGnp2JF2wHYk0OeD4ppNUV+mIHj/wXf7VLHGflw | ||
FQH/+mfUn+tVQRgX7hTadcYmGJ+1XP0py4kUgJDHfw8eBsFurHWr2mXu3BdraSKK | ||
f1G9i+SifmOUUul6mBONmlvzQdKtDCr48o1HQndRHcMWjKhlBhKz4qrmqku8oGBh | ||
6iHhGVVYf8D3mU1nzyjH4rOUXZwzj+SaqgGkvQIDAQABAoIBAA/HuuyoQvUjkJUC | ||
uxL181UxfJ5VLvh2hOe6V1YqCUsbSVjkcoy4hgGfWrKiDnxeRRHgH318LbjzAI53 | ||
VlF2vnXzz8GZAtUQ/efP4+wpoy9J/JFvEd6nh5+iK+rFiRDzY0EzqWAro7OkKuHQ | ||
h0xgR+Id0+O1RRZH/YK6/MjOq6w378/sXeKGh5AYRLP1wYdH7zLtDa/jBzI6VX3U | ||
/TO68hOdAGkNOzub/Bh8DFSI/n9s2Qazitw3LkT9uPH6lLVizuLpUkKqyWtJSsk4 | ||
cs32Nvc/7Vlp3VwvJMM2tHt3Wp8kRsiYAU7gmFMSQOjx1D6Ivy4i/pXUoL65Cb8f | ||
ZXJM9GUCgYEA+AkKmsDMWtxm+GGoE1vEAwBoP7ZjVw8uxurrn7qoCRkK2ob4c0mc | ||
Yy47IJQRKPgVqh+Mz77PH3j8wDPUqwF+iXpCZ8Ywqpa8+gVFsGaSdq9ccoLRAvhL | ||
Dv8baJZTvOZ3HpvLrNlUwNaWKVPVfJcIyhcGGUzIPWr/DkgeEaJbDisCgYEA9joz | ||
hlIeQRn/ZVqFyFwtX3aMB4drXd+3see8NDe6YB06c/bAXzfJllxh03ataVP0Bx27 | ||
qw7QTd462985xhCLs2vf15Uny2j2CsBP3/AENWlrRBltVgM9GHi8YECZLjlLnavV | ||
u1US22xYjJVn9KOaywVPG27QFOeGQ5DRMMMBjLcCgYB2G6AQFrR4o7Don1/wb+cD | ||
YuNBS3E8WH92uxCeC7zOMD2J13FhPHEajT4cgkU1lASE8OcVWY+5NdxtDYE8OMbv | ||
YedTEP3FjWfJSk9n8z/VAiXsZTxvxJnBN1ruz35qWffo/FjdItQHy2bPoRrsa+ME | ||
kDgYPgavsE5pl4+x5/Sh5wKBgQC6KMS94CYpmEtEyMzu7+oKC+nQ42yke7k5k3fy | ||
jtOlSVPhSwSkuNv7h3sa0tZHFQchvjQeH0QUK1ma1MmsWuQUUhHZv1Zn7sJe3IlH | ||
SQMVks9bnFHSvv4ZG2do6k4l6YGnArzENozcQq0sFOWUy4EDz87AceZ2d5lGSh4u | ||
3LC2PwKBgBMHoYUY2SY+CWSaL1oRKi6t1ZF6phNntvWZF/NHivNmdxFUOB7XIG3L | ||
lHUCArXs2qRSJ/EPCB/GtQEQPmrij0YIarunD9a5/i5Qs5XaQvt5mwLxN9sIH1Cp | ||
Y4xKvUQfpXtlFF6qYrK6Ig1gQQA8jfW135ibnmFdy22fhhKrGX0R | ||
-----END RSA PRIVATE 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-----` |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.