mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-30 10:56:28 +02:00
cryptutil: fix potential race with signer
This commit is contained in:
parent
14547a50e4
commit
e30aa2c13b
5 changed files with 66 additions and 14 deletions
|
@ -33,6 +33,10 @@
|
||||||
- Pomerium and its services will gracefully shutdown on [interrupt signal](http://man7.org/linux/man-pages/man7/signal.7.html). [GH-230]
|
- Pomerium and its services will gracefully shutdown on [interrupt signal](http://man7.org/linux/man-pages/man7/signal.7.html). [GH-230]
|
||||||
- [Google](https://developers.google.com/identity/protocols/OpenIDConnect) now prompts the user to select a user account (by adding `select_account` to the sign in url). This allows a user who has multiple accounts at the authorization server to select amongst the multiple accounts that they may have current sessions for.
|
- [Google](https://developers.google.com/identity/protocols/OpenIDConnect) now prompts the user to select a user account (by adding `select_account` to the sign in url). This allows a user who has multiple accounts at the authorization server to select amongst the multiple accounts that they may have current sessions for.
|
||||||
|
|
||||||
|
### FIXED
|
||||||
|
|
||||||
|
- Fixed potential race condition when signing requests. [GH-240]
|
||||||
|
|
||||||
## v0.1.0
|
## v0.1.0
|
||||||
|
|
||||||
### NEW
|
### NEW
|
||||||
|
|
|
@ -77,7 +77,7 @@ func (c *XChaCha20Cipher) GenerateNonce() []byte {
|
||||||
func (c *XChaCha20Cipher) Encrypt(plaintext []byte) (joined []byte, err error) {
|
func (c *XChaCha20Cipher) Encrypt(plaintext []byte) (joined []byte, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = fmt.Errorf("internal/aead: error encrypting bytes: %v", r)
|
err = fmt.Errorf("cryptutil: error encrypting bytes: %v", r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
nonce := c.GenerateNonce()
|
nonce := c.GenerateNonce()
|
||||||
|
@ -92,7 +92,7 @@ func (c *XChaCha20Cipher) Encrypt(plaintext []byte) (joined []byte, err error) {
|
||||||
// Decrypt a value using XChaCha20-Poly1305
|
// Decrypt a value using XChaCha20-Poly1305
|
||||||
func (c *XChaCha20Cipher) Decrypt(joined []byte) ([]byte, error) {
|
func (c *XChaCha20Cipher) Decrypt(joined []byte) ([]byte, error) {
|
||||||
if len(joined) <= c.aead.NonceSize() {
|
if len(joined) <= c.aead.NonceSize() {
|
||||||
return nil, fmt.Errorf("internal/aead: invalid input size: %d", len(joined))
|
return nil, fmt.Errorf("cryptutil: invalid input size: %d", len(joined))
|
||||||
}
|
}
|
||||||
// grab out the nonce
|
// grab out the nonce
|
||||||
pivot := len(joined) - c.aead.NonceSize()
|
pivot := len(joined) - c.aead.NonceSize()
|
||||||
|
@ -161,13 +161,13 @@ func compress(data []byte) ([]byte, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
writer, err := gzip.NewWriterLevel(&buf, gzip.DefaultCompression)
|
writer, err := gzip.NewWriterLevel(&buf, gzip.DefaultCompression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("internal/aead: failed to create a gzip writer: %q", err)
|
return nil, fmt.Errorf("cryptutil: failed to create a gzip writer: %q", err)
|
||||||
}
|
}
|
||||||
if writer == nil {
|
if writer == nil {
|
||||||
return nil, fmt.Errorf("internal/aead: failed to create a gzip writer")
|
return nil, fmt.Errorf("cryptutil: failed to create a gzip writer")
|
||||||
}
|
}
|
||||||
if _, err = writer.Write(data); err != nil {
|
if _, err = writer.Write(data); err != nil {
|
||||||
return nil, fmt.Errorf("internal/aead: failed to compress data with err: %q", err)
|
return nil, fmt.Errorf("cryptutil: failed to compress data with err: %q", err)
|
||||||
}
|
}
|
||||||
if err = writer.Close(); err != nil {
|
if err = writer.Close(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -178,7 +178,7 @@ func compress(data []byte) ([]byte, error) {
|
||||||
func decompress(data []byte) ([]byte, error) {
|
func decompress(data []byte) ([]byte, error) {
|
||||||
reader, err := gzip.NewReader(bytes.NewReader(data))
|
reader, err := gzip.NewReader(bytes.NewReader(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("internal/aead: failed to create a gzip reader: %q", err)
|
return nil, fmt.Errorf("cryptutil: failed to create a gzip reader: %q", err)
|
||||||
}
|
}
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
func DecodePublicKey(encodedKey []byte) (*ecdsa.PublicKey, error) {
|
func DecodePublicKey(encodedKey []byte) (*ecdsa.PublicKey, error) {
|
||||||
block, _ := pem.Decode(encodedKey)
|
block, _ := pem.Decode(encodedKey)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, fmt.Errorf("marshal: decoded nil PEM block")
|
return nil, fmt.Errorf("cryptutil: decoded nil PEM block")
|
||||||
}
|
}
|
||||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,7 +23,7 @@ func DecodePublicKey(encodedKey []byte) (*ecdsa.PublicKey, error) {
|
||||||
|
|
||||||
ecdsaPub, ok := pub.(*ecdsa.PublicKey)
|
ecdsaPub, ok := pub.(*ecdsa.PublicKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("marshal: data was not an ECDSA public key")
|
return nil, errors.New("cryptutil: data was not an ECDSA public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ecdsaPub, nil
|
return ecdsaPub, nil
|
||||||
|
@ -53,7 +53,7 @@ func DecodePrivateKey(encodedKey []byte) (*ecdsa.PrivateKey, error) {
|
||||||
block, encodedKey = pem.Decode(encodedKey)
|
block, encodedKey = pem.Decode(encodedKey)
|
||||||
|
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, fmt.Errorf("failed to find EC PRIVATE KEY in PEM data after skipping types %v", skippedTypes)
|
return nil, fmt.Errorf("cryptutil: failed to find EC PRIVATE KEY in PEM data after skipping types %v", skippedTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.Type == "EC PRIVATE KEY" {
|
if block.Type == "EC PRIVATE KEY" {
|
||||||
|
|
44
internal/cryptutil/mock_cipher_test.go
Normal file
44
internal/cryptutil/mock_cipher_test.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package cryptutil // import "github.com/pomerium/pomerium/internal/cryptutil"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMockCipher_Unmarshal(t *testing.T) {
|
||||||
|
e := errors.New("err")
|
||||||
|
mc := MockCipher{
|
||||||
|
EncryptResponse: []byte("EncryptResponse"),
|
||||||
|
EncryptError: e,
|
||||||
|
DecryptResponse: []byte("DecryptResponse"),
|
||||||
|
DecryptError: e,
|
||||||
|
MarshalResponse: "MarshalResponse",
|
||||||
|
MarshalError: e,
|
||||||
|
UnmarshalError: e,
|
||||||
|
}
|
||||||
|
b, err := mc.Encrypt([]byte("test"))
|
||||||
|
if string(b) != "EncryptResponse" {
|
||||||
|
t.Error("unexpected encrypt response")
|
||||||
|
}
|
||||||
|
if err != e {
|
||||||
|
t.Error("unexpected encrypt error")
|
||||||
|
}
|
||||||
|
b, err = mc.Decrypt([]byte("test"))
|
||||||
|
if string(b) != "DecryptResponse" {
|
||||||
|
t.Error("unexpected Decrypt response")
|
||||||
|
}
|
||||||
|
if err != e {
|
||||||
|
t.Error("unexpected Decrypt error")
|
||||||
|
}
|
||||||
|
s, err := mc.Marshal("test")
|
||||||
|
if err != e {
|
||||||
|
t.Error("unexpected Marshal error")
|
||||||
|
}
|
||||||
|
if s != "MarshalResponse" {
|
||||||
|
t.Error("unexpected MarshalResponse error")
|
||||||
|
}
|
||||||
|
err = mc.Unmarshal("s", "s")
|
||||||
|
if err != e {
|
||||||
|
t.Error("unexpected Unmarshal error")
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package cryptutil // import "github.com/pomerium/pomerium/internal/cryptutil"
|
package cryptutil // import "github.com/pomerium/pomerium/internal/cryptutil"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
jose "gopkg.in/square/go-jose.v2"
|
jose "gopkg.in/square/go-jose.v2"
|
||||||
|
@ -15,6 +16,9 @@ type JWTSigner interface {
|
||||||
|
|
||||||
// ES256Signer is struct containing the required fields to create a ES256 signed JSON Web Tokens
|
// ES256Signer is struct containing the required fields to create a ES256 signed JSON Web Tokens
|
||||||
type ES256Signer struct {
|
type ES256Signer struct {
|
||||||
|
signer jose.Signer
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
// User (sub) is unique, stable identifier for the user.
|
// User (sub) is unique, stable identifier for the user.
|
||||||
// Use in place of the x-pomerium-authenticated-user-id header.
|
// Use in place of the x-pomerium-authenticated-user-id header.
|
||||||
User string `json:"sub,omitempty"`
|
User string `json:"sub,omitempty"`
|
||||||
|
@ -42,8 +46,6 @@ type ES256Signer struct {
|
||||||
// IssuedAt (nbf) is the time is measured in seconds since the UNIX epoch.
|
// IssuedAt (nbf) is the time is measured in seconds since the UNIX epoch.
|
||||||
// Allow 1 minute for skew.
|
// Allow 1 minute for skew.
|
||||||
NotBefore jwt.NumericDate `json:"nbf,omitempty"`
|
NotBefore jwt.NumericDate `json:"nbf,omitempty"`
|
||||||
|
|
||||||
signer jose.Signer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewES256Signer creates an Elliptic Curve, NIST P-256 (aka secp256r1 aka prime256v1) JWT signer.
|
// NewES256Signer creates an Elliptic Curve, NIST P-256 (aka secp256r1 aka prime256v1) JWT signer.
|
||||||
|
@ -56,7 +58,7 @@ type ES256Signer struct {
|
||||||
func NewES256Signer(privKey []byte, audience string) (*ES256Signer, error) {
|
func NewES256Signer(privKey []byte, audience string) (*ES256Signer, error) {
|
||||||
key, err := DecodePrivateKey(privKey)
|
key, err := DecodePrivateKey(privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("internal/cryptutil parsing key failed %v", err)
|
return nil, fmt.Errorf("cryptutil: parsing key failed %v", err)
|
||||||
}
|
}
|
||||||
signer, err := jose.NewSigner(
|
signer, err := jose.NewSigner(
|
||||||
jose.SigningKey{
|
jose.SigningKey{
|
||||||
|
@ -65,7 +67,7 @@ func NewES256Signer(privKey []byte, audience string) (*ES256Signer, error) {
|
||||||
},
|
},
|
||||||
(&jose.SignerOptions{}).WithType("JWT"))
|
(&jose.SignerOptions{}).WithType("JWT"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("internal/cryptutil new signer failed %v", err)
|
return nil, fmt.Errorf("cryptutil: new signer failed %v", err)
|
||||||
}
|
}
|
||||||
return &ES256Signer{
|
return &ES256Signer{
|
||||||
Issuer: "pomerium-proxy",
|
Issuer: "pomerium-proxy",
|
||||||
|
@ -77,6 +79,8 @@ func NewES256Signer(privKey []byte, audience string) (*ES256Signer, error) {
|
||||||
// SignJWT creates a signed JWT containing claims for the logged in
|
// SignJWT creates a signed JWT containing claims for the logged in
|
||||||
// user id (`sub`), email (`email`) and groups (`groups`).
|
// user id (`sub`), email (`email`) and groups (`groups`).
|
||||||
func (s *ES256Signer) SignJWT(user, email, groups string) (string, error) {
|
func (s *ES256Signer) SignJWT(user, email, groups string) (string, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
s.User = user
|
s.User = user
|
||||||
s.Email = email
|
s.Email = email
|
||||||
s.Groups = groups
|
s.Groups = groups
|
||||||
|
@ -86,7 +90,7 @@ func (s *ES256Signer) SignJWT(user, email, groups string) (string, error) {
|
||||||
s.NotBefore = *jwt.NewNumericDate(now.Add(-1 * jwt.DefaultLeeway))
|
s.NotBefore = *jwt.NewNumericDate(now.Add(-1 * jwt.DefaultLeeway))
|
||||||
rawJWT, err := jwt.Signed(s.signer).Claims(s).CompactSerialize()
|
rawJWT, err := jwt.Signed(s.signer).Claims(s).CompactSerialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", fmt.Errorf("cryptutil: sign failed %v", err)
|
||||||
}
|
}
|
||||||
return rawJWT, nil
|
return rawJWT, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue