From fa768ccfde37895b700238d0e4ae348ed41779d2 Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Wed, 9 Mar 2016 16:25:41 -0500 Subject: [PATCH] Create the credential store registration mechanism and wire it in --- cmd/hmacproxy/main.go | 24 +++++++------ config/config.go | 3 +- credential/credential.go | 22 ++++++++++++ credential/singlekey/singlekey.go | 52 +++++++++++++++++++++++++++ credential/store.go | 59 ++++++++++++++++++++++++++++++ credentials.go | 60 ------------------------------- handlers.go | 6 ++-- hmac_v4.go | 11 +++--- 8 files changed, 159 insertions(+), 78 deletions(-) create mode 100644 credential/credential.go create mode 100644 credential/singlekey/singlekey.go create mode 100644 credential/store.go delete mode 100644 credentials.go diff --git a/cmd/hmacproxy/main.go b/cmd/hmacproxy/main.go index ab0147e..aac38dc 100644 --- a/cmd/hmacproxy/main.go +++ b/cmd/hmacproxy/main.go @@ -16,12 +16,16 @@ package main import ( "flag" - log "github.com/Sirupsen/logrus" - "github.com/coreos-inc/hmacproxy" - "github.com/coreos-inc/hmacproxy/config" "net/http/httptest" "net/url" "os" + + log "github.com/Sirupsen/logrus" + + "github.com/coreos-inc/hmacproxy" + "github.com/coreos-inc/hmacproxy/config" + "github.com/coreos-inc/hmacproxy/credential" + _ "github.com/coreos-inc/hmacproxy/credential/singlekey" ) func main() { @@ -47,7 +51,7 @@ func main() { if proxyConfig.Signer != nil { log.Infof("Starting signing proxy on: %s", proxyConfig.Signer.ListenerAddr) - signingCredential := hmacproxy.SingleAccessKey{ + signingCredential := credential.Credential{ proxyConfig.Signer.Key.ID, proxyConfig.Signer.Key.Secret, proxyConfig.Signer.Key.Service, @@ -73,13 +77,13 @@ func main() { proxyConfig.Verifier.ListenerAddr, proxyConfig.Verifier.Upstream, ) - tmpCred := hmacproxy.SingleAccessKey{ - proxyConfig.Signer.Key.ID, - proxyConfig.Signer.Key.Secret, - proxyConfig.Signer.Key.Service, - proxyConfig.Signer.Key.Region, + + cs, err := credential.CreateCredentialStore(proxyConfig.Verifier.CredentialSource) + if err != nil { + log.Fatal(err) } - verificationProxy, err := hmacproxy.CreateVerifyingProxy(proxyConfig.Verifier.Upstream.URL, tmpCred) + + verificationProxy, err := hmacproxy.CreateVerifyingProxy(proxyConfig.Verifier.Upstream.URL, cs) if err != nil { log.Fatal(err) } diff --git a/config/config.go b/config/config.go index 9bb4090..10d1d26 100644 --- a/config/config.go +++ b/config/config.go @@ -16,11 +16,12 @@ package config import ( "fmt" - "gopkg.in/yaml.v2" "io/ioutil" "net/url" "os" "time" + + "gopkg.in/yaml.v2" ) // URL is a custom URL type that allows validation at configuration load time. diff --git a/credential/credential.go b/credential/credential.go new file mode 100644 index 0000000..477e101 --- /dev/null +++ b/credential/credential.go @@ -0,0 +1,22 @@ +// Copyright 2016 CoreOS, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package credential + +type Credential struct { + ID string + Secret string + Service string + Region string +} diff --git a/credential/singlekey/singlekey.go b/credential/singlekey/singlekey.go new file mode 100644 index 0000000..83d851d --- /dev/null +++ b/credential/singlekey/singlekey.go @@ -0,0 +1,52 @@ +// Copyright 2016 CoreOS, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package singlekey + +import ( + "fmt" + + "gopkg.in/yaml.v2" + + "github.com/coreos-inc/hmacproxy/config" + "github.com/coreos-inc/hmacproxy/credential" +) + +type SingleAccessKey struct { + credential.Credential +} + +func (s SingleAccessKey) LoadCredential(keyID, serviceName, regionName string) (*credential.Credential, error) { + if keyID != s.ID || serviceName != s.Service || regionName != s.Region { + return nil, fmt.Errorf("Unknown key with key id: %s", keyID) + } + return &s.Credential, nil +} + +func constructor(cfg *config.CredentialSourceConfig) (credential.CredentialStore, error) { + reserialized, err := yaml.Marshal(cfg.Options) + if err != nil { + return nil, fmt.Errorf("unable to marshall configuration: %v", cfg.Options) + } + var parsed SingleAccessKey + err = yaml.Unmarshal(reserialized, &parsed) + if err != nil { + return nil, fmt.Errorf("unable to parse configuration: %v", reserialized) + } + return parsed, nil +} + +func init() { + credential.RegisterCredentialStoreFacory("SingleCredential", constructor) +} diff --git a/credential/store.go b/credential/store.go new file mode 100644 index 0000000..429643d --- /dev/null +++ b/credential/store.go @@ -0,0 +1,59 @@ +// Copyright 2016 CoreOS, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package credential + +import ( + "fmt" + + "github.com/coreos-inc/hmacproxy/config" +) + +type CredentialStoreConstructor func(*config.CredentialSourceConfig) (CredentialStore, error) + +var storeFactories = make(map[string]CredentialStoreConstructor) + +// RegisterNotifier makes a Fetcher available by the provided name. +// If Register is called twice with the same name or if driver is nil, +// it panics. +func RegisterCredentialStoreFacory(name string, csf func(*config.CredentialSourceConfig) (CredentialStore, error)) { + if name == "" { + panic("credentials: could not register a CredentialStore with an empty name") + } + + if csf == nil { + panic("credentials: could not register a nil CredentialStore") + } + + if _, dup := storeFactories[name]; dup { + panic("credentials: RegisterCredentialStore called twice for " + name) + } + + storeFactories[name] = csf +} + +func CreateCredentialStore(cfg *config.CredentialSourceConfig) (cs CredentialStore, err error) { + constructor, found := storeFactories[cfg.Type] + if !found { + err = fmt.Errorf("credentials: Unable to find credential store constructor for %s", cfg.Type) + return + } + + cs, err = constructor(cfg) + return +} + +type CredentialStore interface { + LoadCredential(keyID, serviceName, regionName string) (*Credential, error) +} diff --git a/credentials.go b/credentials.go deleted file mode 100644 index c27228e..0000000 --- a/credentials.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2016 CoreOS, Inc -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hmacproxy - -import ( - "fmt" -) - -type Credential interface { - ID() string - Secret() string - Service() string - Region() string -} - -type CredentialStore interface { - LoadCredential(keyID, serviceName, regionName string) (Credential, error) -} - -type SingleAccessKey struct { - KeyID string - KeySecret string - KeyService string - KeyRegion string -} - -func (s SingleAccessKey) ID() string { - return s.KeyID -} - -func (s SingleAccessKey) Secret() string { - return s.KeySecret -} - -func (s SingleAccessKey) Service() string { - return s.KeyService -} - -func (s SingleAccessKey) Region() string { - return s.KeyRegion -} - -func (s SingleAccessKey) LoadCredential(keyID, serviceName, regionName string) (Credential, error) { - if keyID != s.ID() || serviceName != s.Service() || regionName != s.Region() { - return nil, fmt.Errorf("Unknown key with key id: %s", keyID) - } - return s, nil -} diff --git a/handlers.go b/handlers.go index 7755f40..4b367d8 100644 --- a/handlers.go +++ b/handlers.go @@ -19,9 +19,11 @@ import ( "net/http" "net/http/httputil" "net/url" + + "github.com/coreos-inc/hmacproxy/credential" ) -func CreateSigningProxy(target *url.URL, credential Credential) (*httputil.ReverseProxy, error) { +func CreateSigningProxy(target *url.URL, cred credential.Credential) (*httputil.ReverseProxy, error) { director := func(req *http.Request) { log.Printf("Proxying request %v", req) req.URL.Scheme = target.Scheme @@ -30,7 +32,7 @@ func CreateSigningProxy(target *url.URL, credential Credential) (*httputil.Rever return &httputil.ReverseProxy{Director: director}, nil } -func CreateVerifyingProxy(target *url.URL, credStore CredentialStore) (*httputil.ReverseProxy, error) { +func CreateVerifyingProxy(target *url.URL, cs credential.CredentialStore) (*httputil.ReverseProxy, error) { director := func(req *http.Request) { log.Printf("Proxying request %v", req) req.URL.Scheme = target.Scheme diff --git a/hmac_v4.go b/hmac_v4.go index 3c587e6..7c4149f 100644 --- a/hmac_v4.go +++ b/hmac_v4.go @@ -29,6 +29,8 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/private/signer/v4" + + "github.com/coreos-inc/hmacproxy/credential" ) // timeFormat is the format used in the X-Amz-Date header with aws-sdk-go. @@ -63,7 +65,7 @@ var ( // Sign4 signs the given http.Request using AWS-Style HMAC v4 // with the specified Credential, region and service names. -func Sign4(req *http.Request, cred Credential, regionName, serviceName string) error { +func Sign4(req *http.Request, cred credential.Credential) error { // If the request that we need to sign has a Body, we must read it entirely and convert it into a // ReadSeeker in order to sign the request. if req.Body != nil { @@ -76,8 +78,7 @@ func Sign4(req *http.Request, cred Credential, regionName, serviceName string) e } // Sign the given http.Request. - return sign(req, cred.KeyID(), cred.KeySecret(), regionName, serviceName, - time.Now().UTC()) + return sign(req, cred.ID, cred.Secret, cred.Region, cred.Service, time.Now().UTC()) } // Verify4 verifies the AWS-Style HMAC v4 signature present in the given http.Request. @@ -85,7 +86,7 @@ func Sign4(req *http.Request, cred Credential, regionName, serviceName string) e // service names. The maxSkew duration represents the time window within a signed request stays // valid. Verify4 returns true if the http.Request has been verified successfully, otherwise // the returned error contains the failure reason. -func Verify4(req *http.Request, creds CredentialStore, maxSkew time.Duration) (bool, error) { +func Verify4(req *http.Request, creds credential.CredentialStore, maxSkew time.Duration) (bool, error) { // Shallow copy the request as we're going to modify its headers, // and make its Body a ReadSeekerCloser as AWS going to read it and http.Request must be able to // Close() it. @@ -124,7 +125,7 @@ func Verify4(req *http.Request, creds CredentialStore, maxSkew time.Duration) (b } // Sign our copy of the given http.Request. - err = sign(reqCopy, keyID, cred.KeySecret(), regionName, serviceName, signatureTime) + err = sign(reqCopy, keyID, cred.Secret, regionName, serviceName, signatureTime) if err != nil { return false, err }