[feat]: [AH-724]: ECR upstream proxy support (#3181)

* [fix]: [AH-724]: pr checks
* [fix]: [AH-724]: pr checks
* [fix]: [AH-724]: pr checks
* [fix]: [AH-724]: fix upstream proxy update
* [fix]: [AH-724]: fix upstream proxy update
* [fix]: [AH-724]: fix upstream proxy update
* Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into AH-724-schema-change
* [feat]: [AH-724]: ECR upstream proxy support
BT-10437
Pragyesh Mishra 2025-01-06 07:59:59 +00:00 committed by Harness
parent 0ac9a3aac1
commit 016a0ce2f5
19 changed files with 719 additions and 179 deletions

View File

@ -460,6 +460,10 @@ issues:
linters: [ goheader ]
- path: "^registry/app/remote/adapter/dockerhub/adapter.go"
linters: [ goheader ]
- path: "^registry/app/remote/adapter/awsecr/adapter.go"
linters: [ goheader ]
- path: "^registry/app/remote/adapter/awsecr/auth.go"
linters: [ goheader ]
- path: "^registry/app/remote/adapter/dockerhub/client.go"
linters: [ goheader ]
- path: "^registry/app/remote/adapter/dockerhub/consts.go"

View File

@ -0,0 +1,2 @@
ALTER TABLE upstream_proxy_configs DROP COLUMN upstream_proxy_config_user_name_secret_space_id;
ALTER TABLE upstream_proxy_configs DROP COLUMN upstream_proxy_config_user_name_secret_identifier;

View File

@ -0,0 +1,2 @@
ALTER TABLE upstream_proxy_configs ADD COLUMN upstream_proxy_config_user_name_secret_space_id INTEGER;
ALTER TABLE upstream_proxy_configs ADD COLUMN upstream_proxy_config_user_name_secret_identifier TEXT;

View File

@ -0,0 +1,2 @@
ALTER TABLE upstream_proxy_configs DROP COLUMN upstream_proxy_config_user_name_secret_space_id;
ALTER TABLE upstream_proxy_configs DROP COLUMN upstream_proxy_config_user_name_secret_identifier;

View File

@ -0,0 +1,2 @@
ALTER TABLE upstream_proxy_configs ADD COLUMN upstream_proxy_config_user_name_secret_space_id INTEGER;
ALTER TABLE upstream_proxy_configs ADD COLUMN upstream_proxy_config_user_name_secret_identifier TEXT;

View File

@ -364,6 +364,18 @@ func CreateUpstreamProxyResponseJSONResponse(upstreamproxy *types.UpstreamProxy)
auth.SecretIdentifier = &upstreamproxy.SecretIdentifier
auth.SecretSpacePath = &upstreamproxy.SecretSpacePath
_ = configAuth.FromUserPassword(auth)
} else if api.AuthType(upstreamproxy.RepoAuthType) == api.AuthTypeAccessKeySecretKey {
auth := api.AccessKeySecretKey{}
auth.AccessKey = &upstreamproxy.UserName
auth.AccessKeySecretIdentifier = &upstreamproxy.UserNameSecretIdentifier
auth.AccessKeySecretSpacePath = &upstreamproxy.UserNameSecretSpacePath
auth.SecretKeyIdentifier = upstreamproxy.SecretIdentifier
auth.SecretKeySpacePath = &upstreamproxy.SecretSpacePath
err := configAuth.FromAccessKeySecretKey(auth)
if err != nil {
log.Warn().Msgf("error in converting auth config to access and secret key: %v", err)
return &api.RegistryResponseJSONResponse{}
}
}
source := api.UpstreamConfigSource(upstreamproxy.Source)

View File

@ -267,6 +267,7 @@ func CreateRegistryEntity(
return entity, nil
}
//nolint:gocognit,cyclop
func (c *APIController) CreateUpstreamProxyEntity(
ctx context.Context, dto artifact.RegistryRequest, parentID int64, rootParentID int64,
) (*registrytypes.Registry, *registrytypes.UpstreamProxyConfig, error) {
@ -316,6 +317,7 @@ func (c *APIController) CreateUpstreamProxyEntity(
}
upstreamProxyConfigEntity.Source = string(*config.Source)
}
//nolint:nestif
if config.AuthType == artifact.AuthTypeUserPassword {
res, err := config.Auth.AsUserPassword()
if err != nil {
@ -326,12 +328,47 @@ func (c *APIController) CreateUpstreamProxyEntity(
return nil, nil, fmt.Errorf("failed to create upstream proxy: secret_identifier missing")
}
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretSpacePath)
if err != nil {
return nil, nil, err
if res.SecretSpacePath != nil && len(*res.SecretSpacePath) > 0 {
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretSpacePath)
if err != nil {
return nil, nil, err
}
} else if res.SecretSpaceId != nil {
upstreamProxyConfigEntity.SecretSpaceID = *res.SecretSpaceId
}
upstreamProxyConfigEntity.SecretIdentifier = *res.SecretIdentifier
} else if config.AuthType == artifact.AuthTypeAccessKeySecretKey {
res, err := config.Auth.AsAccessKeySecretKey()
if err != nil {
return nil, nil, err
}
switch {
case res.AccessKey != nil && len(*res.AccessKey) > 0:
upstreamProxyConfigEntity.UserName = *res.AccessKey
case res.AccessKeySecretIdentifier == nil:
return nil, nil, fmt.Errorf("failed to create upstream proxy: access_key_secret_identifier missing")
default:
if res.AccessKeySecretSpacePath != nil && len(*res.AccessKeySecretSpacePath) > 0 {
upstreamProxyConfigEntity.UserNameSecretSpaceID, err = c.getSecretID(ctx, res.AccessKeySecretSpacePath)
if err != nil {
return nil, nil, err
}
} else if res.AccessKeySecretSpaceId != nil {
upstreamProxyConfigEntity.UserNameSecretSpaceID = *res.AccessKeySecretSpaceId
}
upstreamProxyConfigEntity.UserNameSecretIdentifier = *res.AccessKeySecretIdentifier
}
if res.SecretKeySpacePath != nil && len(*res.SecretKeySpacePath) > 0 {
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretKeySpacePath)
if err != nil {
return nil, nil, err
}
} else if res.SecretKeySpaceId != nil {
upstreamProxyConfigEntity.SecretSpaceID = *res.SecretKeySpaceId
}
upstreamProxyConfigEntity.SecretIdentifier = res.SecretKeyIdentifier
}
return repoEntity, upstreamProxyConfigEntity, nil
}

View File

@ -328,6 +328,7 @@ func UpdateRepoEntity(
return entity, nil
}
//nolint:gocognit,cyclop
func (c *APIController) UpdateUpstreamProxyEntity(
ctx context.Context, dto artifact.RegistryRequest, parentID int64, rootParentID int64, u *types.UpstreamProxy,
) (*types.Registry, *types.UpstreamProxyConfig, error) {
@ -379,7 +380,8 @@ func (c *APIController) UpdateUpstreamProxyEntity(
if u.ID != -1 {
upstreamProxyConfigEntity.ID = u.ID
}
if config.AuthType == artifact.AuthTypeUserPassword {
switch {
case config.AuthType == artifact.AuthTypeUserPassword:
res, err := config.Auth.AsUserPassword()
if err != nil {
return nil, nil, err
@ -389,12 +391,47 @@ func (c *APIController) UpdateUpstreamProxyEntity(
return nil, nil, fmt.Errorf("failed to create upstream proxy: secret_identifier missing")
}
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretSpacePath)
if res.SecretSpacePath != nil && len(*res.SecretSpacePath) > 0 {
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretSpacePath)
if err != nil {
return nil, nil, err
}
} else if res.SecretSpaceId != nil {
upstreamProxyConfigEntity.SecretSpaceID = *res.SecretSpaceId
}
upstreamProxyConfigEntity.SecretIdentifier = *res.SecretIdentifier
case config.AuthType == artifact.AuthTypeAccessKeySecretKey:
res, err := config.Auth.AsAccessKeySecretKey()
if err != nil {
return nil, nil, err
}
upstreamProxyConfigEntity.SecretIdentifier = *res.SecretIdentifier
} else {
switch {
case res.AccessKey != nil && len(*res.AccessKey) > 0:
upstreamProxyConfigEntity.UserName = *res.AccessKey
case res.AccessKeySecretIdentifier == nil:
return nil, nil, fmt.Errorf("failed to create upstream proxy: access_key_secret_identifier missing")
default:
if res.AccessKeySecretSpacePath != nil && len(*res.AccessKeySecretSpacePath) > 0 {
upstreamProxyConfigEntity.UserNameSecretSpaceID, err = c.getSecretID(ctx, res.AccessKeySecretSpacePath)
if err != nil {
return nil, nil, err
}
} else if res.AccessKeySecretSpaceId != nil {
upstreamProxyConfigEntity.UserNameSecretSpaceID = *res.AccessKeySecretSpaceId
}
upstreamProxyConfigEntity.UserNameSecretIdentifier = *res.AccessKeySecretIdentifier
}
if res.SecretKeySpacePath != nil && len(*res.SecretKeySpacePath) > 0 {
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretKeySpacePath)
if err != nil {
return nil, nil, err
}
} else if res.SecretKeySpaceId != nil {
upstreamProxyConfigEntity.SecretSpaceID = *res.SecretKeySpaceId
}
upstreamProxyConfigEntity.SecretIdentifier = res.SecretKeyIdentifier
default:
upstreamProxyConfigEntity.UserName = ""
upstreamProxyConfigEntity.SecretIdentifier = ""
upstreamProxyConfigEntity.SecretSpaceID = 0

View File

@ -107,6 +107,7 @@ var validPackageTypes = []string{
var validUpstreamSources = []string{
string(a.UpstreamConfigSourceCustom),
string(a.UpstreamConfigSourceDockerhub),
string(a.UpstreamConfigSourceAwsEcr),
}
func ValidatePackageTypes(packageTypes []string) error {

View File

@ -1490,6 +1490,7 @@ components:
oneOf:
- $ref: "#/components/schemas/UserPassword"
- $ref: "#/components/schemas/Anonymous"
- $ref: "#/components/schemas/AccessKeySecretKey"
url:
type: string
source:
@ -1497,6 +1498,7 @@ components:
enum:
- Dockerhub
- Custom
- AwsEcr
x-discriminator-value: UPSTREAM
required:
- authType
@ -1544,6 +1546,7 @@ components:
description: "Authentication type"
enum:
- UserPassword
- AccessKeySecretKey
- Anonymous
ClientSetupStepType:
type: string
@ -1625,9 +1628,28 @@ components:
type: string
secretSpacePath:
type: string
secretSpaceId:
type: integer
required:
- userName
- password
AccessKeySecretKey:
properties:
accessKey:
type: string
accessKeySecretIdentifier:
type: string
accessKeySecretSpacePath:
type: string
accessKeySecretSpaceId:
type: integer
secretKeyIdentifier:
type: string
secretKeySpacePath:
type: string
secretKeySpaceId:
type: integer
required:
- secretKeyIdentifier
Anonymous: {}
parameters:
spaceRefQueryParam:

View File

@ -1,6 +1,6 @@
// Package artifact provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT.
// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT.
package artifact
import (
@ -4179,73 +4179,74 @@ func (sh *strictHandler) GetAllRegistries(w http.ResponseWriter, r *http.Request
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"2HfKFt6eAAA=",
"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",
"ePry9J8AAAD//7UL4SbfoAAA",
}
// GetSwagger returns the content of the embedded swagger specification file

View File

@ -1,6 +1,6 @@
// Package artifact provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT.
// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT.
package artifact
import (
@ -13,8 +13,9 @@ import (
// Defines values for AuthType.
const (
AuthTypeAnonymous AuthType = "Anonymous"
AuthTypeUserPassword AuthType = "UserPassword"
AuthTypeAccessKeySecretKey AuthType = "AccessKeySecretKey"
AuthTypeAnonymous AuthType = "Anonymous"
AuthTypeUserPassword AuthType = "UserPassword"
)
// Defines values for ClientSetupStepType.
@ -46,6 +47,7 @@ const (
// Defines values for UpstreamConfigSource.
const (
UpstreamConfigSourceAwsEcr UpstreamConfigSource = "AwsEcr"
UpstreamConfigSourceCustom UpstreamConfigSource = "Custom"
UpstreamConfigSourceDockerhub UpstreamConfigSource = "Dockerhub"
)
@ -62,6 +64,17 @@ const (
GetAllRegistriesParamsTypeVIRTUAL GetAllRegistriesParamsType = "VIRTUAL"
)
// AccessKeySecretKey defines model for AccessKeySecretKey.
type AccessKeySecretKey struct {
AccessKey *string `json:"accessKey,omitempty"`
AccessKeySecretIdentifier *string `json:"accessKeySecretIdentifier,omitempty"`
AccessKeySecretSpaceId *int `json:"accessKeySecretSpaceId,omitempty"`
AccessKeySecretSpacePath *string `json:"accessKeySecretSpacePath,omitempty"`
SecretKeyIdentifier string `json:"secretKeyIdentifier"`
SecretKeySpaceId *int `json:"secretKeySpaceId,omitempty"`
SecretKeySpacePath *string `json:"secretKeySpacePath,omitempty"`
}
// Anonymous defines model for Anonymous.
type Anonymous interface{}
@ -453,6 +466,7 @@ type UpstreamConfigSource string
// UserPassword defines model for UserPassword.
type UserPassword struct {
SecretIdentifier *string `json:"secretIdentifier,omitempty"`
SecretSpaceId *int `json:"secretSpaceId,omitempty"`
SecretSpacePath *string `json:"secretSpacePath,omitempty"`
UserName string `json:"userName"`
}
@ -1054,6 +1068,32 @@ func (t *UpstreamConfig_Auth) MergeAnonymous(v Anonymous) error {
return err
}
// AsAccessKeySecretKey returns the union data inside the UpstreamConfig_Auth as a AccessKeySecretKey
func (t UpstreamConfig_Auth) AsAccessKeySecretKey() (AccessKeySecretKey, error) {
var body AccessKeySecretKey
err := json.Unmarshal(t.union, &body)
return body, err
}
// FromAccessKeySecretKey overwrites any union data inside the UpstreamConfig_Auth as the provided AccessKeySecretKey
func (t *UpstreamConfig_Auth) FromAccessKeySecretKey(v AccessKeySecretKey) error {
b, err := json.Marshal(v)
t.union = b
return err
}
// MergeAccessKeySecretKey performs a merge with any union data inside the UpstreamConfig_Auth, using the provided AccessKeySecretKey
func (t *UpstreamConfig_Auth) MergeAccessKeySecretKey(v AccessKeySecretKey) error {
b, err := json.Marshal(v)
if err != nil {
return err
}
merged, err := runtime.JSONMerge(t.union, b)
t.union = merged
return err
}
func (t UpstreamConfig_Auth) MarshalJSON() ([]byte, error) {
b, err := t.union.MarshalJSON()
return b, err

View File

@ -0,0 +1,96 @@
// Source: https://github.com/goharbor/harbor
// Copyright 2016 Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package awsecr
import (
"context"
"regexp"
store2 "github.com/harness/gitness/app/store"
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
adp "github.com/harness/gitness/registry/app/remote/adapter"
"github.com/harness/gitness/registry/app/remote/adapter/native"
"github.com/harness/gitness/registry/types"
"github.com/harness/gitness/secret"
awsecrapi "github.com/aws/aws-sdk-go/service/ecr"
"github.com/rs/zerolog/log"
)
const (
//nolint:lll
ecrPattern = "https://(?:api|(\\d+)\\.dkr)\\.ecr(\\-fips)?\\.([\\w\\-]+)\\.(amazonaws\\.com(\\.cn)?|sc2s\\.sgov\\.gov|c2s\\.ic\\.gov)"
)
var (
ecrRegexp = regexp.MustCompile(ecrPattern)
)
func init() {
adapterType := string(artifact.UpstreamConfigSourceAwsEcr)
if err := adp.RegisterFactory(adapterType, new(factory)); err != nil {
log.Error().Stack().Err(err).Msgf("Register adapter factory for %s", adapterType)
return
}
}
func newAdapter(
ctx context.Context, spacePathStore store2.SpacePathStore, service secret.Service, registry types.UpstreamProxy,
) (adp.Adapter, error) {
accessKey, secretKey, err := getCreds(ctx, spacePathStore, service, registry)
if err != nil {
return nil, err
}
svc, err := getAwsSvc(accessKey, secretKey, registry)
if err != nil {
return nil, err
}
authorizer := NewAuth(accessKey, svc)
return &adapter{
cacheSvc: svc,
Adapter: native.NewAdapterWithAuthorizer(registry, authorizer),
}, nil
}
// Create ...
func (f *factory) Create(
ctx context.Context, spacePathStore store2.SpacePathStore, record types.UpstreamProxy, service secret.Service,
) (adp.Adapter, error) {
return newAdapter(ctx, spacePathStore, service, record)
}
type factory struct {
}
// HealthCheck checks health status of a proxy.
func (a *adapter) HealthCheck() (string, error) {
return "Not implemented", nil
}
var (
_ adp.Adapter = (*adapter)(nil)
_ adp.ArtifactRegistry = (*adapter)(nil)
)
type adapter struct {
*native.Adapter
cacheSvc *awsecrapi.ECR
}
// Ensure '*adapter' implements interface 'Adapter'.
var _ adp.Adapter = (*adapter)(nil)

View File

@ -0,0 +1,233 @@
// Source: https://github.com/goharbor/harbor
// Copyright 2016 Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package awsecr
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/harness/gitness/app/store"
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
commonhttp "github.com/harness/gitness/registry/app/common/http"
"github.com/harness/gitness/registry/app/common/http/modifier"
"github.com/harness/gitness/registry/types"
"github.com/harness/gitness/secret"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
awsecrapi "github.com/aws/aws-sdk-go/service/ecr"
"github.com/rs/zerolog/log"
)
// Credential ...
type Credential modifier.Modifier
// Implements interface Credential.
type awsAuthCredential struct {
accessKey string
awssvc *awsecrapi.ECR
cacheToken *cacheToken
cacheExpired *time.Time
}
type cacheToken struct {
endpoint string
user string
password string
host string
}
// DefaultCacheExpiredTime is expired timeout for aws auth token.
const DefaultCacheExpiredTime = time.Hour * 1
func (a *awsAuthCredential) Modify(req *http.Request) error {
// url maybe redirect to s3
if !strings.Contains(req.URL.Host, ".ecr.") {
return nil
}
if !a.isTokenValid() {
endpoint, user, pass, expiresAt, err := a.getAuthorization(req.URL.String())
if err != nil {
return err
}
u, err := url.Parse(endpoint)
if err != nil {
return err
}
a.cacheToken = &cacheToken{}
a.cacheToken.host = u.Host
a.cacheToken.user = user
a.cacheToken.password = pass
a.cacheToken.endpoint = endpoint
t := time.Now().Add(DefaultCacheExpiredTime)
if t.Before(*expiresAt) {
a.cacheExpired = &t
} else {
a.cacheExpired = expiresAt
}
}
req.Host = a.cacheToken.host
req.URL.Host = a.cacheToken.host
req.SetBasicAuth(a.cacheToken.user, a.cacheToken.password)
return nil
}
func getAwsSvc(accessKey, secretKey string, reg types.UpstreamProxy) (*awsecrapi.ECR, error) {
_, region, err := parseAccountRegion(reg.RepoURL)
if err != nil {
return nil, err
}
sess, err := session.NewSession()
if err != nil {
return nil, err
}
var cred *credentials.Credentials
log.Info().Msgf("Aws Ecr getAuthorization %s", accessKey)
if accessKey != "" {
cred = credentials.NewStaticCredentials(
accessKey,
secretKey,
"")
}
config := &aws.Config{
Credentials: cred,
Region: &region,
HTTPClient: &http.Client{
Transport: commonhttp.GetHTTPTransport(commonhttp.WithInsecure(false)),
},
}
svc := awsecrapi.New(sess, config)
return svc, nil
}
func parseAccountRegion(url string) (string, string, error) {
rs := ecrRegexp.FindStringSubmatch(url)
if rs == nil || len(rs) < 4 {
return "", "", errors.New("bad aws url")
}
return rs[1], rs[3], nil
}
func getCreds(
ctx context.Context, spacePathStore store.SpacePathStore, secretService secret.Service, reg types.UpstreamProxy,
) (string, string, error) {
if api.AuthType(reg.RepoAuthType) != api.AuthTypeAccessKeySecretKey {
log.Debug().Msgf("invalid auth type: %s", reg.RepoAuthType)
return "", "", nil
}
secretKey, err := getSecretValue(ctx, spacePathStore, secretService, reg.SecretSpaceID,
reg.SecretIdentifier)
if err != nil {
return "", "", err
}
if reg.UserName != "" {
return reg.UserName, secretKey, nil
}
accessKey, err := getSecretValue(ctx, spacePathStore, secretService, reg.SecretSpaceID,
reg.SecretIdentifier)
if err != nil {
return "", "", err
}
return accessKey, secretKey, nil
}
func getSecretValue(ctx context.Context, spacePathStore store.SpacePathStore, secretService secret.Service,
secretSpaceID int64, secretSpacePath string) (string, error) {
spacePath, err := spacePathStore.FindPrimaryBySpaceID(ctx, secretSpaceID)
if err != nil {
log.Error().Msgf("failed to find space path: %v", err)
return "", err
}
decryptSecret, err := secretService.DecryptSecret(ctx, spacePath.Value, secretSpacePath)
if err != nil {
log.Error().Msgf("failed to decrypt secret: %v", err)
return "", err
}
return decryptSecret, nil
}
func (a *awsAuthCredential) getAuthorization(url string) (string, string, string, *time.Time, error) {
id, _, err := parseAccountRegion(url)
if err != nil {
return "", "", "", nil, err
}
var input *awsecrapi.GetAuthorizationTokenInput
if id != "" {
input = &awsecrapi.GetAuthorizationTokenInput{RegistryIds: []*string{&id}}
}
svc := a.awssvc
result, err := svc.GetAuthorizationToken(input)
if err != nil {
var awsErr *awserr.Error
if errors.As(err, awsErr) {
return "", "", "", nil, fmt.Errorf("%s", err.Error())
}
return "", "", "", nil, err
}
// Double check
if len(result.AuthorizationData) == 0 {
return "", "", "", nil, errors.New("no authorization token returned")
}
theOne := result.AuthorizationData[0]
expiresAt := theOne.ExpiresAt
payload, _ := base64.StdEncoding.DecodeString(*theOne.AuthorizationToken)
pair := strings.SplitN(string(payload), ":", 2)
log.Debug().Msgf("Aws Ecr getAuthorization %s result: %d %s...", a.accessKey, len(pair[1]), pair[1][:25])
return *(theOne.ProxyEndpoint), pair[0], pair[1], expiresAt, nil
}
func (a *awsAuthCredential) isTokenValid() bool {
if a.cacheToken == nil {
return false
}
if a.cacheExpired == nil {
return false
}
if time.Now().After(*a.cacheExpired) {
a.cacheExpired = nil
a.cacheToken = nil
return false
}
return true
}
// NewAuth new aws auth.
func NewAuth(accessKey string, awssvc *awsecrapi.ECR) Credential {
return &awsAuthCredential{
accessKey: accessKey,
awssvc: awssvc,
}
}

View File

@ -20,6 +20,7 @@ import (
"context"
store2 "github.com/harness/gitness/app/store"
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
adp "github.com/harness/gitness/registry/app/remote/adapter"
"github.com/harness/gitness/registry/app/remote/adapter/native"
"github.com/harness/gitness/registry/types"
@ -29,7 +30,7 @@ import (
)
func init() {
adapterType := "docker"
adapterType := string(artifact.UpstreamConfigSourceDockerhub)
if err := adp.RegisterFactory(adapterType, new(factory)); err != nil {
log.Error().Stack().Err(err).Msgf("Register adapter factory for %s", adapterType)
return

View File

@ -21,6 +21,7 @@ import (
"github.com/harness/gitness/app/store"
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
"github.com/harness/gitness/registry/app/common/lib"
"github.com/harness/gitness/registry/app/common/lib/errors"
adp "github.com/harness/gitness/registry/app/remote/adapter"
"github.com/harness/gitness/registry/app/remote/clients/registry"
@ -58,6 +59,14 @@ func NewAdapter(
return adapter
}
// NewAdapterWithAuthorizer returns an instance of the Adapter with provided authorizer.
func NewAdapterWithAuthorizer(reg types.UpstreamProxy, authorizer lib.Authorizer) *Adapter {
return &Adapter{
proxy: reg,
Client: registry.NewClientWithAuthorizer(reg.RepoURL, authorizer, false),
}
}
// getPwd: lookup secrets.secret_data using secret_identifier & secret_space_id.
func getPwd(
ctx context.Context, spacePathStore store.SpacePathStore, secretService secret.Service, reg types.UpstreamProxy,

View File

@ -29,6 +29,7 @@ import (
"github.com/rs/zerolog/log"
"golang.org/x/net/context"
_ "github.com/harness/gitness/registry/app/remote/adapter/awsecr" // This is required to init aws ecr adapter
_ "github.com/harness/gitness/registry/app/remote/adapter/dockerhub" // This is required to init docker adapter
)
@ -68,19 +69,19 @@ func NewRemoteHelper(
upstreamProxy: proxy,
secretService: secretService,
}
if err := r.init(ctx, spacePathStore); err != nil {
if err := r.init(ctx, spacePathStore, proxy.Source); err != nil {
return nil, err
}
return r, nil
}
func (r *remoteHelper) init(ctx context.Context, spacePathStore store.SpacePathStore) error {
func (r *remoteHelper) init(ctx context.Context, spacePathStore store.SpacePathStore, proxyType string) error {
if r.registry != nil {
return nil
}
// TODO add health check.
factory, err := adapter.GetFactory("docker")
factory, err := adapter.GetFactory(proxyType)
if err != nil {
return err
}

View File

@ -53,40 +53,44 @@ func NewUpstreamproxyDao(
// upstreamProxyConfigDB holds the record of an upstream_proxy_config in DB.
type upstreamProxyConfigDB struct {
ID int64 `db:"upstream_proxy_config_id"`
RegistryID int64 `db:"upstream_proxy_config_registry_id"`
Source string `db:"upstream_proxy_config_source"`
URL string `db:"upstream_proxy_config_url"`
AuthType string `db:"upstream_proxy_config_auth_type"`
UserName string `db:"upstream_proxy_config_user_name"`
SecretIdentifier sql.NullString `db:"upstream_proxy_config_secret_identifier"`
SecretSpaceID sql.NullInt32 `db:"upstream_proxy_config_secret_space_id"`
Token string `db:"upstream_proxy_config_token"`
CreatedAt int64 `db:"upstream_proxy_config_created_at"`
UpdatedAt int64 `db:"upstream_proxy_config_updated_at"`
CreatedBy int64 `db:"upstream_proxy_config_created_by"`
UpdatedBy int64 `db:"upstream_proxy_config_updated_by"`
ID int64 `db:"upstream_proxy_config_id"`
RegistryID int64 `db:"upstream_proxy_config_registry_id"`
Source string `db:"upstream_proxy_config_source"`
URL string `db:"upstream_proxy_config_url"`
AuthType string `db:"upstream_proxy_config_auth_type"`
UserName string `db:"upstream_proxy_config_user_name"`
SecretIdentifier sql.NullString `db:"upstream_proxy_config_secret_identifier"`
SecretSpaceID sql.NullInt32 `db:"upstream_proxy_config_secret_space_id"`
UserNameSecretIdentifier sql.NullString `db:"upstream_proxy_config_user_name_secret_identifier"`
UserNameSecretSpaceID sql.NullInt32 `db:"upstream_proxy_config_user_name_secret_space_id"`
Token string `db:"upstream_proxy_config_token"`
CreatedAt int64 `db:"upstream_proxy_config_created_at"`
UpdatedAt int64 `db:"upstream_proxy_config_updated_at"`
CreatedBy int64 `db:"upstream_proxy_config_created_by"`
UpdatedBy int64 `db:"upstream_proxy_config_updated_by"`
}
type upstreamProxyDB struct {
ID int64 `db:"id"`
RegistryID int64 `db:"registry_id"`
RepoKey string `db:"repo_key"`
ParentID string `db:"parent_id"`
PackageType artifact.PackageType `db:"package_type"`
AllowedPattern sql.NullString `db:"allowed_pattern"`
BlockedPattern sql.NullString `db:"blocked_pattern"`
Source string `db:"source"`
RepoURL string `db:"repo_url"`
RepoAuthType string `db:"repo_auth_type"`
UserName string `db:"user_name"`
SecretIdentifier sql.NullString `db:"secret_identifier"`
SecretSpaceID sql.NullInt32 `db:"secret_space_id"`
Token string `db:"token"`
CreatedAt int64 `db:"created_at"`
UpdatedAt int64 `db:"updated_at"`
CreatedBy sql.NullInt64 `db:"created_by"`
UpdatedBy sql.NullInt64 `db:"updated_by"`
ID int64 `db:"id"`
RegistryID int64 `db:"registry_id"`
RepoKey string `db:"repo_key"`
ParentID string `db:"parent_id"`
PackageType artifact.PackageType `db:"package_type"`
AllowedPattern sql.NullString `db:"allowed_pattern"`
BlockedPattern sql.NullString `db:"blocked_pattern"`
Source string `db:"source"`
RepoURL string `db:"repo_url"`
RepoAuthType string `db:"repo_auth_type"`
UserName string `db:"user_name"`
SecretIdentifier sql.NullString `db:"secret_identifier"`
SecretSpaceID sql.NullInt32 `db:"secret_space_id"`
UserNameSecretIdentifier sql.NullString `db:"user_name_secret_identifier"`
UserNameSecretSpaceID sql.NullInt32 `db:"user_name_secret_space_id"`
Token string `db:"token"`
CreatedAt int64 `db:"created_at"`
UpdatedAt int64 `db:"updated_at"`
CreatedBy sql.NullInt64 `db:"created_by"`
UpdatedBy sql.NullInt64 `db:"updated_by"`
}
func getUpstreamProxyQuery() squirrel.SelectBuilder {
@ -104,6 +108,8 @@ func getUpstreamProxyQuery() squirrel.SelectBuilder {
" u.upstream_proxy_config_user_name as user_name," +
" u.upstream_proxy_config_secret_identifier as secret_identifier," +
" u.upstream_proxy_config_secret_space_id as secret_space_id," +
" u.upstream_proxy_config_user_name_secret_identifier as user_name_secret_identifier," +
" u.upstream_proxy_config_user_name_secret_space_id as user_name_secret_space_id," +
" u.upstream_proxy_config_token as token," +
" r.registry_created_at as created_at," +
" r.registry_updated_at as updated_at ").
@ -189,6 +195,8 @@ func (r UpstreamproxyDao) Create(
,upstream_proxy_config_user_name
,upstream_proxy_config_secret_identifier
,upstream_proxy_config_secret_space_id
,upstream_proxy_config_user_name_secret_identifier
,upstream_proxy_config_user_name_secret_space_id
,upstream_proxy_config_token
,upstream_proxy_config_created_at
,upstream_proxy_config_updated_at
@ -202,6 +210,8 @@ func (r UpstreamproxyDao) Create(
,:upstream_proxy_config_user_name
,:upstream_proxy_config_secret_identifier
,:upstream_proxy_config_secret_space_id
,:upstream_proxy_config_user_name_secret_identifier
,:upstream_proxy_config_user_name_secret_space_id
,:upstream_proxy_config_token
,:upstream_proxy_config_created_at
,:upstream_proxy_config_updated_at
@ -360,19 +370,21 @@ func (r UpstreamproxyDao) mapToInternalUpstreamProxy(
in.UpdatedBy = session.Principal.ID
return &upstreamProxyConfigDB{
ID: in.ID,
RegistryID: in.RegistryID,
Source: in.Source,
URL: in.URL,
AuthType: in.AuthType,
UserName: in.UserName,
SecretIdentifier: util.GetEmptySQLString(in.SecretIdentifier),
SecretSpaceID: util.GetEmptySQLInt32(in.SecretSpaceID),
Token: in.Token,
CreatedAt: in.CreatedAt.UnixMilli(),
UpdatedAt: in.UpdatedAt.UnixMilli(),
CreatedBy: in.CreatedBy,
UpdatedBy: in.UpdatedBy,
ID: in.ID,
RegistryID: in.RegistryID,
Source: in.Source,
URL: in.URL,
AuthType: in.AuthType,
UserName: in.UserName,
SecretIdentifier: util.GetEmptySQLString(in.SecretIdentifier),
SecretSpaceID: util.GetEmptySQLInt32(in.SecretSpaceID),
UserNameSecretSpaceID: util.GetEmptySQLInt32(in.UserNameSecretSpaceID),
UserNameSecretIdentifier: util.GetEmptySQLString(in.UserNameSecretIdentifier),
Token: in.Token,
CreatedAt: in.CreatedAt.UnixMilli(),
UpdatedAt: in.UpdatedAt.UnixMilli(),
CreatedBy: in.CreatedBy,
UpdatedBy: in.UpdatedBy,
}
}
@ -406,26 +418,47 @@ func (r UpstreamproxyDao) mapToUpstreamProxy(
secretSpacePath = primary.Value
}
userNameSecretIdentifier := ""
userNameSecretSpaceID := int64(-1)
if dst.UserNameSecretIdentifier.Valid {
userNameSecretIdentifier = dst.UserNameSecretIdentifier.String
}
if dst.UserNameSecretSpaceID.Valid {
userNameSecretSpaceID = int64(dst.UserNameSecretSpaceID.Int32)
}
userNameSecretSpacePath := ""
if dst.UserNameSecretSpaceID.Valid {
primary, err := r.spacePathStore.FindPrimaryBySpaceID(ctx, int64(dst.UserNameSecretSpaceID.Int32))
if err != nil {
return nil, fmt.Errorf("failed to get secret space path: %w", err)
}
userNameSecretSpacePath = primary.Value
}
return &types.UpstreamProxy{
ID: dst.ID,
RegistryID: dst.RegistryID,
RepoKey: dst.RepoKey,
ParentID: dst.ParentID,
PackageType: dst.PackageType,
AllowedPattern: util.StringToArr(dst.AllowedPattern.String),
BlockedPattern: util.StringToArr(dst.BlockedPattern.String),
Source: dst.Source,
RepoURL: dst.RepoURL,
RepoAuthType: dst.RepoAuthType,
UserName: dst.UserName,
SecretIdentifier: secretIdentifier,
SecretSpaceID: secretSpaceID,
SecretSpacePath: secretSpacePath,
Token: dst.Token,
CreatedAt: time.UnixMilli(dst.CreatedAt),
UpdatedAt: time.UnixMilli(dst.UpdatedAt),
CreatedBy: createdBy,
UpdatedBy: updatedBy,
ID: dst.ID,
RegistryID: dst.RegistryID,
RepoKey: dst.RepoKey,
ParentID: dst.ParentID,
PackageType: dst.PackageType,
AllowedPattern: util.StringToArr(dst.AllowedPattern.String),
BlockedPattern: util.StringToArr(dst.BlockedPattern.String),
Source: dst.Source,
RepoURL: dst.RepoURL,
RepoAuthType: dst.RepoAuthType,
UserName: dst.UserName,
SecretIdentifier: secretIdentifier,
SecretSpaceID: secretSpaceID,
SecretSpacePath: secretSpacePath,
UserNameSecretIdentifier: userNameSecretIdentifier,
UserNameSecretSpaceID: userNameSecretSpaceID,
UserNameSecretSpacePath: userNameSecretSpacePath,
Token: dst.Token,
CreatedAt: time.UnixMilli(dst.CreatedAt),
UpdatedAt: time.UnixMilli(dst.UpdatedAt),
CreatedBy: createdBy,
UpdatedBy: updatedBy,
}, nil
}

View File

@ -22,40 +22,45 @@ import (
// UpstreamProxyConfig DTO object.
type UpstreamProxyConfig struct {
ID int64
RegistryID int64
Source string
URL string
AuthType string
UserName string
Password string
SecretIdentifier string
SecretSpaceID int
Token string
CreatedAt time.Time
UpdatedAt time.Time
CreatedBy int64
UpdatedBy int64
ID int64
RegistryID int64
Source string
URL string
AuthType string
UserName string
UserNameSecretIdentifier string
UserNameSecretSpaceID int
Password string
SecretIdentifier string
SecretSpaceID int
Token string
CreatedAt time.Time
UpdatedAt time.Time
CreatedBy int64
UpdatedBy int64
}
type UpstreamProxy struct {
ID int64
RegistryID int64
RepoKey string
ParentID string
PackageType artifact.PackageType
AllowedPattern []string
BlockedPattern []string
Source string
RepoURL string
RepoAuthType string
UserName string
SecretIdentifier string
SecretSpaceID int64
SecretSpacePath string
Token string
CreatedAt time.Time
UpdatedAt time.Time
CreatedBy int64
UpdatedBy int64
ID int64
RegistryID int64
RepoKey string
ParentID string
PackageType artifact.PackageType
AllowedPattern []string
BlockedPattern []string
Source string
RepoURL string
RepoAuthType string
UserName string
UserNameSecretIdentifier string
UserNameSecretSpaceID int64
UserNameSecretSpacePath string
SecretIdentifier string
SecretSpaceID int64
SecretSpacePath string
Token string
CreatedAt time.Time
UpdatedAt time.Time
CreatedBy int64
UpdatedBy int64
}