feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
148
vendor/github.com/open-policy-agent/opa/internal/providers/aws/ecr.go
generated
vendored
Normal file
148
vendor/github.com/open-policy-agent/opa/internal/providers/aws/ecr.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/version"
|
||||
"github.com/open-policy-agent/opa/logging"
|
||||
)
|
||||
|
||||
// Values taken from
|
||||
// https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_GetAuthorizationToken.html
|
||||
const (
|
||||
ecrGetAuthorizationTokenTarget = "AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken"
|
||||
ecrEndpointFmt = "https://ecr.%s.amazonaws.com/"
|
||||
)
|
||||
|
||||
// ECR is used to request tokens from Elastic Container Registry.
|
||||
type ECR struct {
|
||||
// endpoint returns the region-specifc ECR endpoint.
|
||||
// It can be overridden by tests.
|
||||
endpoint func(region string) string
|
||||
|
||||
// client is used to send authorization tokens requests.
|
||||
client *http.Client
|
||||
|
||||
logger logging.Logger
|
||||
}
|
||||
|
||||
func NewECR(logger logging.Logger) *ECR {
|
||||
return &ECR{
|
||||
endpoint: func(region string) string {
|
||||
return fmt.Sprintf(ecrEndpointFmt, region)
|
||||
},
|
||||
client: &http.Client{},
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// GetAuthorizationToken requests a token that can be used to authenticate image pull requests.
|
||||
func (e *ECR) GetAuthorizationToken(ctx context.Context, creds Credentials, signatureVersion string) (ECRAuthorizationToken, error) {
|
||||
endpoint := e.endpoint(creds.RegionName)
|
||||
body := strings.NewReader("{}")
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, body)
|
||||
if err != nil {
|
||||
return ECRAuthorizationToken{}, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("X-Amz-Target", ecrGetAuthorizationTokenTarget)
|
||||
req.Header.Set("Accept-Encoding", "identity")
|
||||
req.Header.Set("Content-Type", "application/x-amz-json-1.1")
|
||||
req.Header.Set("User-Agent", version.UserAgent)
|
||||
|
||||
e.logger.Debug("Signing ECR authorization token request")
|
||||
|
||||
if err := SignRequest(req, "ecr", creds, time.Now(), signatureVersion); err != nil {
|
||||
return ECRAuthorizationToken{}, fmt.Errorf("failed to sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := DoRequestWithClient(req, e.client, "ecr get authorization token", e.logger)
|
||||
if err != nil {
|
||||
return ECRAuthorizationToken{}, err
|
||||
}
|
||||
|
||||
var data struct {
|
||||
AuthorizationData []struct {
|
||||
AuthorizationToken string `json:"authorizationToken"`
|
||||
ExpiresAt json.Number `json:"expiresAt"`
|
||||
} `json:"authorizationData"`
|
||||
}
|
||||
if err := json.Unmarshal(resp, &data); err != nil {
|
||||
return ECRAuthorizationToken{}, fmt.Errorf("failed to unmarshal response: %w", err)
|
||||
}
|
||||
|
||||
if len(data.AuthorizationData) < 1 {
|
||||
return ECRAuthorizationToken{}, errors.New("empty authorization data")
|
||||
}
|
||||
|
||||
// The GetAuthorizationToken request returns a list of tokens for
|
||||
// backwards compatibility reasons. We should only ever get one token back
|
||||
// because we don't define any registryIDs in the request.
|
||||
// See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_GetAuthorizationToken.html#API_GetAuthorizationToken_ResponseSyntax
|
||||
resultToken := data.AuthorizationData[0]
|
||||
|
||||
expiresAt, err := parseTimestamp(resultToken.ExpiresAt)
|
||||
if err != nil {
|
||||
return ECRAuthorizationToken{}, fmt.Errorf("failed to parse expiresAt: %w", err)
|
||||
}
|
||||
|
||||
return ECRAuthorizationToken{
|
||||
AuthorizationToken: resultToken.AuthorizationToken,
|
||||
ExpiresAt: expiresAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ECRAuthorizationToken can sign requests to AWS ECR.
|
||||
//
|
||||
// It corresponds to data returned by the AWS GetAuthorizationToken API.
|
||||
// See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_AuthorizationData.html
|
||||
type ECRAuthorizationToken struct {
|
||||
AuthorizationToken string
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// IsValid returns true if the token is set and not expired.
|
||||
// It respects a margin of error for time handling and will mark it as expired early.
|
||||
func (t *ECRAuthorizationToken) IsValid() bool {
|
||||
const tokenExpirationMargin = 5 * time.Minute
|
||||
|
||||
expired := time.Now().Add(tokenExpirationMargin).After(t.ExpiresAt)
|
||||
return t.AuthorizationToken != "" && !expired
|
||||
}
|
||||
|
||||
var millisecondsFloat = new(big.Float).SetInt64(1e3)
|
||||
|
||||
// parseTimestamp parses the AWS format for timestamps.
|
||||
// The time precision is in milliseconds.
|
||||
//
|
||||
// The logic is taken from
|
||||
// https://github.com/aws/aws-sdk-go/blob/41717ba2c04d3fd03f94d09ea984a10899574935/private/protocol/json/jsonutil/unmarshal.go#L294-L302
|
||||
func parseTimestamp(raw json.Number) (time.Time, error) {
|
||||
s := raw.String()
|
||||
|
||||
float, ok := new(big.Float).SetString(s)
|
||||
if !ok {
|
||||
return time.Time{}, fmt.Errorf("not a float: %q", raw)
|
||||
}
|
||||
|
||||
// The float is expected to be in second resolution with millisecond
|
||||
// decimal places.
|
||||
// Multiply by millisecondsFloat to obtain an integer in millisecond
|
||||
// resolution
|
||||
ms, _ := float.Mul(float, millisecondsFloat).Int64()
|
||||
|
||||
// Multiply again to obtain nanosecond resolution for time.Unix
|
||||
ns := ms * 1e6
|
||||
|
||||
t := time.Unix(0, ns).UTC()
|
||||
|
||||
return t, nil
|
||||
}
|
||||
106
vendor/github.com/open-policy-agent/opa/internal/providers/aws/kms.go
generated
vendored
Normal file
106
vendor/github.com/open-policy-agent/opa/internal/providers/aws/kms.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/version"
|
||||
"github.com/open-policy-agent/opa/logging"
|
||||
)
|
||||
|
||||
// Values taken from
|
||||
// https://docs.aws.amazon.com/kms/latest/APIReference/Welcome.html
|
||||
// https://docs.aws.amazon.com/general/latest/gr/kms.html
|
||||
const (
|
||||
kmsSignTarget = "TrentService.Sign"
|
||||
kmsEndpointFmt = "https://kms.%s.amazonaws.com/"
|
||||
)
|
||||
|
||||
// KMS is used to sign payloads using AWS Key Management Service.
|
||||
type KMS struct {
|
||||
// endpoint returns the region-specifc KMS endpoint.
|
||||
// It can be overridden by tests.
|
||||
endpoint func(region string) string
|
||||
|
||||
// client is used to send authorization tokens requests.
|
||||
client *http.Client
|
||||
|
||||
logger logging.Logger
|
||||
}
|
||||
|
||||
func NewKMS(logger logging.Logger) *KMS {
|
||||
return &KMS{
|
||||
endpoint: func(region string) string {
|
||||
return fmt.Sprintf(kmsEndpointFmt, region)
|
||||
},
|
||||
client: &http.Client{},
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func NewKMSWithURLClient(url string, client *http.Client, logger logging.Logger) *KMS {
|
||||
return &KMS{
|
||||
endpoint: func(string) string { return url },
|
||||
client: client,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
type KMSSignRequest struct {
|
||||
KeyID string `json:"KeyId"`
|
||||
Message string `json:"Message"`
|
||||
MessageType string `json:"MessageType"`
|
||||
SigningAlgorithm string `json:"SigningAlgorithm"`
|
||||
}
|
||||
type KMSSignResponse struct {
|
||||
KeyID string `json:"KeyId"`
|
||||
Signature string `json:"Signature"`
|
||||
SigningAlgorithm string `json:"SigningAlgorithm"`
|
||||
}
|
||||
|
||||
// SignDigest signs a digest using KMS.
|
||||
func (k *KMS) SignDigest(ctx context.Context, digest []byte, keyID string, signingAlgorithm string, creds Credentials, signatureVersion string) (string, error) {
|
||||
endpoint := k.endpoint(creds.RegionName)
|
||||
|
||||
kmsRequest := KMSSignRequest{
|
||||
KeyID: keyID,
|
||||
Message: base64.StdEncoding.EncodeToString(digest),
|
||||
MessageType: "DIGEST",
|
||||
SigningAlgorithm: signingAlgorithm,
|
||||
}
|
||||
requestJSONBytes, err := json.Marshal(kmsRequest)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshall request: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer(requestJSONBytes))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("X-Amz-Target", kmsSignTarget)
|
||||
req.Header.Set("Accept-Encoding", "identity")
|
||||
req.Header.Set("Content-Type", "application/x-amz-json-1.1")
|
||||
req.Header.Set("User-Agent", version.UserAgent)
|
||||
|
||||
if err := SignRequest(req, "kms", creds, time.Now(), signatureVersion); err != nil {
|
||||
return "", fmt.Errorf("failed to sign request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := DoRequestWithClient(req, k.client, "kms sign digest", k.logger)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var data KMSSignResponse
|
||||
if err := json.Unmarshal(resp, &data); err != nil {
|
||||
return "", fmt.Errorf("failed to unmarshal response: %w", err)
|
||||
}
|
||||
|
||||
return data.Signature, nil
|
||||
}
|
||||
42
vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4.go
generated
vendored
42
vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4.go
generated
vendored
@@ -5,9 +5,13 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -74,6 +78,42 @@ func sortKeys(strMap map[string][]string) []string {
|
||||
return keys
|
||||
}
|
||||
|
||||
// SignRequest modifies an http.Request to include an AWS V4 signature based on the provided credentials.
|
||||
func SignRequest(req *http.Request, service string, creds Credentials, theTime time.Time, sigVersion string) error {
|
||||
// General ref. https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
|
||||
// S3 ref. https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html
|
||||
// APIGateway ref. https://docs.aws.amazon.com/apigateway/api-reference/signing-requests/
|
||||
|
||||
var body []byte
|
||||
if req.Body == nil {
|
||||
body = []byte("")
|
||||
} else {
|
||||
var err error
|
||||
body, err = io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return errors.New("error getting request body: " + err.Error())
|
||||
}
|
||||
// Since ReadAll consumed the body ReadCloser, we must create a new ReadCloser for the request so that the
|
||||
// subsequent read starts from the beginning
|
||||
req.Body = io.NopCloser(bytes.NewReader(body))
|
||||
}
|
||||
|
||||
now := theTime.UTC()
|
||||
|
||||
if sigVersion == "4a" {
|
||||
signedHeaders := SignV4a(req.Header, req.Method, req.URL, body, service, creds, now)
|
||||
req.Header = signedHeaders
|
||||
} else {
|
||||
authHeader, awsHeaders := SignV4(req.Header, req.Method, req.URL, body, service, creds, now)
|
||||
req.Header.Set("Authorization", authHeader)
|
||||
for k, v := range awsHeaders {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignV4 modifies a map[string][]string of headers to generate an AWS V4 signature + headers based on the config/credentials provided.
|
||||
func SignV4(headers map[string][]string, method string, theURL *url.URL, body []byte, service string, awsCreds Credentials, theTime time.Time) (string, map[string]string) {
|
||||
// General ref. https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
|
||||
@@ -118,7 +158,7 @@ func SignV4(headers map[string][]string, method string, theURL *url.URL, body []
|
||||
}
|
||||
|
||||
// the "canonical request" is the normalized version of the AWS service access
|
||||
// that we're attempting to perform; in this case, a GET from an S3 bucket
|
||||
// that we're attempting to perform
|
||||
canonicalReq := method + "\n" // HTTP method
|
||||
canonicalReq += theURL.EscapedPath() + "\n" // URI-escaped path
|
||||
canonicalReq += theURL.RawQuery + "\n" // RAW Query String
|
||||
|
||||
45
vendor/github.com/open-policy-agent/opa/internal/providers/aws/util.go
generated
vendored
Normal file
45
vendor/github.com/open-policy-agent/opa/internal/providers/aws/util.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/open-policy-agent/opa/logging"
|
||||
)
|
||||
|
||||
// DoRequestWithClient is a convenience function to get the body of an http response with
|
||||
// appropriate error-handling boilerplate and logging.
|
||||
func DoRequestWithClient(req *http.Request, client *http.Client, desc string, logger logging.Logger) ([]byte, error) {
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
// some kind of catastrophe talking to the service
|
||||
return nil, errors.New(desc + " HTTP request failed: " + err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
logger.WithFields(map[string]interface{}{
|
||||
"url": req.URL.String(),
|
||||
"status": resp.Status,
|
||||
"headers": resp.Header,
|
||||
}).Debug("Received response from " + desc + " service.")
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
if logger.GetLevel() == logging.Debug {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err == nil {
|
||||
logger.Debug("Error response with response body: %s", body)
|
||||
}
|
||||
}
|
||||
// could be 404 for role that's not available, but cover all the bases
|
||||
return nil, errors.New(desc + " HTTP request returned unexpected status: " + resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
// deal with problems reading the body, whatever those might be
|
||||
return nil, errors.New(desc + " HTTP response body could not be read: " + err.Error())
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
Reference in New Issue
Block a user