Skip to content

Commit

Permalink
x-pack/filebeat/input/{cel,httpjson}: fix PEM key validation (#38405)
Browse files Browse the repository at this point in the history
Previously the validation was attempting to parse the PEM text as a key
and was also attempting to parse the data as the wrong kind of key.

(cherry picked from commit c29075e)
  • Loading branch information
efd6 committed Mar 19, 2024
1 parent f3f8824 commit 124f31c
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 17 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ fields added to events containing the Beats version. {pull}37553[37553]
- Fix "failed processing S3 event for object key" error on aws-s3 input when key contains the "+" character {issue}38012[38012] {pull}38125[38125]
- Fix duplicated addition of regexp extension in CEL input. {pull}38181[38181]
- Fix HTTPJSON handling of empty object bodies in POST requests. {issue}33961[33961] {pull}38290[38290]
- Fix the incorrect values generated by the uri_parts processor. {pull}38216[38216]
- Fix HTTPJSON handling of empty object bodies in POST requests. {issue}33961[33961] {pull}38290[38290]
- Fix PEM key validation for CEL and HTTPJSON inputs. {pull}38405[38405]

*Heartbeat*

Expand Down
6 changes: 4 additions & 2 deletions x-pack/filebeat/input/cel/config_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package cel

import (
"context"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -341,7 +340,10 @@ func (o *oAuth2Config) validateOktaProvider() error {
}
// jwk_pem
if o.OktaJWKPEM != "" {
_, err := x509.ParsePKCS1PrivateKey([]byte(o.OktaJWKPEM))
_, err := pemPKCS8PrivateKey([]byte(o.OktaJWKPEM))
if err != nil {
return fmt.Errorf("okta validation error: %w", err)
}
return err
}
// jwk_file
Expand Down
20 changes: 14 additions & 6 deletions x-pack/filebeat/input/cel/config_okta_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"math/big"
"net/http"
Expand Down Expand Up @@ -160,17 +161,24 @@ func (i *base64int) UnmarshalJSON(b []byte) error {
}

func generateOktaJWTPEM(pemdata string, cnf *oauth2.Config) (string, error) {
blk, rest := pem.Decode([]byte(pemdata))
if rest := bytes.TrimSpace(rest); len(rest) != 0 {
return "", fmt.Errorf("PEM text has trailing data: %s", rest)
}
key, err := x509.ParsePKCS8PrivateKey(blk.Bytes)
key, err := pemPKCS8PrivateKey([]byte(pemdata))
if err != nil {
return "", err
}
return signJWT(cnf, key)
}

func pemPKCS8PrivateKey(pemdata []byte) (any, error) {
blk, rest := pem.Decode(pemdata)
if rest := bytes.TrimSpace(rest); len(rest) != 0 {
return nil, fmt.Errorf("PEM text has trailing data: %d bytes", len(rest))
}
if blk == nil {
return nil, errors.New("no PEM data")
}
return x509.ParsePKCS8PrivateKey(blk.Bytes)
}

// signJWT creates a JWT token using required claims and sign it with the
// private key.
func signJWT(cnf *oauth2.Config, key any) (string, error) {
Expand All @@ -182,7 +190,7 @@ func signJWT(cnf *oauth2.Config, key any) (string, error) {
Expiration(now.Add(time.Hour)).
Build()
if err != nil {
return "", err
return "", fmt.Errorf("failed to create token: %w", err)
}
signedToken, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, key))
if err != nil {
Expand Down
41 changes: 41 additions & 0 deletions x-pack/filebeat/input/cel/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,47 @@ var oAuth2ValidationTests = []struct {
},
},
},
{
name: "okta_successful_pem_oauth2_validation",
input: map[string]interface{}{
"auth.oauth2": map[string]interface{}{
"provider": "okta",
"client.id": "a_client_id",
"token_url": "localhost",
"scopes": []string{"foo"},
"okta.jwk_pem": `
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCOuef3HMRhohVT
5kSoAJgV+atpDjkwTwkOq+ImnbBlv75GaApG90w8VpjXjhqN/1KJmwfyrKiquiMq
OPu+o/672Dys5rUAaWSbT7wRF1GjLDDZrM0GHRdV4DGxM/LKI8I5yE1Mx3EzV+D5
ZLmcRc5U4oEoMwtGpr0zRZ7uUr6a28UQwcUsVIPItc1/9rERlo1WTv8dcaj4ECC3
2Sc0y/F+9XqwJvLd4Uv6ckzP0Sv4tbDA+7jpD9MneAIUiZ4LVj2cwbBd+YRY6jXx
MkevcCSmSX60clBY1cIFkw1DYHqtdHEwAQcQHLGMoi72xRP2qrdzIPsaTKVYoHVo
WA9vADdHAgMBAAECggEAIlx7jjCsztyYyeQsL05FTzUWoWo9NnYwtgmHnshkCXsK
MiUmJEOxZO1sSqj5l6oakupyFWigCspZYPbrFNCiqVK7+NxqQzkccY/WtT6p9uDS
ufUyPwCN96zMCd952lSVlBe3FH8Hr9a+YQxw60CbFjCZ67WuR0opTsi6JKJjJSDb
TQQZ4qJR97D05I1TgfmO+VO7G/0/dDaNHnnlYz0AnOgZPSyvrU2G5cYye4842EMB
ng81xjHD+xp55JNui/xYkhmYspYhrB2KlEjkKb08OInUjBeaLEAgA1r9yOHsfV/3
DQzDPRO9iuqx5BfJhdIqUB1aifrye+sbxt9uMBtUgQKBgQDVdfO3GYT+ZycOQG9P
QtdMn6uiSddchVCGFpk331u6M6yafCKjI/MlJDl29B+8R5sVsttwo8/qnV/xd3cn
pY14HpKAsE4l6/Ciagzoj+0NqfPEDhEzbo8CyArcd7pSxt3XxECAfZe2+xivEPHe
gFO60vSFjFtvlLRMDMOmqX3kYQKBgQCrK1DISyQTnD6/axsgh2/ESOmT7n+JRMx/
YzA7Lxu3zGzUC8/sRDa1C41t054nf5ZXJueYLDSc4kEAPddzISuCLxFiTD2FQ75P
lHWMgsEzQObDm4GPE9cdKOjoAvtAJwbvZcjDa029CDx7aCaDzbNvdmplZ7EUrznR
55U8Wsm8pwKBgBytxTmzZwfbCgdDJvFKNKzpwuCB9TpL+v6Y6Kr2Clfg+26iAPFU
MiWqUUInGGBuamqm5g6jI5sM28gQWeTsvC4IRXyes1Eq+uCHSQax15J/Y+3SSgNT
9kjUYYkvWMwoRcPobRYWSZze7XkP2L8hFJ7EGvAaZGqAWxzgliS9HtnhAoGAONZ/
UqMw7Zoac/Ga5mhSwrj7ZvXxP6Gqzjofj+eKqrOlB5yMhIX6LJATfH6iq7cAMxxm
Fu/G4Ll4oB3o5wACtI3wldV/MDtYfJBtoCTjBqPsfNOsZ9hMvBATlsc2qwzKjsAb
tFhzTevoOYpSD75EcSS/G8Ec2iN9bagatBnpl00CgYBVqAOFZelNfP7dj//lpk8y
EUAw7ABOq0S9wkpFWTXIVPoBQUipm3iAUqGNPmvr/9ShdZC9xeu5AwKram4caMWJ
ExRhcDP1hFM6CdmSkIYEgBKvN9N0O4Lx1ba34gk74Hm65KXxokjJHOC0plO7c7ok
LNV/bIgMHOMoxiGrwyjAhg==
-----END PRIVATE KEY-----
`,
},
},
},
}

func TestConfigOauth2Validation(t *testing.T) {
Expand Down
8 changes: 5 additions & 3 deletions x-pack/filebeat/input/httpjson/config_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package httpjson

import (
"context"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -309,8 +308,11 @@ func (o *oAuth2Config) validateOktaProvider() error {
}
// jwk_pem
if o.OktaJWKPEM != "" {
_, err := x509.ParsePKCS1PrivateKey([]byte(o.OktaJWKPEM))
return err
_, err := pemPKCS8PrivateKey([]byte(o.OktaJWKPEM))
if err != nil {
return fmt.Errorf("okta validation error: %w", err)
}
return nil
}
// jwk_file
if o.OktaJWKFile != "" {
Expand Down
20 changes: 14 additions & 6 deletions x-pack/filebeat/input/httpjson/config_okta_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"math/big"
"net/http"
Expand Down Expand Up @@ -158,17 +159,24 @@ func (i *base64int) UnmarshalJSON(b []byte) error {
}

func generateOktaJWTPEM(pemdata string, cnf *oauth2.Config) (string, error) {
blk, rest := pem.Decode([]byte(pemdata))
if rest := bytes.TrimSpace(rest); len(rest) != 0 {
return "", fmt.Errorf("PEM text has trailing data: %s", rest)
}
key, err := x509.ParsePKCS8PrivateKey(blk.Bytes)
key, err := pemPKCS8PrivateKey([]byte(pemdata))
if err != nil {
return "", err
}
return signJWT(cnf, key)
}

func pemPKCS8PrivateKey(pemdata []byte) (any, error) {
blk, rest := pem.Decode(pemdata)
if rest := bytes.TrimSpace(rest); len(rest) != 0 {
return nil, fmt.Errorf("PEM text has trailing data: %d bytes", len(rest))
}
if blk == nil {
return nil, errors.New("no PEM data")
}
return x509.ParsePKCS8PrivateKey(blk.Bytes)
}

// signJWT creates a JWT token using required claims and sign it with the private key.
func signJWT(cnf *oauth2.Config, key any) (string, error) {
now := time.Now()
Expand All @@ -179,7 +187,7 @@ func signJWT(cnf *oauth2.Config, key any) (string, error) {
Expiration(now.Add(time.Hour)).
Build()
if err != nil {
return "", err
return "", fmt.Errorf("failed to create token: %w", err)
}
signedToken, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, key))
if err != nil {
Expand Down
41 changes: 41 additions & 0 deletions x-pack/filebeat/input/httpjson/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,47 @@ func TestConfigOauth2Validation(t *testing.T) {
},
},
},
{
name: "okta successful pem oauth2 validation",
input: map[string]interface{}{
"auth.oauth2": map[string]interface{}{
"provider": "okta",
"client.id": "a_client_id",
"token_url": "localhost",
"scopes": []string{"foo"},
"okta.jwk_pem": `
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCOuef3HMRhohVT
5kSoAJgV+atpDjkwTwkOq+ImnbBlv75GaApG90w8VpjXjhqN/1KJmwfyrKiquiMq
OPu+o/672Dys5rUAaWSbT7wRF1GjLDDZrM0GHRdV4DGxM/LKI8I5yE1Mx3EzV+D5
ZLmcRc5U4oEoMwtGpr0zRZ7uUr6a28UQwcUsVIPItc1/9rERlo1WTv8dcaj4ECC3
2Sc0y/F+9XqwJvLd4Uv6ckzP0Sv4tbDA+7jpD9MneAIUiZ4LVj2cwbBd+YRY6jXx
MkevcCSmSX60clBY1cIFkw1DYHqtdHEwAQcQHLGMoi72xRP2qrdzIPsaTKVYoHVo
WA9vADdHAgMBAAECggEAIlx7jjCsztyYyeQsL05FTzUWoWo9NnYwtgmHnshkCXsK
MiUmJEOxZO1sSqj5l6oakupyFWigCspZYPbrFNCiqVK7+NxqQzkccY/WtT6p9uDS
ufUyPwCN96zMCd952lSVlBe3FH8Hr9a+YQxw60CbFjCZ67WuR0opTsi6JKJjJSDb
TQQZ4qJR97D05I1TgfmO+VO7G/0/dDaNHnnlYz0AnOgZPSyvrU2G5cYye4842EMB
ng81xjHD+xp55JNui/xYkhmYspYhrB2KlEjkKb08OInUjBeaLEAgA1r9yOHsfV/3
DQzDPRO9iuqx5BfJhdIqUB1aifrye+sbxt9uMBtUgQKBgQDVdfO3GYT+ZycOQG9P
QtdMn6uiSddchVCGFpk331u6M6yafCKjI/MlJDl29B+8R5sVsttwo8/qnV/xd3cn
pY14HpKAsE4l6/Ciagzoj+0NqfPEDhEzbo8CyArcd7pSxt3XxECAfZe2+xivEPHe
gFO60vSFjFtvlLRMDMOmqX3kYQKBgQCrK1DISyQTnD6/axsgh2/ESOmT7n+JRMx/
YzA7Lxu3zGzUC8/sRDa1C41t054nf5ZXJueYLDSc4kEAPddzISuCLxFiTD2FQ75P
lHWMgsEzQObDm4GPE9cdKOjoAvtAJwbvZcjDa029CDx7aCaDzbNvdmplZ7EUrznR
55U8Wsm8pwKBgBytxTmzZwfbCgdDJvFKNKzpwuCB9TpL+v6Y6Kr2Clfg+26iAPFU
MiWqUUInGGBuamqm5g6jI5sM28gQWeTsvC4IRXyes1Eq+uCHSQax15J/Y+3SSgNT
9kjUYYkvWMwoRcPobRYWSZze7XkP2L8hFJ7EGvAaZGqAWxzgliS9HtnhAoGAONZ/
UqMw7Zoac/Ga5mhSwrj7ZvXxP6Gqzjofj+eKqrOlB5yMhIX6LJATfH6iq7cAMxxm
Fu/G4Ll4oB3o5wACtI3wldV/MDtYfJBtoCTjBqPsfNOsZ9hMvBATlsc2qwzKjsAb
tFhzTevoOYpSD75EcSS/G8Ec2iN9bagatBnpl00CgYBVqAOFZelNfP7dj//lpk8y
EUAw7ABOq0S9wkpFWTXIVPoBQUipm3iAUqGNPmvr/9ShdZC9xeu5AwKram4caMWJ
ExRhcDP1hFM6CdmSkIYEgBKvN9N0O4Lx1ba34gk74Hm65KXxokjJHOC0plO7c7ok
LNV/bIgMHOMoxiGrwyjAhg==
-----END PRIVATE KEY-----
`,
},
},
},
}

for _, c := range cases {
Expand Down

0 comments on commit 124f31c

Please sign in to comment.