From d68347ff4d03d1c9348ef8200ae2f710b0f181b9 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Wed, 15 Jul 2020 13:01:22 +0300 Subject: [PATCH 01/17] Add secure enclave --- neofs/layer/auth.go | 149 ++++++++++++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 52 deletions(-) diff --git a/neofs/layer/auth.go b/neofs/layer/auth.go index 69702900..d6e32fed 100644 --- a/neofs/layer/auth.go +++ b/neofs/layer/auth.go @@ -1,6 +1,7 @@ package layer import ( + "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/sha256" @@ -11,39 +12,101 @@ import ( ) const ( - GatewayKeySize = 2048 + gatewayEncryptionKeySize = 2048 ) -type keyPair struct { - PrivateKey *rsa.PrivateKey - PublicKey *rsa.PublicKey +type ( + signatureKeyName byte + encryptionKeyName byte +) + +const ( + _ signatureKeyName = iota + // Indicates that the key is a NeoFS ECDSA key. + gateNeoFSECDSAKey + gateNeoFSEd25519Key +) + +const ( + _ encryptionKeyName = iota + // Indicates that the key is used to encrypt + // a bearer token to pass auth procedure. + gateUserAuthKey +) + +type ( + signatureKeyPair struct { + PrivateKey *ecdsa.PrivateKey + PublicKey *ecdsa.PublicKey + } + + encryptionKeyPair struct { + PrivateKey *rsa.PrivateKey + PublicKey *rsa.PublicKey + } +) + +type secureEnclave struct { + signatureKeys map[signatureKeyName]signatureKeyPair + encryptionKeys map[encryptionKeyName]encryptionKeyPair } -type AuthCenter struct { - gatewayKeys keyPair +func newSecureEnclave() (*secureEnclave, error) { + // TODO: Get private keys. + // TODO: Fetch NeoFS and Auth private keys from app settings. + return &secureEnclave{ + signatureKeys: map[signatureKeyName]signatureKeyPair{}, + encryptionKeys: map[encryptionKeyName]encryptionKeyPair{}, + }, nil } -func NewAuthCenter() (*AuthCenter, error) { - var ( - err error - privateKey *rsa.PrivateKey - ) - privateKey, err = pullGatewayPrivateKey() +func (se *secureEnclave) Encrypt(keyName encryptionKeyName, data []byte) ([]byte, error) { + return rsa.EncryptOAEP(sha256.New(), rand.Reader, se.encryptionKeys[keyName].PublicKey, data, []byte{}) +} + +func (se *secureEnclave) Decrypt(keyName encryptionKeyName, data []byte) ([]byte, error) { + return rsa.DecryptOAEP(sha256.New(), rand.Reader, se.encryptionKeys[keyName].PrivateKey, data, []byte{}) +} + +var globalEnclave *secureEnclave + +func init() { + var err error + globalEnclave, err = newSecureEnclave() if err != nil { - return nil, errors.Wrap(err, "failed to pull gateway private key from trusted enclave") + panic("failed to initialize secure enclave") } - if privateKey == nil { - if privateKey, err = rsa.GenerateKey(rand.Reader, GatewayKeySize); err != nil { - return nil, errors.Wrap(err, "failed to generate gateway private key") - } - if err = pushGatewayPrivateKey(privateKey); err != nil { - return nil, errors.Wrap(err, "failed to push gateway private key to trusted enclave") - } - } - ac := &AuthCenter{gatewayKeys: keyPair{ - PrivateKey: privateKey, - PublicKey: &privateKey.PublicKey, - }} +} + +// AuthCenter is a central app's authentication/authorization management unit. +type AuthCenter struct { + zstdEncoder *zstd.Encoder + zstdDecoder *zstd.Decoder +} + +// NewAuthCenter creates an instance of AuthCenter. +func NewAuthCenter() (*AuthCenter, error) { + // var ( + // err error + // privateKey *rsa.PrivateKey + // ) + // secureEnclave := &SecureEnclave{} + // privateKey, err = secureEnclave.PullGatewayEncryptionPrivateKey() + // if err != nil { + // return nil, errors.Wrap(err, "failed to pull gateway private key from trusted enclave") + // } + // if privateKey == nil { + // // TODO: Move this logic to the enclave. + // if privateKey, err = rsa.GenerateKey(rand.Reader, gatewayEncryptionKeySize); err != nil { + // return nil, errors.Wrap(err, "failed to generate gateway private key") + // } + // // if err = keysEnclave.PushGatewayEncryptionPrivateKey(privateKey); err != nil { + // // return nil, errors.Wrap(err, "failed to push gateway private key to trusted enclave") + // // } + // } + zstdEncoder, _ := zstd.NewWriter(nil) + zstdDecoder, _ := zstd.NewReader(nil) + ac := &AuthCenter{zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder} return ac, nil } @@ -52,7 +115,7 @@ func (ac *AuthCenter) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]by if err != nil { return nil, errors.Wrap(err, "failed to marshal bearer token") } - encryptedKeyID, err := ac.encrypt(compress(data)) + encryptedKeyID, err := globalEnclave.Encrypt(gateUserAuthKey, ac.compress(data)) if err != nil { return nil, errors.Wrap(err, "") } @@ -61,11 +124,11 @@ func (ac *AuthCenter) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]by func (ac *AuthCenter) UnpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { compressedKeyID := packedBearerToken[32:] - encryptedKeyID, err := decompress(compressedKeyID) + encryptedKeyID, err := ac.decompress(compressedKeyID) if err != nil { return nil, errors.Wrap(err, "failed to decompress key ID") } - keyID, err := ac.decrypt(encryptedKeyID) + keyID, err := globalEnclave.Decrypt(gateUserAuthKey, encryptedKeyID) if err != nil { return nil, errors.Wrap(err, "failed to decrypt key ID") } @@ -76,35 +139,17 @@ func (ac *AuthCenter) UnpackBearerToken(packedBearerToken []byte) (*service.Bear return bearerToken, nil } -func pullGatewayPrivateKey() (*rsa.PrivateKey, error) { - // TODO: Pull the private key from a persistent and trusted enclave. - return nil, nil -} - -func pushGatewayPrivateKey(key *rsa.PrivateKey) error { - // TODO: Push the private key to a persistent and trusted enclave. - return nil -} - -func (ac *AuthCenter) encrypt(data []byte) ([]byte, error) { - return rsa.EncryptOAEP(sha256.New(), rand.Reader, ac.gatewayKeys.PublicKey, data, []byte{}) -} - -func (ac *AuthCenter) decrypt(data []byte) ([]byte, error) { - return rsa.DecryptOAEP(sha256.New(), rand.Reader, ac.gatewayKeys.PrivateKey, data, []byte{}) -} - -func compress(data []byte) []byte { +func (ac *AuthCenter) compress(data []byte) []byte { + ac.zstdEncoder.Reset(nil) var compressedData []byte - zstdEncoder, _ := zstd.NewWriter(nil) - zstdEncoder.EncodeAll(data, compressedData) + ac.zstdEncoder.EncodeAll(data, compressedData) return compressedData } -func decompress(data []byte) ([]byte, error) { +func (ac *AuthCenter) decompress(data []byte) ([]byte, error) { + ac.zstdDecoder.Reset(nil) var decompressedData []byte - zstdDecoder, _ := zstd.NewReader(nil) - if _, err := zstdDecoder.DecodeAll(data, decompressedData); err != nil { + if _, err := ac.zstdDecoder.DecodeAll(data, decompressedData); err != nil { return nil, err } return decompressedData, nil From e670e6822686acf9e0629112ddad43107454bd99 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Wed, 15 Jul 2020 13:28:43 +0300 Subject: [PATCH 02/17] Set legacy gateway name to the actual gate's name --- legacy/neofs-router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/legacy/neofs-router.go b/legacy/neofs-router.go index 89d28958..5be69730 100644 --- a/legacy/neofs-router.go +++ b/legacy/neofs-router.go @@ -10,7 +10,7 @@ func AttachS3API(r *mux.Router, obj ObjectLayer, l *zap.Logger) { // Initialize all help initHelp() - globalGatewayName = "NeoFS GW" + globalGatewayName = "NeoFS S3 Gate" // Set when gateway is enabled globalIsGateway = true From aa28121466afed97a4e52a575817f73d1205ddd1 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Wed, 15 Jul 2020 16:48:25 +0300 Subject: [PATCH 03/17] Introduce a auth center + secure enclave in a separate package --- auth/center.go | 80 ++++++++++++++++++++++ auth/enclave.go | 159 +++++++++++++++++++++++++++++++++++++++++++ neofs/layer/auth.go | 162 -------------------------------------------- 3 files changed, 239 insertions(+), 162 deletions(-) create mode 100644 auth/center.go create mode 100644 auth/enclave.go delete mode 100644 neofs/layer/auth.go diff --git a/auth/center.go b/auth/center.go new file mode 100644 index 00000000..dc556ceb --- /dev/null +++ b/auth/center.go @@ -0,0 +1,80 @@ +package auth + +import ( + "crypto/sha256" + + "github.com/klauspost/compress/zstd" + "github.com/nspcc-dev/neofs-api-go/service" + "github.com/pkg/errors" +) + +var _debug = false + +func SetDebug() { + _debug = true +} + +// Center is a central app's authentication/authorization management unit. +type Center struct { + zstdEncoder *zstd.Encoder + zstdDecoder *zstd.Decoder +} + +// NewAuthCenter creates an instance of AuthCenter. +func NewCenter() (*Center, error) { + zstdEncoder, _ := zstd.NewWriter(nil) + zstdDecoder, _ := zstd.NewReader(nil) + ac := &Center{zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder} + return ac, nil +} + +func (ac *Center) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { + data, err := bearerToken.Marshal() + if err != nil { + return nil, errors.Wrap(err, "failed to marshal bearer token") + } + encryptedKeyID, err := globalEnclave.Encrypt(gateUserAuthKey, ac.compress(data)) + if err != nil { + return nil, errors.Wrap(err, "") + } + return append(sha256Hash(data), encryptedKeyID...), nil +} + +func (ac *Center) UnpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { + compressedKeyID := packedBearerToken[32:] + encryptedKeyID, err := ac.decompress(compressedKeyID) + if err != nil { + return nil, errors.Wrap(err, "failed to decompress key ID") + } + keyID, err := globalEnclave.Decrypt(gateUserAuthKey, encryptedKeyID) + if err != nil { + return nil, errors.Wrap(err, "failed to decrypt key ID") + } + bearerToken := new(service.BearerTokenMsg) + if err := bearerToken.Unmarshal(keyID); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal embedded bearer token") + } + return bearerToken, nil +} + +func (ac *Center) compress(data []byte) []byte { + ac.zstdEncoder.Reset(nil) + var compressedData []byte + ac.zstdEncoder.EncodeAll(data, compressedData) + return compressedData +} + +func (ac *Center) decompress(data []byte) ([]byte, error) { + ac.zstdDecoder.Reset(nil) + var decompressedData []byte + if _, err := ac.zstdDecoder.DecodeAll(data, decompressedData); err != nil { + return nil, err + } + return decompressedData, nil +} + +func sha256Hash(data []byte) []byte { + hash := sha256.New() + hash.Write(data) + return hash.Sum(nil) +} diff --git a/auth/enclave.go b/auth/enclave.go new file mode 100644 index 00000000..851e89c7 --- /dev/null +++ b/auth/enclave.go @@ -0,0 +1,159 @@ +package auth + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "io/ioutil" + "os" + "path/filepath" + + "github.com/pkg/errors" +) + +const ( + // PathToUserAuthPrivateKeyFile is a linux-specific predefined path + // to a persisted RSA private key for authenticating at S3 server. + PathToUserAuthPrivateKeyFile = "/etc/neofs/1.pem" + // PathToNeoFSECDSAPrivateKeyFile is a linux-specific predefined path + // to a persisted ECDSA private key for accessing NeoFS network. + PathToNeoFSECDSAPrivateKeyFile = "/etc/neofs/2.pem" +) + +const ( + gatewayEncryptionKeySize = 4096 +) + +const ( + _ encryptionKeyName = iota + // Indicates that the key is used to encrypt + // a bearer token to pass auth procedure. + gateUserAuthKey +) + +const ( + _ signatureKeyName = iota + // Indicates that the key is a NeoFS ECDSA key. + gateNeoFSECDSAKey + // Indicates that the key is a NeoFS Ed25519 key. + gateNeoFSEd25519Key +) + +type ( + signatureKeyName byte + encryptionKeyName byte +) + +type ( + signatureKeyPair struct { + PrivateKey *ecdsa.PrivateKey + PublicKey *ecdsa.PublicKey + } + + encryptionKeyPair struct { + PrivateKey *rsa.PrivateKey + PublicKey *rsa.PublicKey + } +) + +var globalEnclave *secureEnclave + +func init() { + var err error + globalEnclave, err = newSecureEnclave() + if err != nil { + panic("failed to initialize secure enclave") + } +} + +type secureEnclave struct { + signatureKeys map[signatureKeyName]signatureKeyPair + encryptionKeys map[encryptionKeyName]encryptionKeyPair +} + +func newSecureEnclave() (*secureEnclave, error) { + var ( + pathToKey1, pathToKey2 string + key1 *rsa.PrivateKey + key2 *ecdsa.PrivateKey + ) + // FIXME: Get private keys properly. + if _debug { + ep, _ := os.Executable() + base := filepath.Dir(ep) + pathToKey1, pathToKey2 = filepath.Join(base, "1.pem"), filepath.Join(base, "2.pem") + } else { + pathToKey1, pathToKey2 = PathToUserAuthPrivateKeyFile, PathToNeoFSECDSAPrivateKeyFile + } + if key1bs, err := ioutil.ReadFile(pathToKey1); err != nil { + // No file found. + if os.IsNotExist(err) { + if key1, err = rsa.GenerateKey(rand.Reader, gatewayEncryptionKeySize); err != nil { + return nil, errors.Wrap(err, "failed to generate RSA key") + } + key1bs := x509.MarshalPKCS1PrivateKey(key1) + data := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: key1bs}) + if err := ioutil.WriteFile(pathToKey1, data, 0o600); err != nil { + return nil, errors.Wrapf(err, "failed to write file %s", pathToKey1) + } + } else { + return nil, errors.Wrapf(err, "failed to open file %s", pathToKey1) + } + } else { + pemBlock, _ := pem.Decode(key1bs) + if pemBlock == nil { + return nil, errors.Errorf("failed to decode PEM data from file %s", pathToKey1) + } + key1, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", pathToKey1) + } + } + if key2bs, err := ioutil.ReadFile(pathToKey2); err != nil { + // No file found. + if os.IsNotExist(err) { + if key2, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil { + return nil, errors.Wrap(err, "failed to generate ECDSA key") + } + key2bs, err := x509.MarshalECPrivateKey(key2) + if err != nil { + return nil, errors.New("failed to marshal ECDSA private key") + } + data := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: key2bs}) + if err := ioutil.WriteFile(pathToKey2, data, 0o600); err != nil { + return nil, errors.Wrapf(err, "failed to write file %s", pathToKey2) + } + } else { + return nil, errors.Wrapf(err, "failed to open file %s", pathToKey2) + } + } else { + pemBlock, _ := pem.Decode(key2bs) + if pemBlock == nil { + return nil, errors.Errorf("failed to decode PEM data from file %s", pathToKey2) + } + key2, err = x509.ParseECPrivateKey(pemBlock.Bytes) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", pathToKey2) + } + } + return &secureEnclave{ + encryptionKeys: map[encryptionKeyName]encryptionKeyPair{ + gateUserAuthKey: {key1, &key1.PublicKey}, + }, + signatureKeys: map[signatureKeyName]signatureKeyPair{ + gateNeoFSECDSAKey: {key2, &key2.PublicKey}, + }, + }, nil +} + +func (se *secureEnclave) Encrypt(keyName encryptionKeyName, data []byte) ([]byte, error) { + return rsa.EncryptOAEP(sha256.New(), rand.Reader, se.encryptionKeys[keyName].PublicKey, data, []byte{}) +} + +func (se *secureEnclave) Decrypt(keyName encryptionKeyName, data []byte) ([]byte, error) { + return rsa.DecryptOAEP(sha256.New(), rand.Reader, se.encryptionKeys[keyName].PrivateKey, data, []byte{}) +} diff --git a/neofs/layer/auth.go b/neofs/layer/auth.go deleted file mode 100644 index d6e32fed..00000000 --- a/neofs/layer/auth.go +++ /dev/null @@ -1,162 +0,0 @@ -package layer - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - - "github.com/klauspost/compress/zstd" - "github.com/nspcc-dev/neofs-api-go/service" - "github.com/pkg/errors" -) - -const ( - gatewayEncryptionKeySize = 2048 -) - -type ( - signatureKeyName byte - encryptionKeyName byte -) - -const ( - _ signatureKeyName = iota - // Indicates that the key is a NeoFS ECDSA key. - gateNeoFSECDSAKey - gateNeoFSEd25519Key -) - -const ( - _ encryptionKeyName = iota - // Indicates that the key is used to encrypt - // a bearer token to pass auth procedure. - gateUserAuthKey -) - -type ( - signatureKeyPair struct { - PrivateKey *ecdsa.PrivateKey - PublicKey *ecdsa.PublicKey - } - - encryptionKeyPair struct { - PrivateKey *rsa.PrivateKey - PublicKey *rsa.PublicKey - } -) - -type secureEnclave struct { - signatureKeys map[signatureKeyName]signatureKeyPair - encryptionKeys map[encryptionKeyName]encryptionKeyPair -} - -func newSecureEnclave() (*secureEnclave, error) { - // TODO: Get private keys. - // TODO: Fetch NeoFS and Auth private keys from app settings. - return &secureEnclave{ - signatureKeys: map[signatureKeyName]signatureKeyPair{}, - encryptionKeys: map[encryptionKeyName]encryptionKeyPair{}, - }, nil -} - -func (se *secureEnclave) Encrypt(keyName encryptionKeyName, data []byte) ([]byte, error) { - return rsa.EncryptOAEP(sha256.New(), rand.Reader, se.encryptionKeys[keyName].PublicKey, data, []byte{}) -} - -func (se *secureEnclave) Decrypt(keyName encryptionKeyName, data []byte) ([]byte, error) { - return rsa.DecryptOAEP(sha256.New(), rand.Reader, se.encryptionKeys[keyName].PrivateKey, data, []byte{}) -} - -var globalEnclave *secureEnclave - -func init() { - var err error - globalEnclave, err = newSecureEnclave() - if err != nil { - panic("failed to initialize secure enclave") - } -} - -// AuthCenter is a central app's authentication/authorization management unit. -type AuthCenter struct { - zstdEncoder *zstd.Encoder - zstdDecoder *zstd.Decoder -} - -// NewAuthCenter creates an instance of AuthCenter. -func NewAuthCenter() (*AuthCenter, error) { - // var ( - // err error - // privateKey *rsa.PrivateKey - // ) - // secureEnclave := &SecureEnclave{} - // privateKey, err = secureEnclave.PullGatewayEncryptionPrivateKey() - // if err != nil { - // return nil, errors.Wrap(err, "failed to pull gateway private key from trusted enclave") - // } - // if privateKey == nil { - // // TODO: Move this logic to the enclave. - // if privateKey, err = rsa.GenerateKey(rand.Reader, gatewayEncryptionKeySize); err != nil { - // return nil, errors.Wrap(err, "failed to generate gateway private key") - // } - // // if err = keysEnclave.PushGatewayEncryptionPrivateKey(privateKey); err != nil { - // // return nil, errors.Wrap(err, "failed to push gateway private key to trusted enclave") - // // } - // } - zstdEncoder, _ := zstd.NewWriter(nil) - zstdDecoder, _ := zstd.NewReader(nil) - ac := &AuthCenter{zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder} - return ac, nil -} - -func (ac *AuthCenter) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { - data, err := bearerToken.Marshal() - if err != nil { - return nil, errors.Wrap(err, "failed to marshal bearer token") - } - encryptedKeyID, err := globalEnclave.Encrypt(gateUserAuthKey, ac.compress(data)) - if err != nil { - return nil, errors.Wrap(err, "") - } - return append(sha256Hash(data), encryptedKeyID...), nil -} - -func (ac *AuthCenter) UnpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { - compressedKeyID := packedBearerToken[32:] - encryptedKeyID, err := ac.decompress(compressedKeyID) - if err != nil { - return nil, errors.Wrap(err, "failed to decompress key ID") - } - keyID, err := globalEnclave.Decrypt(gateUserAuthKey, encryptedKeyID) - if err != nil { - return nil, errors.Wrap(err, "failed to decrypt key ID") - } - bearerToken := new(service.BearerTokenMsg) - if err := bearerToken.Unmarshal(keyID); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal embedded bearer token") - } - return bearerToken, nil -} - -func (ac *AuthCenter) compress(data []byte) []byte { - ac.zstdEncoder.Reset(nil) - var compressedData []byte - ac.zstdEncoder.EncodeAll(data, compressedData) - return compressedData -} - -func (ac *AuthCenter) decompress(data []byte) ([]byte, error) { - ac.zstdDecoder.Reset(nil) - var decompressedData []byte - if _, err := ac.zstdDecoder.DecodeAll(data, decompressedData); err != nil { - return nil, err - } - return decompressedData, nil -} - -func sha256Hash(data []byte) []byte { - hash := sha256.New() - hash.Write(data) - return hash.Sum(nil) -} From 4fc7eaed34e37e7f16f9b58f284ad4372e1d85f3 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Wed, 15 Jul 2020 18:20:45 +0300 Subject: [PATCH 04/17] Remove global state --- auth/center.go | 45 ++++++++++++++------------- auth/enclave.go | 82 +++++++++++++++++++------------------------------ 2 files changed, 55 insertions(+), 72 deletions(-) diff --git a/auth/center.go b/auth/center.go index dc556ceb..966177cc 100644 --- a/auth/center.go +++ b/auth/center.go @@ -8,45 +8,48 @@ import ( "github.com/pkg/errors" ) -var _debug = false - -func SetDebug() { - _debug = true -} - // Center is a central app's authentication/authorization management unit. type Center struct { + enclave *secureEnclave zstdEncoder *zstd.Encoder zstdDecoder *zstd.Decoder } -// NewAuthCenter creates an instance of AuthCenter. -func NewCenter() (*Center, error) { +// NewCenter creates an instance of AuthCenter. +func NewCenter(pathToRSAKey, pathToECDSAKey string) (*Center, error) { zstdEncoder, _ := zstd.NewWriter(nil) zstdDecoder, _ := zstd.NewReader(nil) - ac := &Center{zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder} - return ac, nil + enclave, err := newSecureEnclave(pathToRSAKey, pathToECDSAKey) + if err != nil { + return nil, errors.Wrap(err, "failed to create secure enclave") + } + center := &Center{ + enclave: enclave, + zstdEncoder: zstdEncoder, + zstdDecoder: zstdDecoder, + } + return center, nil } -func (ac *Center) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { +func (center *Center) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { data, err := bearerToken.Marshal() if err != nil { return nil, errors.Wrap(err, "failed to marshal bearer token") } - encryptedKeyID, err := globalEnclave.Encrypt(gateUserAuthKey, ac.compress(data)) + encryptedKeyID, err := center.enclave.Encrypt(gateUserAuthKey, center.compress(data)) if err != nil { return nil, errors.Wrap(err, "") } return append(sha256Hash(data), encryptedKeyID...), nil } -func (ac *Center) UnpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { +func (center *Center) UnpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { compressedKeyID := packedBearerToken[32:] - encryptedKeyID, err := ac.decompress(compressedKeyID) + encryptedKeyID, err := center.decompress(compressedKeyID) if err != nil { return nil, errors.Wrap(err, "failed to decompress key ID") } - keyID, err := globalEnclave.Decrypt(gateUserAuthKey, encryptedKeyID) + keyID, err := center.enclave.Decrypt(gateUserAuthKey, encryptedKeyID) if err != nil { return nil, errors.Wrap(err, "failed to decrypt key ID") } @@ -57,17 +60,17 @@ func (ac *Center) UnpackBearerToken(packedBearerToken []byte) (*service.BearerTo return bearerToken, nil } -func (ac *Center) compress(data []byte) []byte { - ac.zstdEncoder.Reset(nil) +func (center *Center) compress(data []byte) []byte { + center.zstdEncoder.Reset(nil) var compressedData []byte - ac.zstdEncoder.EncodeAll(data, compressedData) + center.zstdEncoder.EncodeAll(data, compressedData) return compressedData } -func (ac *Center) decompress(data []byte) ([]byte, error) { - ac.zstdDecoder.Reset(nil) +func (center *Center) decompress(data []byte) ([]byte, error) { + center.zstdDecoder.Reset(nil) var decompressedData []byte - if _, err := ac.zstdDecoder.DecodeAll(data, decompressedData); err != nil { + if _, err := center.zstdDecoder.DecodeAll(data, decompressedData); err != nil { return nil, err } return decompressedData, nil diff --git a/auth/enclave.go b/auth/enclave.go index 851e89c7..1033ed00 100644 --- a/auth/enclave.go +++ b/auth/enclave.go @@ -10,19 +10,18 @@ import ( "encoding/pem" "io/ioutil" "os" - "path/filepath" "github.com/pkg/errors" ) -const ( - // PathToUserAuthPrivateKeyFile is a linux-specific predefined path - // to a persisted RSA private key for authenticating at S3 server. - PathToUserAuthPrivateKeyFile = "/etc/neofs/1.pem" - // PathToNeoFSECDSAPrivateKeyFile is a linux-specific predefined path - // to a persisted ECDSA private key for accessing NeoFS network. - PathToNeoFSECDSAPrivateKeyFile = "/etc/neofs/2.pem" -) +// const ( +// // PathToUserAuthPrivateKeyFile is a linux-specific predefined path +// // to a persisted RSA private key for authenticating at S3 server. +// PathToUserAuthPrivateKeyFile = "/etc/neofs/1.pem" +// // PathToNeoFSECDSAPrivateKeyFile is a linux-specific predefined path +// // to a persisted ECDSA private key for accessing NeoFS network. +// PathToNeoFSECDSAPrivateKeyFile = "/etc/neofs/2.pem" +// ) const ( gatewayEncryptionKeySize = 4096 @@ -60,92 +59,73 @@ type ( } ) -var globalEnclave *secureEnclave - -func init() { - var err error - globalEnclave, err = newSecureEnclave() - if err != nil { - panic("failed to initialize secure enclave") - } -} - type secureEnclave struct { signatureKeys map[signatureKeyName]signatureKeyPair encryptionKeys map[encryptionKeyName]encryptionKeyPair } -func newSecureEnclave() (*secureEnclave, error) { +func newSecureEnclave(pathToRSAKey, pathToECDSAKey string) (*secureEnclave, error) { var ( - pathToKey1, pathToKey2 string - key1 *rsa.PrivateKey - key2 *ecdsa.PrivateKey + rsaKey *rsa.PrivateKey + ecdsaKey *ecdsa.PrivateKey ) - // FIXME: Get private keys properly. - if _debug { - ep, _ := os.Executable() - base := filepath.Dir(ep) - pathToKey1, pathToKey2 = filepath.Join(base, "1.pem"), filepath.Join(base, "2.pem") - } else { - pathToKey1, pathToKey2 = PathToUserAuthPrivateKeyFile, PathToNeoFSECDSAPrivateKeyFile - } - if key1bs, err := ioutil.ReadFile(pathToKey1); err != nil { + if key1bs, err := ioutil.ReadFile(pathToRSAKey); err != nil { // No file found. if os.IsNotExist(err) { - if key1, err = rsa.GenerateKey(rand.Reader, gatewayEncryptionKeySize); err != nil { + if rsaKey, err = rsa.GenerateKey(rand.Reader, gatewayEncryptionKeySize); err != nil { return nil, errors.Wrap(err, "failed to generate RSA key") } - key1bs := x509.MarshalPKCS1PrivateKey(key1) + key1bs := x509.MarshalPKCS1PrivateKey(rsaKey) data := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: key1bs}) - if err := ioutil.WriteFile(pathToKey1, data, 0o600); err != nil { - return nil, errors.Wrapf(err, "failed to write file %s", pathToKey1) + if err := ioutil.WriteFile(pathToRSAKey, data, 0o600); err != nil { + return nil, errors.Wrapf(err, "failed to write file %s", pathToRSAKey) } } else { - return nil, errors.Wrapf(err, "failed to open file %s", pathToKey1) + return nil, errors.Wrapf(err, "failed to open file %s", pathToRSAKey) } } else { pemBlock, _ := pem.Decode(key1bs) if pemBlock == nil { - return nil, errors.Errorf("failed to decode PEM data from file %s", pathToKey1) + return nil, errors.Errorf("failed to decode PEM data from file %s", pathToRSAKey) } - key1, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + rsaKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes) if err != nil { - return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", pathToKey1) + return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", pathToRSAKey) } } - if key2bs, err := ioutil.ReadFile(pathToKey2); err != nil { + if key2bs, err := ioutil.ReadFile(pathToECDSAKey); err != nil { // No file found. if os.IsNotExist(err) { - if key2, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil { + if ecdsaKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil { return nil, errors.Wrap(err, "failed to generate ECDSA key") } - key2bs, err := x509.MarshalECPrivateKey(key2) + key2bs, err := x509.MarshalECPrivateKey(ecdsaKey) if err != nil { return nil, errors.New("failed to marshal ECDSA private key") } data := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: key2bs}) - if err := ioutil.WriteFile(pathToKey2, data, 0o600); err != nil { - return nil, errors.Wrapf(err, "failed to write file %s", pathToKey2) + if err := ioutil.WriteFile(pathToECDSAKey, data, 0o600); err != nil { + return nil, errors.Wrapf(err, "failed to write file %s", pathToECDSAKey) } } else { - return nil, errors.Wrapf(err, "failed to open file %s", pathToKey2) + return nil, errors.Wrapf(err, "failed to open file %s", pathToECDSAKey) } } else { pemBlock, _ := pem.Decode(key2bs) if pemBlock == nil { - return nil, errors.Errorf("failed to decode PEM data from file %s", pathToKey2) + return nil, errors.Errorf("failed to decode PEM data from file %s", pathToECDSAKey) } - key2, err = x509.ParseECPrivateKey(pemBlock.Bytes) + ecdsaKey, err = x509.ParseECPrivateKey(pemBlock.Bytes) if err != nil { - return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", pathToKey2) + return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", pathToECDSAKey) } } return &secureEnclave{ encryptionKeys: map[encryptionKeyName]encryptionKeyPair{ - gateUserAuthKey: {key1, &key1.PublicKey}, + gateUserAuthKey: {rsaKey, &rsaKey.PublicKey}, }, signatureKeys: map[signatureKeyName]signatureKeyPair{ - gateNeoFSECDSAKey: {key2, &key2.PublicKey}, + gateNeoFSECDSAKey: {ecdsaKey, &ecdsaKey.PublicKey}, }, }, nil } From a890d9142d23b59d017f2611115976bc68f3f97b Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Wed, 15 Jul 2020 18:31:05 +0300 Subject: [PATCH 05/17] Clear useless comments --- auth/enclave.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/auth/enclave.go b/auth/enclave.go index 1033ed00..16113fbf 100644 --- a/auth/enclave.go +++ b/auth/enclave.go @@ -14,15 +14,6 @@ import ( "github.com/pkg/errors" ) -// const ( -// // PathToUserAuthPrivateKeyFile is a linux-specific predefined path -// // to a persisted RSA private key for authenticating at S3 server. -// PathToUserAuthPrivateKeyFile = "/etc/neofs/1.pem" -// // PathToNeoFSECDSAPrivateKeyFile is a linux-specific predefined path -// // to a persisted ECDSA private key for accessing NeoFS network. -// PathToNeoFSECDSAPrivateKeyFile = "/etc/neofs/2.pem" -// ) - const ( gatewayEncryptionKeySize = 4096 ) From a43c596f4916ae7458da78bdd4301fbd4acf081e Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Wed, 15 Jul 2020 23:16:27 +0300 Subject: [PATCH 06/17] Remove enclove as a separate entity; move auth center to app settings --- auth/center.go | 95 ++++++++++++++++++++++++---- auth/enclave.go | 130 --------------------------------------- cmd/gate/app-settings.go | 74 ++++++++++++---------- cmd/gate/app.go | 77 ++++++++++------------- 4 files changed, 156 insertions(+), 220 deletions(-) delete mode 100644 auth/enclave.go diff --git a/auth/center.go b/auth/center.go index 966177cc..84b95c62 100644 --- a/auth/center.go +++ b/auth/center.go @@ -1,34 +1,83 @@ package auth import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" "crypto/sha256" + "crypto/x509" + "encoding/pem" + "io/ioutil" "github.com/klauspost/compress/zstd" + "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" + crypto "github.com/nspcc-dev/neofs-crypto" "github.com/pkg/errors" ) // Center is a central app's authentication/authorization management unit. type Center struct { - enclave *secureEnclave zstdEncoder *zstd.Encoder zstdDecoder *zstd.Decoder + neofsKeys struct { + PrivateKey *ecdsa.PrivateKey + PublicKey *ecdsa.PublicKey + } + ownerID refs.OwnerID + wifString string + userAuthKeys struct { + PrivateKey *rsa.PrivateKey + PublicKey *rsa.PublicKey + } } // NewCenter creates an instance of AuthCenter. -func NewCenter(pathToRSAKey, pathToECDSAKey string) (*Center, error) { +func NewCenter() *Center { zstdEncoder, _ := zstd.NewWriter(nil) zstdDecoder, _ := zstd.NewReader(nil) - enclave, err := newSecureEnclave(pathToRSAKey, pathToECDSAKey) - if err != nil { - return nil, errors.Wrap(err, "failed to create secure enclave") - } - center := &Center{ - enclave: enclave, + return &Center{ zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder, } - return center, nil +} + +func (center *Center) SetNeoFSKeys(key *ecdsa.PrivateKey) error { + publicKey := &key.PublicKey + oid, err := refs.NewOwnerID(publicKey) + if err != nil { + return errors.Wrap(err, "failed to get OwnerID") + } + center.neofsKeys.PrivateKey = key + wif, err := crypto.WIFEncode(key) + if err != nil { + return errors.Wrap(err, "failed to get WIF string from given key") + } + center.neofsKeys.PublicKey = publicKey + center.ownerID = oid + center.wifString = wif + return nil +} + +func (center *Center) GetNeoFSKeyPrivateKey() *ecdsa.PrivateKey { + return center.neofsKeys.PrivateKey +} + +func (center *Center) GetNeoFSKeyPublicKey() *ecdsa.PublicKey { + return center.neofsKeys.PublicKey +} + +func (center *Center) GetOwnerID() refs.OwnerID { + return center.ownerID +} + +func (center *Center) GetWIFString() string { + return center.wifString +} + +func (center *Center) SetUserAuthKeys(key *rsa.PrivateKey) { + center.userAuthKeys.PrivateKey = key + center.userAuthKeys.PublicKey = &key.PublicKey } func (center *Center) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { @@ -36,7 +85,7 @@ func (center *Center) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]by if err != nil { return nil, errors.Wrap(err, "failed to marshal bearer token") } - encryptedKeyID, err := center.enclave.Encrypt(gateUserAuthKey, center.compress(data)) + encryptedKeyID, err := encrypt(center.userAuthKeys.PublicKey, center.compress(data)) if err != nil { return nil, errors.Wrap(err, "") } @@ -49,7 +98,7 @@ func (center *Center) UnpackBearerToken(packedBearerToken []byte) (*service.Bear if err != nil { return nil, errors.Wrap(err, "failed to decompress key ID") } - keyID, err := center.enclave.Decrypt(gateUserAuthKey, encryptedKeyID) + keyID, err := decrypt(center.userAuthKeys.PrivateKey, encryptedKeyID) if err != nil { return nil, errors.Wrap(err, "failed to decrypt key ID") } @@ -76,8 +125,32 @@ func (center *Center) decompress(data []byte) ([]byte, error) { return decompressedData, nil } +func encrypt(key *rsa.PublicKey, data []byte) ([]byte, error) { + return rsa.EncryptOAEP(sha256.New(), rand.Reader, key, data, []byte{}) +} + +func decrypt(key *rsa.PrivateKey, data []byte) ([]byte, error) { + return rsa.DecryptOAEP(sha256.New(), rand.Reader, key, data, []byte{}) +} + func sha256Hash(data []byte) []byte { hash := sha256.New() hash.Write(data) return hash.Sum(nil) } + +func ReadRSAPrivateKeyFromPEMFile(filePath string) (*rsa.PrivateKey, error) { + kbs, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, errors.Wrapf(err, "failed to read file %s", filePath) + } + pemBlock, _ := pem.Decode(kbs) + if pemBlock == nil { + return nil, errors.Errorf("failed to decode PEM data from file %s", filePath) + } + rsaKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", filePath) + } + return rsaKey, nil +} diff --git a/auth/enclave.go b/auth/enclave.go deleted file mode 100644 index 16113fbf..00000000 --- a/auth/enclave.go +++ /dev/null @@ -1,130 +0,0 @@ -package auth - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/x509" - "encoding/pem" - "io/ioutil" - "os" - - "github.com/pkg/errors" -) - -const ( - gatewayEncryptionKeySize = 4096 -) - -const ( - _ encryptionKeyName = iota - // Indicates that the key is used to encrypt - // a bearer token to pass auth procedure. - gateUserAuthKey -) - -const ( - _ signatureKeyName = iota - // Indicates that the key is a NeoFS ECDSA key. - gateNeoFSECDSAKey - // Indicates that the key is a NeoFS Ed25519 key. - gateNeoFSEd25519Key -) - -type ( - signatureKeyName byte - encryptionKeyName byte -) - -type ( - signatureKeyPair struct { - PrivateKey *ecdsa.PrivateKey - PublicKey *ecdsa.PublicKey - } - - encryptionKeyPair struct { - PrivateKey *rsa.PrivateKey - PublicKey *rsa.PublicKey - } -) - -type secureEnclave struct { - signatureKeys map[signatureKeyName]signatureKeyPair - encryptionKeys map[encryptionKeyName]encryptionKeyPair -} - -func newSecureEnclave(pathToRSAKey, pathToECDSAKey string) (*secureEnclave, error) { - var ( - rsaKey *rsa.PrivateKey - ecdsaKey *ecdsa.PrivateKey - ) - if key1bs, err := ioutil.ReadFile(pathToRSAKey); err != nil { - // No file found. - if os.IsNotExist(err) { - if rsaKey, err = rsa.GenerateKey(rand.Reader, gatewayEncryptionKeySize); err != nil { - return nil, errors.Wrap(err, "failed to generate RSA key") - } - key1bs := x509.MarshalPKCS1PrivateKey(rsaKey) - data := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: key1bs}) - if err := ioutil.WriteFile(pathToRSAKey, data, 0o600); err != nil { - return nil, errors.Wrapf(err, "failed to write file %s", pathToRSAKey) - } - } else { - return nil, errors.Wrapf(err, "failed to open file %s", pathToRSAKey) - } - } else { - pemBlock, _ := pem.Decode(key1bs) - if pemBlock == nil { - return nil, errors.Errorf("failed to decode PEM data from file %s", pathToRSAKey) - } - rsaKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", pathToRSAKey) - } - } - if key2bs, err := ioutil.ReadFile(pathToECDSAKey); err != nil { - // No file found. - if os.IsNotExist(err) { - if ecdsaKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil { - return nil, errors.Wrap(err, "failed to generate ECDSA key") - } - key2bs, err := x509.MarshalECPrivateKey(ecdsaKey) - if err != nil { - return nil, errors.New("failed to marshal ECDSA private key") - } - data := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: key2bs}) - if err := ioutil.WriteFile(pathToECDSAKey, data, 0o600); err != nil { - return nil, errors.Wrapf(err, "failed to write file %s", pathToECDSAKey) - } - } else { - return nil, errors.Wrapf(err, "failed to open file %s", pathToECDSAKey) - } - } else { - pemBlock, _ := pem.Decode(key2bs) - if pemBlock == nil { - return nil, errors.Errorf("failed to decode PEM data from file %s", pathToECDSAKey) - } - ecdsaKey, err = x509.ParseECPrivateKey(pemBlock.Bytes) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", pathToECDSAKey) - } - } - return &secureEnclave{ - encryptionKeys: map[encryptionKeyName]encryptionKeyPair{ - gateUserAuthKey: {rsaKey, &rsaKey.PublicKey}, - }, - signatureKeys: map[signatureKeyName]signatureKeyPair{ - gateNeoFSECDSAKey: {ecdsaKey, &ecdsaKey.PublicKey}, - }, - }, nil -} - -func (se *secureEnclave) Encrypt(keyName encryptionKeyName, data []byte) ([]byte, error) { - return rsa.EncryptOAEP(sha256.New(), rand.Reader, se.encryptionKeys[keyName].PublicKey, data, []byte{}) -} - -func (se *secureEnclave) Decrypt(keyName encryptionKeyName, data []byte) ([]byte, error) { - return rsa.DecryptOAEP(sha256.New(), rand.Reader, se.encryptionKeys[keyName].PrivateKey, data, []byte{}) -} diff --git a/cmd/gate/app-settings.go b/cmd/gate/app-settings.go index da3089ff..98422939 100644 --- a/cmd/gate/app-settings.go +++ b/cmd/gate/app-settings.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/rsa" "fmt" "io" "os" @@ -11,19 +12,18 @@ import ( "strings" "time" + s3auth "github.com/minio/minio/auth" "github.com/minio/minio/neofs/pool" + "github.com/pkg/errors" "github.com/minio/minio/misc" - "github.com/nspcc-dev/neofs-api-go/refs" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/spf13/pflag" "github.com/spf13/viper" "go.uber.org/zap" ) -type empty int - const ( devNull = empty(0) generated = "generated" @@ -55,7 +55,11 @@ const ( // settings cfgKeepaliveTimeout = "keepalive.timeout" cfgKeepalivePermitWithoutStream = "keepalive.permit_without_stream" - // HTTPS/TLS: + // Keys + cfgNeoFSPrivateKey = "neofs-ecdsa-key" + cfgUserAuthPrivateKey = "userauth-rsa-key" + + // HTTPS/TLS cfgTLSKeyFile = "tls.key_file" cfgTLSCertFile = "tls.cert_file" @@ -66,8 +70,7 @@ const ( // settings cfgRebalanceTimer = "rebalance_timer" // gRPC - cfgGRPCVerbose = "verbose" - cfgGRPCPrivateKey = "key" + cfgGRPCVerbose = "verbose" // Metrics / Profiler / Web cfgEnableMetrics = "metrics" @@ -80,33 +83,37 @@ const ( // settings cfgApplicationBuildTime = "app.build_time" ) +type empty int + func (empty) Read([]byte) (int, error) { return 0, io.EOF } -func fetchKey(l *zap.Logger, v *viper.Viper) *ecdsa.PrivateKey { - switch val := v.GetString("key"); val { +func fetchAuthCenter(l *zap.Logger, v *viper.Viper) (*s3auth.Center, error) { + var ( + err error + neofsPrivateKey *ecdsa.PrivateKey + userAuthPrivateKey *rsa.PrivateKey + ) + switch nfspk := v.GetString(cfgNeoFSPrivateKey); nfspk { case generated: - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + neofsPrivateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - l.Fatal("could not generate private key", zap.Error(err)) + return nil, errors.Wrap(err, "could not generate NeoFS private key") } - - id, err := refs.NewOwnerID(&key.PublicKey) - l.Info("generate new key", - zap.Stringer("key", id), - zap.Error(err)) - - return key - default: - key, err := crypto.LoadPrivateKey(val) + neofsPrivateKey, err = crypto.LoadPrivateKey(nfspk) if err != nil { - l.Fatal("could not load private key", - zap.String("key", v.GetString("key")), - zap.Error(err)) + return nil, errors.Wrap(err, "could not load NeoFS private key") } - - return key } + uapk := v.GetString(cfgUserAuthPrivateKey) + userAuthPrivateKey, err = s3auth.ReadRSAPrivateKeyFromPEMFile(uapk) + if err != nil { + return nil, errors.Wrap(err, "could not load UserAuth private key") + } + center := s3auth.NewCenter() + center.SetUserAuthKeys(userAuthPrivateKey) + center.SetNeoFSKeys(neofsPrivateKey) + return center, nil } func fetchPeers(l *zap.Logger, v *viper.Viper) []pool.Peer { @@ -145,22 +152,23 @@ func newSettings() *viper.Viper { flags.SortFlags = false flags.Bool(cfgEnableProfiler, false, "enable pprof") - flags.Bool(cfgEnableMetrics, false, "enable prometheus") + flags.Bool(cfgEnableMetrics, false, "enable prometheus metrics") help := flags.BoolP("help", "h", false, "show help") version := flags.BoolP("version", "v", false, "show version") - flags.String(cfgGRPCPrivateKey, generated, `"`+generated+`" to generate key, path to private key file, hex string or wif`) + flags.String(cfgNeoFSPrivateKey, generated, fmt.Sprintf(`set value to hex string, WIF string, or path to NeoFS private key file (use "%s" to generate key)`, generated)) + flags.String(cfgUserAuthPrivateKey, "", "set path to file with private key to use in auth scheme") - flags.Bool(cfgGRPCVerbose, false, "debug gRPC connections") - flags.Duration(cfgRequestTimeout, defaultRequestTimeout, "gRPC request timeout") - flags.Duration(cfgConnectTimeout, defaultConnectTimeout, "gRPC connect timeout") - flags.Duration(cfgRebalanceTimer, defaultRebalanceTimer, "gRPC connection rebalance timer") + flags.Bool(cfgGRPCVerbose, false, "set debug mode of gRPC connections") + flags.Duration(cfgRequestTimeout, defaultRequestTimeout, "set gRPC request timeout") + flags.Duration(cfgConnectTimeout, defaultConnectTimeout, "set gRPC connect timeout") + flags.Duration(cfgRebalanceTimer, defaultRebalanceTimer, "set gRPC connection rebalance timer") - ttl := flags.DurationP(cfgConnectionTTL, "t", defaultTTL, "gRPC connection time to live") + ttl := flags.DurationP(cfgConnectionTTL, "t", defaultTTL, "set gRPC connection time to live") - flags.String(cfgListenAddress, "0.0.0.0:8080", "S3 Gateway listen address") - peers := flags.StringArrayP("peers", "p", nil, "NeoFS nodes") + flags.String(cfgListenAddress, "0.0.0.0:8080", "set address to listen") + peers := flags.StringArrayP("peers", "p", nil, "set NeoFS nodes") // set prefers: v.Set(cfgApplicationName, misc.ApplicationName) diff --git a/cmd/gate/app.go b/cmd/gate/app.go index a5c4021c..317b1d2b 100644 --- a/cmd/gate/app.go +++ b/cmd/gate/app.go @@ -7,13 +7,12 @@ import ( "os" "time" + s3auth "github.com/minio/minio/auth" minio "github.com/minio/minio/legacy" "github.com/minio/minio/legacy/config" "github.com/minio/minio/neofs/layer" "github.com/minio/minio/neofs/pool" "github.com/minio/minio/pkg/auth" - "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" "github.com/spf13/viper" "go.uber.org/zap" "google.golang.org/grpc/keepalive" @@ -21,11 +20,12 @@ import ( type ( App struct { - cli pool.Pool - log *zap.Logger - cfg *viper.Viper - tls *tlsConfig - obj minio.ObjectLayer + center *s3auth.Center + cli pool.Pool + log *zap.Logger + cfg *viper.Viper + tls *tlsConfig + obj minio.ObjectLayer conTimeout time.Duration reqTimeout time.Duration @@ -44,21 +44,22 @@ type ( func newApp(l *zap.Logger, v *viper.Viper) *App { var ( - err error - wif string - cli pool.Pool - tls *tlsConfig - uid refs.OwnerID - obj minio.ObjectLayer - - key = fetchKey(l, v) - - reBalance = defaultRebalanceTimer - + err error + cli pool.Pool + tls *tlsConfig + obj minio.ObjectLayer + reBalance = defaultRebalanceTimer conTimeout = defaultConnectTimeout reqTimeout = defaultRequestTimeout ) + center, err := fetchAuthCenter(l, v) + if err != nil { + l.Fatal("failed to initialize auth center", zap.Error(err)) + } + uid := center.GetOwnerID() + wif := center.GetWIFString() + if v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile) { tls = &tlsConfig{ KeyFile: v.GetString(cfgTLSKeyFile), @@ -82,7 +83,7 @@ func newApp(l *zap.Logger, v *viper.Viper) *App { Peers: fetchPeers(l, v), Logger: l, - PrivateKey: key, + PrivateKey: center.GetNeoFSKeyPrivateKey(), GRPCLogger: gRPCLogger(l), GRPCVerbose: v.GetBool(cfgGRPCVerbose), @@ -95,8 +96,7 @@ func newApp(l *zap.Logger, v *viper.Viper) *App { } if cli, err = pool.New(poolConfig); err != nil { - l.Fatal("could not prepare pool connections", - zap.Error(err)) + l.Fatal("could not prepare pool connections", zap.Error(err)) } { // should establish connection with NeoFS Storage Nodes @@ -112,42 +112,27 @@ func newApp(l *zap.Logger, v *viper.Viper) *App { } { // should prepare object layer - if uid, err = refs.NewOwnerID(&key.PublicKey); err != nil { - l.Fatal("could not fetch OwnerID", - zap.Error(err)) - } - - if wif, err = crypto.WIFEncode(key); err != nil { - l.Fatal("could not encode key to WIF", - zap.Error(err)) - } - { // Temporary solution, to resolve problems with MinIO GW access/secret keys: if err = os.Setenv(config.EnvAccessKey, uid.String()); err != nil { - l.Fatal("could not set "+config.EnvAccessKey, - zap.Error(err)) + l.Fatal("could not set "+config.EnvAccessKey, zap.Error(err)) } else if err = os.Setenv(config.EnvSecretKey, wif); err != nil { - l.Fatal("could not set "+config.EnvSecretKey, - zap.Error(err)) + l.Fatal("could not set "+config.EnvSecretKey, zap.Error(err)) } - - l.Info("used credentials", - zap.String("AccessKey", uid.String()), - zap.String("SecretKey", wif)) + l.Info("used credentials", zap.String("AccessKey", uid.String()), zap.String("SecretKey", wif)) } if obj, err = layer.NewLayer(cli, l, auth.Credentials{AccessKey: uid.String(), SecretKey: wif}); err != nil { - l.Fatal("could not prepare ObjectLayer", - zap.Error(err)) + l.Fatal("could not prepare ObjectLayer", zap.Error(err)) } } return &App{ - cli: cli, - log: l, - cfg: v, - obj: obj, - tls: tls, + center: center, + cli: cli, + log: l, + cfg: v, + obj: obj, + tls: tls, webDone: make(chan struct{}, 1), wrkDone: make(chan struct{}, 1), From 3ff7028229c22c0d82f64bc3e63309e337122120 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Thu, 16 Jul 2020 18:33:47 +0300 Subject: [PATCH 07/17] Add early support of auth middleware --- auth/center.go | 45 ++++++++++++++++++++++++++++++++++-- cmd/gate/app-new-auth.go | 24 +++++++++++++++++++ cmd/gate/app.go | 4 ++-- legacy/api-router.go | 6 ++--- legacy/neofs-router.go | 1 + neofs/layer/gateway-neofs.go | 29 ++++------------------- 6 files changed, 78 insertions(+), 31 deletions(-) create mode 100644 cmd/gate/app-new-auth.go diff --git a/auth/center.go b/auth/center.go index 84b95c62..78217075 100644 --- a/auth/center.go +++ b/auth/center.go @@ -8,6 +8,8 @@ import ( "crypto/x509" "encoding/pem" "io/ioutil" + "net/http" + "regexp" "github.com/klauspost/compress/zstd" "github.com/nspcc-dev/neofs-api-go/refs" @@ -16,8 +18,11 @@ import ( "github.com/pkg/errors" ) +const authorizationFieldPattern = `AWS4-HMAC-SHA256 Credential=(?P[^/]+)/(?P[^/]+)/(?P[^/]*)/(?P[^/]+)/aws4_request, SignedHeaders=(?P.*), Signature=(?P.*)` + // Center is a central app's authentication/authorization management unit. type Center struct { + submatcher *regexpSubmatcher zstdEncoder *zstd.Encoder zstdDecoder *zstd.Decoder neofsKeys struct { @@ -37,6 +42,7 @@ func NewCenter() *Center { zstdEncoder, _ := zstd.NewWriter(nil) zstdDecoder, _ := zstd.NewReader(nil) return &Center{ + submatcher: ®expSubmatcher{re: regexp.MustCompile(authorizationFieldPattern)}, zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder, } @@ -80,7 +86,7 @@ func (center *Center) SetUserAuthKeys(key *rsa.PrivateKey) { center.userAuthKeys.PublicKey = &key.PublicKey } -func (center *Center) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { +func (center *Center) packBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { data, err := bearerToken.Marshal() if err != nil { return nil, errors.Wrap(err, "failed to marshal bearer token") @@ -92,7 +98,7 @@ func (center *Center) PackBearerToken(bearerToken *service.BearerTokenMsg) ([]by return append(sha256Hash(data), encryptedKeyID...), nil } -func (center *Center) UnpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { +func (center *Center) unpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { compressedKeyID := packedBearerToken[32:] encryptedKeyID, err := center.decompress(compressedKeyID) if err != nil { @@ -109,6 +115,25 @@ func (center *Center) UnpackBearerToken(packedBearerToken []byte) (*service.Bear return bearerToken, nil } +func (center *Center) AuthenticationPassed(header http.Header) (*service.BearerTokenMsg, error) { + authHeaderField := header["Authorization"] + if len(authHeaderField) != 1 { + return nil, errors.New("wrong length of Authorization header field") + } + sms := center.submatcher.getSubmatches(authHeaderField[0]) + if len(sms) != 6 { + return nil, errors.New("bad Authorization header field") + } + akid := sms["access_key_id"] + bt, err := center.unpackBearerToken([]byte(akid)) + if err != nil { + return nil, errors.Wrap(err, "failed to unpack bearer token") + } + // v4sig := sms["v4_signature"] + // TODO: Validate V4 signature. + return bt, nil +} + func (center *Center) compress(data []byte) []byte { center.zstdEncoder.Reset(nil) var compressedData []byte @@ -154,3 +179,19 @@ func ReadRSAPrivateKeyFromPEMFile(filePath string) (*rsa.PrivateKey, error) { } return rsaKey, nil } + +type regexpSubmatcher struct { + re *regexp.Regexp +} + +func (resm *regexpSubmatcher) getSubmatches(target string) map[string]string { + matches := resm.re.FindStringSubmatch(target) + l := len(matches) + submatches := make(map[string]string, l) + for i, name := range resm.re.SubexpNames() { + if i > 0 && i <= l { + submatches[name] = matches[i] + } + } + return submatches +} diff --git a/cmd/gate/app-new-auth.go b/cmd/gate/app-new-auth.go new file mode 100644 index 00000000..5e771843 --- /dev/null +++ b/cmd/gate/app-new-auth.go @@ -0,0 +1,24 @@ +package main + +import ( + "net/http" + + "github.com/gorilla/mux" + s3auth "github.com/minio/minio/auth" + "go.uber.org/zap" +) + +func attachNewUserAuth(router *mux.Router, center *s3auth.Center, log *zap.Logger) { + uamw := func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := center.AuthenticationPassed(r.Header) + if err != nil { + log.Error("failed to pass authentication", zap.Error(err)) + } + // TODO: Handle any auth error by rejecting request. + h.ServeHTTP(w, r) + + }) + } + router.Use(uamw) +} diff --git a/cmd/gate/app.go b/cmd/gate/app.go index 317b1d2b..a99c93ea 100644 --- a/cmd/gate/app.go +++ b/cmd/gate/app.go @@ -12,7 +12,6 @@ import ( "github.com/minio/minio/legacy/config" "github.com/minio/minio/neofs/layer" "github.com/minio/minio/neofs/pool" - "github.com/minio/minio/pkg/auth" "github.com/spf13/viper" "go.uber.org/zap" "google.golang.org/grpc/keepalive" @@ -121,7 +120,7 @@ func newApp(l *zap.Logger, v *viper.Viper) *App { l.Info("used credentials", zap.String("AccessKey", uid.String()), zap.String("SecretKey", wif)) } - if obj, err = layer.NewLayer(cli, l, auth.Credentials{AccessKey: uid.String(), SecretKey: wif}); err != nil { + if obj, err = layer.NewLayer(cli, l, center); err != nil { l.Fatal("could not prepare ObjectLayer", zap.Error(err)) } } @@ -174,6 +173,7 @@ func (a *App) Server(ctx context.Context) { router := newS3Router() // Attach app-specific routes: + attachNewUserAuth(router, a.center, a.log) attachHealthy(router, a.cli) attachMetrics(router, a.cfg, a.log) attachProfiler(router, a.cfg, a.log) diff --git a/legacy/api-router.go b/legacy/api-router.go index ffecfd7a..ad1d0f31 100644 --- a/legacy/api-router.go +++ b/legacy/api-router.go @@ -77,13 +77,13 @@ func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool) return allowSSEKMS }, } - // API Router apiRouter := router.PathPrefix(SlashSeparator).Subrouter() var routers []*mux.Router for _, domainName := range globalDomainNames { - routers = append(routers, apiRouter.Host("{bucket:.+}."+domainName).Subrouter()) - routers = append(routers, apiRouter.Host("{bucket:.+}."+domainName+":{port:.*}").Subrouter()) + r1 := apiRouter.Host("{bucket:.+}." + domainName).Subrouter() + r2 := apiRouter.Host("{bucket:.+}." + domainName + ":{port:.*}").Subrouter() + routers = append(routers, []*mux.Router{r1, r2}...) } routers = append(routers, apiRouter.PathPrefix("/{bucket}").Subrouter()) diff --git a/legacy/neofs-router.go b/legacy/neofs-router.go index 5be69730..ac421431 100644 --- a/legacy/neofs-router.go +++ b/legacy/neofs-router.go @@ -10,6 +10,7 @@ func AttachS3API(r *mux.Router, obj ObjectLayer, l *zap.Logger) { // Initialize all help initHelp() + // TODO: If this name is actually stays unchanges, move it to constants. globalGatewayName = "NeoFS S3 Gate" // Set when gateway is enabled diff --git a/neofs/layer/gateway-neofs.go b/neofs/layer/gateway-neofs.go index 2f4c161d..05898fec 100644 --- a/neofs/layer/gateway-neofs.go +++ b/neofs/layer/gateway-neofs.go @@ -6,13 +6,11 @@ import ( "math" "time" + s3auth "github.com/minio/minio/auth" minio "github.com/minio/minio/legacy" "github.com/minio/minio/neofs/pool" - "github.com/minio/minio/pkg/auth" - "github.com/nspcc-dev/neofs-api-go/chain" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" - crypto "github.com/nspcc-dev/neofs-crypto" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -42,41 +40,24 @@ type ( // NewGatewayLayer creates instance of neofsObject. It checks credentials // and establishes gRPC connection with node. -func NewLayer(cli pool.Client, log *zap.Logger, cred auth.Credentials) (minio.ObjectLayer, error) { - // check if wif is correct - key, err := crypto.WIFDecode(cred.SecretKey) - if err != nil { - return nil, errors.New("can't decode secret key, it must be WIF") - } - // check if wif corresponds wallet address - if cred.AccessKey != chain.KeysToAddress(&key.PublicKey) { - return nil, errors.New("wif and wallet are not corresponded") - } - // format public key into owner - owner, err := refs.NewOwnerID(&key.PublicKey) - if err != nil { - return nil, errors.New("can't create owner id from key") - } - +func NewLayer(cli pool.Client, log *zap.Logger, center *s3auth.Center) (minio.ObjectLayer, error) { // setup gRPC connection // todo: think about getting timeout parameters from cli args ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - token, err := generateToken(ctx, tokenParams{ cli: cli, - key: key, + key: center.GetNeoFSKeyPrivateKey(), until: math.MaxInt64, }) if err != nil { return nil, errors.Wrap(err, "can't establish neofs session with remote host") } - return &neofsObject{ cli: cli, - key: key, + key: center.GetNeoFSKeyPrivateKey(), log: log, - owner: owner, + owner: center.GetOwnerID(), token: token, }, nil } From d9b146628d25366bff5d471a45003c3f82043c58 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Mon, 20 Jul 2020 20:23:16 +0300 Subject: [PATCH 08/17] Tune getting bearer token; prepare for passing through bearer token --- auth/center.go | 69 ++++++++++++++++++++-------------- cmd/gate/app-new-auth.go | 2 +- cmd/gate/app-settings.go | 2 +- cmd/gate/app.go | 2 +- legacy/auth-handler.go | 7 +++- neofs/layer/gateway-neofs.go | 13 ++++--- neofs/layer/neofs-container.go | 1 + 7 files changed, 57 insertions(+), 39 deletions(-) diff --git a/auth/center.go b/auth/center.go index 78217075..984162f0 100644 --- a/auth/center.go +++ b/auth/center.go @@ -1,11 +1,13 @@ package auth import ( + "bytes" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" + "encoding/hex" "encoding/pem" "io/ioutil" "net/http" @@ -16,12 +18,16 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/pkg/errors" + "go.uber.org/zap" ) const authorizationFieldPattern = `AWS4-HMAC-SHA256 Credential=(?P[^/]+)/(?P[^/]+)/(?P[^/]*)/(?P[^/]+)/aws4_request, SignedHeaders=(?P.*), Signature=(?P.*)` +const emptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` + // Center is a central app's authentication/authorization management unit. type Center struct { + log *zap.Logger submatcher *regexpSubmatcher zstdEncoder *zstd.Encoder zstdDecoder *zstd.Decoder @@ -38,10 +44,11 @@ type Center struct { } // NewCenter creates an instance of AuthCenter. -func NewCenter() *Center { +func NewCenter(log *zap.Logger) *Center { zstdEncoder, _ := zstd.NewWriter(nil) zstdDecoder, _ := zstd.NewReader(nil) return &Center{ + log: log, submatcher: ®expSubmatcher{re: regexp.MustCompile(authorizationFieldPattern)}, zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder, @@ -86,68 +93,72 @@ func (center *Center) SetUserAuthKeys(key *rsa.PrivateKey) { center.userAuthKeys.PublicKey = &key.PublicKey } -func (center *Center) packBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { +func (center *Center) packBearerToken(bearerToken *service.BearerTokenMsg) (string, string, error) { data, err := bearerToken.Marshal() if err != nil { - return nil, errors.Wrap(err, "failed to marshal bearer token") + return "", "", errors.Wrap(err, "failed to marshal bearer token") } encryptedKeyID, err := encrypt(center.userAuthKeys.PublicKey, center.compress(data)) if err != nil { - return nil, errors.Wrap(err, "") + return "", "", errors.Wrap(err, "failed to encrypt bearer token bytes") } - return append(sha256Hash(data), encryptedKeyID...), nil + accessKeyID := hex.EncodeToString(encryptedKeyID) + secretAccessKey := hex.EncodeToString(sha256Hash(data)) + return accessKeyID, secretAccessKey, nil } -func (center *Center) unpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { - compressedKeyID := packedBearerToken[32:] - encryptedKeyID, err := center.decompress(compressedKeyID) +func (center *Center) unpackBearerToken(accessKeyID string) (*service.BearerTokenMsg, error) { + encryptedKeyID, err := hex.DecodeString(accessKeyID) if err != nil { - return nil, errors.Wrap(err, "failed to decompress key ID") + return nil, errors.Wrap(err, "failed to decode HEX string") } - keyID, err := decrypt(center.userAuthKeys.PrivateKey, encryptedKeyID) + compressedKeyID, err := decrypt(center.userAuthKeys.PrivateKey, encryptedKeyID) if err != nil { return nil, errors.Wrap(err, "failed to decrypt key ID") } + data, err := center.decompress(compressedKeyID) + if err != nil { + return nil, errors.Wrap(err, "failed to decompress key ID") + } bearerToken := new(service.BearerTokenMsg) - if err := bearerToken.Unmarshal(keyID); err != nil { + if err := bearerToken.Unmarshal(data); err != nil { return nil, errors.Wrap(err, "failed to unmarshal embedded bearer token") } return bearerToken, nil } -func (center *Center) AuthenticationPassed(header http.Header) (*service.BearerTokenMsg, error) { - authHeaderField := header["Authorization"] +func (center *Center) AuthenticationPassed(request *http.Request) (*service.BearerTokenMsg, error) { + authHeaderField := request.Header["Authorization"] if len(authHeaderField) != 1 { - return nil, errors.New("wrong length of Authorization header field") + return nil, errors.New("unsupported request: wrong length of Authorization header field") } sms := center.submatcher.getSubmatches(authHeaderField[0]) if len(sms) != 6 { return nil, errors.New("bad Authorization header field") } - akid := sms["access_key_id"] - bt, err := center.unpackBearerToken([]byte(akid)) + bt, err := center.unpackBearerToken(sms["access_key_id"]) if err != nil { - return nil, errors.Wrap(err, "failed to unpack bearer token") + center.log.Warn("Failed to unpack bearer token", zap.Error(err)) + //return nil, errors.Wrap(err, "failed to unpack bearer token") } - // v4sig := sms["v4_signature"] - // TODO: Validate V4 signature. return bt, nil } +func readAndReplaceBody(request *http.Request) []byte { + if request.Body == nil { + return []byte{} + } + payload, _ := ioutil.ReadAll(request.Body) + request.Body = ioutil.NopCloser(bytes.NewReader(payload)) + return payload +} + func (center *Center) compress(data []byte) []byte { - center.zstdEncoder.Reset(nil) - var compressedData []byte - center.zstdEncoder.EncodeAll(data, compressedData) - return compressedData + return center.zstdEncoder.EncodeAll(data, make([]byte, 0, len(data))) } func (center *Center) decompress(data []byte) ([]byte, error) { - center.zstdDecoder.Reset(nil) - var decompressedData []byte - if _, err := center.zstdDecoder.DecodeAll(data, decompressedData); err != nil { - return nil, err - } - return decompressedData, nil + return center.zstdDecoder.DecodeAll(data, nil) } func encrypt(key *rsa.PublicKey, data []byte) ([]byte, error) { diff --git a/cmd/gate/app-new-auth.go b/cmd/gate/app-new-auth.go index 5e771843..066092c6 100644 --- a/cmd/gate/app-new-auth.go +++ b/cmd/gate/app-new-auth.go @@ -11,7 +11,7 @@ import ( func attachNewUserAuth(router *mux.Router, center *s3auth.Center, log *zap.Logger) { uamw := func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := center.AuthenticationPassed(r.Header) + _, err := center.AuthenticationPassed(r) if err != nil { log.Error("failed to pass authentication", zap.Error(err)) } diff --git a/cmd/gate/app-settings.go b/cmd/gate/app-settings.go index 98422939..e9895053 100644 --- a/cmd/gate/app-settings.go +++ b/cmd/gate/app-settings.go @@ -110,7 +110,7 @@ func fetchAuthCenter(l *zap.Logger, v *viper.Viper) (*s3auth.Center, error) { if err != nil { return nil, errors.Wrap(err, "could not load UserAuth private key") } - center := s3auth.NewCenter() + center := s3auth.NewCenter(l) center.SetUserAuthKeys(userAuthPrivateKey) center.SetNeoFSKeys(neofsPrivateKey) return center, nil diff --git a/cmd/gate/app.go b/cmd/gate/app.go index a99c93ea..a94c9f9a 100644 --- a/cmd/gate/app.go +++ b/cmd/gate/app.go @@ -120,7 +120,7 @@ func newApp(l *zap.Logger, v *viper.Viper) *App { l.Info("used credentials", zap.String("AccessKey", uid.String()), zap.String("SecretKey", wif)) } - if obj, err = layer.NewLayer(cli, l, center); err != nil { + if obj, err = layer.NewLayer(l, cli, center); err != nil { l.Fatal("could not prepare ObjectLayer", zap.Error(err)) } } diff --git a/legacy/auth-handler.go b/legacy/auth-handler.go index 0d66d768..29c98038 100644 --- a/legacy/auth-handler.go +++ b/legacy/auth-handler.go @@ -274,13 +274,18 @@ func checkRequestAuthType(ctx context.Context, r *http.Request, action policy.Ac return s3Err } +// FIXME: Remove this temporary stub to by-pass Minio auth procedure. +func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (string, bool, APIErrorCode) { + return "", true, ErrNone +} + // Check request auth type verifies the incoming http request // - validates the request signature // - validates the policy action if anonymous tests bucket policies if any, // for authenticated requests validates IAM policies. // returns APIErrorCode if any to be replied to the client. // Additionally returns the accessKey used in the request, and if this request is by an admin. -func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (accessKey string, owner bool, s3Err APIErrorCode) { +func _checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (accessKey string, owner bool, s3Err APIErrorCode) { var cred auth.Credentials switch getRequestAuthType(r) { case authTypeUnknown, authTypeStreamingSigned: diff --git a/neofs/layer/gateway-neofs.go b/neofs/layer/gateway-neofs.go index 05898fec..71824ddf 100644 --- a/neofs/layer/gateway-neofs.go +++ b/neofs/layer/gateway-neofs.go @@ -21,11 +21,12 @@ type ( neofsObject struct { minio.GatewayUnsupported // placeholder for unimplemented functions - cli pool.Client - log *zap.Logger - key *ecdsa.PrivateKey - owner refs.OwnerID - token *service.Token + log *zap.Logger + cli pool.Client + key *ecdsa.PrivateKey + owner refs.OwnerID + token *service.Token + bearerToken *service.BearerTokenMsg // Concurrency must be resolved by creating one lock per object, but // it may be unnecessary in neofs, because objects are immutable. So @@ -40,7 +41,7 @@ type ( // NewGatewayLayer creates instance of neofsObject. It checks credentials // and establishes gRPC connection with node. -func NewLayer(cli pool.Client, log *zap.Logger, center *s3auth.Center) (minio.ObjectLayer, error) { +func NewLayer(log *zap.Logger, cli pool.Client, center *s3auth.Center) (minio.ObjectLayer, error) { // setup gRPC connection // todo: think about getting timeout parameters from cli args ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) diff --git a/neofs/layer/neofs-container.go b/neofs/layer/neofs-container.go index e6a60bb3..fb43031f 100644 --- a/neofs/layer/neofs-container.go +++ b/neofs/layer/neofs-container.go @@ -16,6 +16,7 @@ func (n *neofsObject) containerList(ctx context.Context) ([]refs.CID, error) { req.OwnerID = n.owner req.SetTTL(service.SingleForwardingTTL) req.SetVersion(APIVersion) + req.SetBearer(nil) err := service.SignRequestData(n.key, req) if err != nil { From 5d0505001cc1846be24aebca50c22b1b15285196 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 02:42:48 +0300 Subject: [PATCH 09/17] Update deps --- go.mod | 1 + go.sum | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/go.mod b/go.mod index 6edcbe51..f01570da 100644 --- a/go.mod +++ b/go.mod @@ -110,4 +110,5 @@ require ( gopkg.in/olivere/elastic.v5 v5.0.80 gopkg.in/yaml.v2 v2.2.8 honnef.co/go/tools v0.0.1-2020.1.3 // indirect + github.com/aws/aws-sdk-go v1.33.8 ) diff --git a/go.sum b/go.sum index 0c76b481..bb5f39f6 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,8 @@ github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQh github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310 h1:t+qxRrRtwNiUYA+Xh2jSXhoG2grnMCMKX4Fg6lx9X1U= github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/aws/aws-sdk-go v1.33.8 h1:2/sOfb9oPHTRZ0lxinoaTPDcYwNa1H/SpKP4nVRBwmg= +github.com/aws/aws-sdk-go v1.33.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/bcicen/jstream v0.0.0-20190220045926-16c1f8af81c2 h1:M+TYzBcNIRyzPRg66ndEqUMd7oWDmhvdQmaPC6EZNwM= github.com/bcicen/jstream v0.0.0-20190220045926-16c1f8af81c2/go.mod h1:RDu/qcrnpEdJC/p8tx34+YBFqqX71lB7dOX9QE+ZC4M= github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw= @@ -259,6 +261,8 @@ github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7V github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjLBdCp5PRlCFijNjvcYANOZXzCfXwCM= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -599,6 +603,7 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= From 22487b9ccf42ecc91725b8a866a637bd34bbf070 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 02:43:40 +0300 Subject: [PATCH 10/17] Enable auth validation for signed requests --- auth/center.go | 83 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/auth/center.go b/auth/center.go index 984162f0..fc629a86 100644 --- a/auth/center.go +++ b/auth/center.go @@ -2,6 +2,7 @@ package auth import ( "bytes" + "context" "crypto/ecdsa" "crypto/rand" "crypto/rsa" @@ -12,7 +13,11 @@ import ( "io/ioutil" "net/http" "regexp" + "strings" + "time" + "github.com/aws/aws-sdk-go/aws/credentials" + v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/klauspost/compress/zstd" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" @@ -107,50 +112,94 @@ func (center *Center) packBearerToken(bearerToken *service.BearerTokenMsg) (stri return accessKeyID, secretAccessKey, nil } -func (center *Center) unpackBearerToken(accessKeyID string) (*service.BearerTokenMsg, error) { +func (center *Center) unpackBearerToken(accessKeyID string) (*service.BearerTokenMsg, string, error) { encryptedKeyID, err := hex.DecodeString(accessKeyID) if err != nil { - return nil, errors.Wrap(err, "failed to decode HEX string") + return nil, "", errors.Wrap(err, "failed to decode HEX string") } compressedKeyID, err := decrypt(center.userAuthKeys.PrivateKey, encryptedKeyID) if err != nil { - return nil, errors.Wrap(err, "failed to decrypt key ID") + return nil, "", errors.Wrap(err, "failed to decrypt key ID") } data, err := center.decompress(compressedKeyID) if err != nil { - return nil, errors.Wrap(err, "failed to decompress key ID") + return nil, "", errors.Wrap(err, "failed to decompress key ID") } bearerToken := new(service.BearerTokenMsg) if err := bearerToken.Unmarshal(data); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal embedded bearer token") + return nil, "", errors.Wrap(err, "failed to unmarshal embedded bearer token") } - return bearerToken, nil + secretAccessKey := hex.EncodeToString(sha256Hash(data)) + return bearerToken, secretAccessKey, nil } func (center *Center) AuthenticationPassed(request *http.Request) (*service.BearerTokenMsg, error) { + queryValues := request.URL.Query() + if queryValues.Get("X-Amz-Algorithm") == "AWS4-HMAC-SHA256" { + return nil, errors.New("pre-signed form of request is not supported") + } authHeaderField := request.Header["Authorization"] if len(authHeaderField) != 1 { return nil, errors.New("unsupported request: wrong length of Authorization header field") } - sms := center.submatcher.getSubmatches(authHeaderField[0]) - if len(sms) != 6 { + sms1 := center.submatcher.getSubmatches(authHeaderField[0]) + if len(sms1) != 6 { return nil, errors.New("bad Authorization header field") } - bt, err := center.unpackBearerToken(sms["access_key_id"]) - if err != nil { - center.log.Warn("Failed to unpack bearer token", zap.Error(err)) - //return nil, errors.Wrap(err, "failed to unpack bearer token") + signedHeaderFieldsNames := strings.Split(sms1["signed_header_fields"], ";") + if len(signedHeaderFieldsNames) == 0 { + return nil, errors.New("wrong format of signed headers part") } - return bt, nil + signatureDateTime, err := time.Parse("20060102T150405Z", request.Header.Get("X-Amz-Date")) + if err != nil { + // TODO + } + accessKeyID := sms1["access_key_id"] + bearerToken, secretAccessKey, err := center.unpackBearerToken(accessKeyID) + if err != nil { + // FIXME: Should be `return nil, errors.Wrap(err, "failed to unpack bearer token")` + center.log.Warn("Failed to unpack bearer token", zap.Error(err)) + return nil, nil + } + otherRequest := request.Clone(context.TODO()) + otherRequest.Header = map[string][]string{} + for hfn, hfvs := range request.Header { + for _, shfn := range signedHeaderFieldsNames { + if strings.EqualFold(hfn, shfn) { + otherRequest.Header[hfn] = hfvs + } + } + } + awsCreds := credentials.NewStaticCredentials(accessKeyID, secretAccessKey, "") + signer := v4.NewSigner(awsCreds) + body, err := readAndKeepBody(request) + if err != nil { + // TODO + } + _, err = signer.Sign(otherRequest, body, sms1["service"], sms1["region"], signatureDateTime) + if err != nil { + // TODO + } + sms2 := center.submatcher.getSubmatches(otherRequest.Header.Get("Authorization")) + if sms1["v4_signature"] != sms2["v4_signature"] { + // FIXME: Should be `return nil, errors.Wrap(err, "failed to pass authentication procedure")` + center.log.Warn("Failed to pass authentication procedure") + return nil, nil + } + return bearerToken, nil } -func readAndReplaceBody(request *http.Request) []byte { +func readAndKeepBody(request *http.Request) (*bytes.Reader, error) { if request.Body == nil { - return []byte{} + var r bytes.Reader + return &r, nil + } + payload, err := ioutil.ReadAll(request.Body) + if err != nil { + return nil, err } - payload, _ := ioutil.ReadAll(request.Body) request.Body = ioutil.NopCloser(bytes.NewReader(payload)) - return payload + return bytes.NewReader(payload), nil } func (center *Center) compress(data []byte) []byte { From e4f7a702dc9f85bec35bfa159ad346a4f8b8ab65 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 11:35:30 +0300 Subject: [PATCH 11/17] Move auth HTTP header field regexp to a package level variable --- auth/center.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth/center.go b/auth/center.go index fc629a86..35797766 100644 --- a/auth/center.go +++ b/auth/center.go @@ -26,7 +26,7 @@ import ( "go.uber.org/zap" ) -const authorizationFieldPattern = `AWS4-HMAC-SHA256 Credential=(?P[^/]+)/(?P[^/]+)/(?P[^/]*)/(?P[^/]+)/aws4_request, SignedHeaders=(?P.*), Signature=(?P.*)` +var authorizationFieldRegexp = regexp.MustCompile(`AWS4-HMAC-SHA256 Credential=(?P[^/]+)/(?P[^/]+)/(?P[^/]*)/(?P[^/]+)/aws4_request, SignedHeaders=(?P.*), Signature=(?P.*)`) const emptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` @@ -54,7 +54,7 @@ func NewCenter(log *zap.Logger) *Center { zstdDecoder, _ := zstd.NewReader(nil) return &Center{ log: log, - submatcher: ®expSubmatcher{re: regexp.MustCompile(authorizationFieldPattern)}, + submatcher: ®expSubmatcher{re: authorizationFieldRegexp}, zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder, } From 6c0ddca8d0730501c37f0c7b88a8d3b3c36ca11f Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 12:28:00 +0300 Subject: [PATCH 12/17] Add missing error checks --- auth/center.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/auth/center.go b/auth/center.go index 35797766..801f6074 100644 --- a/auth/center.go +++ b/auth/center.go @@ -152,7 +152,7 @@ func (center *Center) AuthenticationPassed(request *http.Request) (*service.Bear } signatureDateTime, err := time.Parse("20060102T150405Z", request.Header.Get("X-Amz-Date")) if err != nil { - // TODO + return nil, errors.Wrap(err, "failed to parse x-amz-date header field") } accessKeyID := sms1["access_key_id"] bearerToken, secretAccessKey, err := center.unpackBearerToken(accessKeyID) @@ -174,11 +174,11 @@ func (center *Center) AuthenticationPassed(request *http.Request) (*service.Bear signer := v4.NewSigner(awsCreds) body, err := readAndKeepBody(request) if err != nil { - // TODO + return nil, errors.Wrap(err, "failed to read out request body") } _, err = signer.Sign(otherRequest, body, sms1["service"], sms1["region"], signatureDateTime) if err != nil { - // TODO + return nil, errors.Wrap(err, "failed to sign temporary HTTP request") } sms2 := center.submatcher.getSubmatches(otherRequest.Header.Get("Authorization")) if sms1["v4_signature"] != sms2["v4_signature"] { From ea7286c875351a517b2847abb32c7f590ae38541 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 12:40:46 +0300 Subject: [PATCH 13/17] Split code into smaller parts within the auth package --- auth/center.go | 51 +------------------------------------------- auth/regexp-utils.go | 19 +++++++++++++++++ auth/rsa-utils.go | 42 ++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 50 deletions(-) create mode 100644 auth/regexp-utils.go create mode 100644 auth/rsa-utils.go diff --git a/auth/center.go b/auth/center.go index 801f6074..1a163a09 100644 --- a/auth/center.go +++ b/auth/center.go @@ -4,12 +4,8 @@ import ( "bytes" "context" "crypto/ecdsa" - "crypto/rand" "crypto/rsa" - "crypto/sha256" - "crypto/x509" "encoding/hex" - "encoding/pem" "io/ioutil" "net/http" "regexp" @@ -189,6 +185,7 @@ func (center *Center) AuthenticationPassed(request *http.Request) (*service.Bear return bearerToken, nil } +// TODO: Make this write into a smart buffer backed by a file on a fast drive. func readAndKeepBody(request *http.Request) (*bytes.Reader, error) { if request.Body == nil { var r bytes.Reader @@ -209,49 +206,3 @@ func (center *Center) compress(data []byte) []byte { func (center *Center) decompress(data []byte) ([]byte, error) { return center.zstdDecoder.DecodeAll(data, nil) } - -func encrypt(key *rsa.PublicKey, data []byte) ([]byte, error) { - return rsa.EncryptOAEP(sha256.New(), rand.Reader, key, data, []byte{}) -} - -func decrypt(key *rsa.PrivateKey, data []byte) ([]byte, error) { - return rsa.DecryptOAEP(sha256.New(), rand.Reader, key, data, []byte{}) -} - -func sha256Hash(data []byte) []byte { - hash := sha256.New() - hash.Write(data) - return hash.Sum(nil) -} - -func ReadRSAPrivateKeyFromPEMFile(filePath string) (*rsa.PrivateKey, error) { - kbs, err := ioutil.ReadFile(filePath) - if err != nil { - return nil, errors.Wrapf(err, "failed to read file %s", filePath) - } - pemBlock, _ := pem.Decode(kbs) - if pemBlock == nil { - return nil, errors.Errorf("failed to decode PEM data from file %s", filePath) - } - rsaKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", filePath) - } - return rsaKey, nil -} - -type regexpSubmatcher struct { - re *regexp.Regexp -} - -func (resm *regexpSubmatcher) getSubmatches(target string) map[string]string { - matches := resm.re.FindStringSubmatch(target) - l := len(matches) - submatches := make(map[string]string, l) - for i, name := range resm.re.SubexpNames() { - if i > 0 && i <= l { - submatches[name] = matches[i] - } - } - return submatches -} diff --git a/auth/regexp-utils.go b/auth/regexp-utils.go new file mode 100644 index 00000000..94ba85d1 --- /dev/null +++ b/auth/regexp-utils.go @@ -0,0 +1,19 @@ +package auth + +import "regexp" + +type regexpSubmatcher struct { + re *regexp.Regexp +} + +func (resm *regexpSubmatcher) getSubmatches(target string) map[string]string { + matches := resm.re.FindStringSubmatch(target) + l := len(matches) + submatches := make(map[string]string, l) + for i, name := range resm.re.SubexpNames() { + if i > 0 && i <= l { + submatches[name] = matches[i] + } + } + return submatches +} diff --git a/auth/rsa-utils.go b/auth/rsa-utils.go new file mode 100644 index 00000000..cd1e7562 --- /dev/null +++ b/auth/rsa-utils.go @@ -0,0 +1,42 @@ +package auth + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "io/ioutil" + + "github.com/pkg/errors" +) + +func ReadRSAPrivateKeyFromPEMFile(filePath string) (*rsa.PrivateKey, error) { + kbs, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, errors.Wrapf(err, "failed to read file %s", filePath) + } + pemBlock, _ := pem.Decode(kbs) + if pemBlock == nil { + return nil, errors.Errorf("failed to decode PEM data from file %s", filePath) + } + rsaKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse private key bytes from pem data from file %s", filePath) + } + return rsaKey, nil +} + +func encrypt(key *rsa.PublicKey, data []byte) ([]byte, error) { + return rsa.EncryptOAEP(sha256.New(), rand.Reader, key, data, []byte{}) +} + +func decrypt(key *rsa.PrivateKey, data []byte) ([]byte, error) { + return rsa.DecryptOAEP(sha256.New(), rand.Reader, key, data, []byte{}) +} + +func sha256Hash(data []byte) []byte { + hash := sha256.New() + hash.Write(data) + return hash.Sum(nil) +} From 3870f59484c4d74a78bad0b88029676d0988916c Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 12:44:25 +0300 Subject: [PATCH 14/17] Rename GetNeoFSKey(Public/Private)Key to GetNeoFS(Public/Private)Key --- auth/center.go | 4 ++-- cmd/gate/app.go | 2 +- neofs/layer/gateway-neofs.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/auth/center.go b/auth/center.go index 1a163a09..df7f2fb0 100644 --- a/auth/center.go +++ b/auth/center.go @@ -73,11 +73,11 @@ func (center *Center) SetNeoFSKeys(key *ecdsa.PrivateKey) error { return nil } -func (center *Center) GetNeoFSKeyPrivateKey() *ecdsa.PrivateKey { +func (center *Center) GetNeoFSPrivateKey() *ecdsa.PrivateKey { return center.neofsKeys.PrivateKey } -func (center *Center) GetNeoFSKeyPublicKey() *ecdsa.PublicKey { +func (center *Center) GetNeoFSPublicKey() *ecdsa.PublicKey { return center.neofsKeys.PublicKey } diff --git a/cmd/gate/app.go b/cmd/gate/app.go index f3bf6222..08bff022 100644 --- a/cmd/gate/app.go +++ b/cmd/gate/app.go @@ -83,7 +83,7 @@ func newApp(l *zap.Logger, v *viper.Viper) *App { Peers: fetchPeers(l, v), Logger: l, - PrivateKey: center.GetNeoFSKeyPrivateKey(), + PrivateKey: center.GetNeoFSPrivateKey(), GRPCLogger: gRPCLogger(l), GRPCVerbose: v.GetBool(cfgGRPCVerbose), diff --git a/neofs/layer/gateway-neofs.go b/neofs/layer/gateway-neofs.go index 71824ddf..6bf45a8c 100644 --- a/neofs/layer/gateway-neofs.go +++ b/neofs/layer/gateway-neofs.go @@ -48,7 +48,7 @@ func NewLayer(log *zap.Logger, cli pool.Client, center *s3auth.Center) (minio.Ob defer cancel() token, err := generateToken(ctx, tokenParams{ cli: cli, - key: center.GetNeoFSKeyPrivateKey(), + key: center.GetNeoFSPrivateKey(), until: math.MaxInt64, }) if err != nil { @@ -56,7 +56,7 @@ func NewLayer(log *zap.Logger, cli pool.Client, center *s3auth.Center) (minio.Ob } return &neofsObject{ cli: cli, - key: center.GetNeoFSKeyPrivateKey(), + key: center.GetNeoFSPrivateKey(), log: log, owner: center.GetOwnerID(), token: token, From 24b19152c4fde9703caaf7ab173e631e0a231a5a Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 12:58:53 +0300 Subject: [PATCH 15/17] Remove aliasing --- cmd/gate/app-new-auth.go | 4 ++-- cmd/gate/app-settings.go | 8 ++++---- cmd/gate/app.go | 4 ++-- neofs/layer/gateway-neofs.go | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/gate/app-new-auth.go b/cmd/gate/app-new-auth.go index 066092c6..6410193e 100644 --- a/cmd/gate/app-new-auth.go +++ b/cmd/gate/app-new-auth.go @@ -4,11 +4,11 @@ import ( "net/http" "github.com/gorilla/mux" - s3auth "github.com/minio/minio/auth" + "github.com/minio/minio/auth" "go.uber.org/zap" ) -func attachNewUserAuth(router *mux.Router, center *s3auth.Center, log *zap.Logger) { +func attachNewUserAuth(router *mux.Router, center *auth.Center, log *zap.Logger) { uamw := func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := center.AuthenticationPassed(r) diff --git a/cmd/gate/app-settings.go b/cmd/gate/app-settings.go index e9895053..5461f512 100644 --- a/cmd/gate/app-settings.go +++ b/cmd/gate/app-settings.go @@ -12,7 +12,7 @@ import ( "strings" "time" - s3auth "github.com/minio/minio/auth" + "github.com/minio/minio/auth" "github.com/minio/minio/neofs/pool" "github.com/pkg/errors" @@ -87,7 +87,7 @@ type empty int func (empty) Read([]byte) (int, error) { return 0, io.EOF } -func fetchAuthCenter(l *zap.Logger, v *viper.Viper) (*s3auth.Center, error) { +func fetchAuthCenter(l *zap.Logger, v *viper.Viper) (*auth.Center, error) { var ( err error neofsPrivateKey *ecdsa.PrivateKey @@ -106,11 +106,11 @@ func fetchAuthCenter(l *zap.Logger, v *viper.Viper) (*s3auth.Center, error) { } } uapk := v.GetString(cfgUserAuthPrivateKey) - userAuthPrivateKey, err = s3auth.ReadRSAPrivateKeyFromPEMFile(uapk) + userAuthPrivateKey, err = auth.ReadRSAPrivateKeyFromPEMFile(uapk) if err != nil { return nil, errors.Wrap(err, "could not load UserAuth private key") } - center := s3auth.NewCenter(l) + center := auth.NewCenter(l) center.SetUserAuthKeys(userAuthPrivateKey) center.SetNeoFSKeys(neofsPrivateKey) return center, nil diff --git a/cmd/gate/app.go b/cmd/gate/app.go index 08bff022..f7c7c1c2 100644 --- a/cmd/gate/app.go +++ b/cmd/gate/app.go @@ -7,7 +7,7 @@ import ( "os" "time" - s3auth "github.com/minio/minio/auth" + "github.com/minio/minio/auth" minio "github.com/minio/minio/legacy" "github.com/minio/minio/legacy/config" "github.com/minio/minio/neofs/layer" @@ -20,7 +20,7 @@ import ( type ( App struct { - center *s3auth.Center + center *auth.Center cli pool.Pool log *zap.Logger cfg *viper.Viper diff --git a/neofs/layer/gateway-neofs.go b/neofs/layer/gateway-neofs.go index 6bf45a8c..498d7b30 100644 --- a/neofs/layer/gateway-neofs.go +++ b/neofs/layer/gateway-neofs.go @@ -6,7 +6,7 @@ import ( "math" "time" - s3auth "github.com/minio/minio/auth" + auth "github.com/minio/minio/auth" minio "github.com/minio/minio/legacy" "github.com/minio/minio/neofs/pool" "github.com/nspcc-dev/neofs-api-go/refs" @@ -41,7 +41,7 @@ type ( // NewGatewayLayer creates instance of neofsObject. It checks credentials // and establishes gRPC connection with node. -func NewLayer(log *zap.Logger, cli pool.Client, center *s3auth.Center) (minio.ObjectLayer, error) { +func NewLayer(log *zap.Logger, cli pool.Client, center *auth.Center) (minio.ObjectLayer, error) { // setup gRPC connection // todo: think about getting timeout parameters from cli args ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) From c6b60765443cf0016e67dde7522f533a949a0058 Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 13:07:09 +0300 Subject: [PATCH 16/17] Get back checking severe auth errors --- auth/center.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/auth/center.go b/auth/center.go index df7f2fb0..83bfc6f4 100644 --- a/auth/center.go +++ b/auth/center.go @@ -153,9 +153,7 @@ func (center *Center) AuthenticationPassed(request *http.Request) (*service.Bear accessKeyID := sms1["access_key_id"] bearerToken, secretAccessKey, err := center.unpackBearerToken(accessKeyID) if err != nil { - // FIXME: Should be `return nil, errors.Wrap(err, "failed to unpack bearer token")` - center.log.Warn("Failed to unpack bearer token", zap.Error(err)) - return nil, nil + return nil, errors.Wrap(err, "failed to unpack bearer token") } otherRequest := request.Clone(context.TODO()) otherRequest.Header = map[string][]string{} @@ -178,9 +176,7 @@ func (center *Center) AuthenticationPassed(request *http.Request) (*service.Bear } sms2 := center.submatcher.getSubmatches(otherRequest.Header.Get("Authorization")) if sms1["v4_signature"] != sms2["v4_signature"] { - // FIXME: Should be `return nil, errors.Wrap(err, "failed to pass authentication procedure")` - center.log.Warn("Failed to pass authentication procedure") - return nil, nil + return nil, errors.Wrap(err, "failed to pass authentication procedure") } return bearerToken, nil } From b5bd835cd57d528a62b29221b990f3f95c2a34cf Mon Sep 17 00:00:00 2001 From: Pavel Korotkov Date: Tue, 21 Jul 2020 13:21:03 +0300 Subject: [PATCH 17/17] Add error checking while creating auth center --- auth/center.go | 14 ++++++++++---- cmd/gate/app-settings.go | 5 ++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/auth/center.go b/auth/center.go index 83bfc6f4..5400ec46 100644 --- a/auth/center.go +++ b/auth/center.go @@ -45,15 +45,21 @@ type Center struct { } // NewCenter creates an instance of AuthCenter. -func NewCenter(log *zap.Logger) *Center { - zstdEncoder, _ := zstd.NewWriter(nil) - zstdDecoder, _ := zstd.NewReader(nil) +func NewCenter(log *zap.Logger) (*Center, error) { + zstdEncoder, err := zstd.NewWriter(nil) + if err != nil { + return nil, errors.Wrap(err, "failed to create zstd encoder") + } + zstdDecoder, err := zstd.NewReader(nil) + if err != nil { + return nil, errors.Wrap(err, "failed to create zstd decoder") + } return &Center{ log: log, submatcher: ®expSubmatcher{re: authorizationFieldRegexp}, zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder, - } + }, nil } func (center *Center) SetNeoFSKeys(key *ecdsa.PrivateKey) error { diff --git a/cmd/gate/app-settings.go b/cmd/gate/app-settings.go index 5461f512..323338f7 100644 --- a/cmd/gate/app-settings.go +++ b/cmd/gate/app-settings.go @@ -110,7 +110,10 @@ func fetchAuthCenter(l *zap.Logger, v *viper.Viper) (*auth.Center, error) { if err != nil { return nil, errors.Wrap(err, "could not load UserAuth private key") } - center := auth.NewCenter(l) + center, err := auth.NewCenter(l) + if err != nil { + return nil, errors.Wrap(err, "failed to create auth center") + } center.SetUserAuthKeys(userAuthPrivateKey) center.SetNeoFSKeys(neofsPrivateKey) return center, nil