[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")
}
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")
}
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 {
case 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
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,6 +13,7 @@ import (
// Defines values for AuthType.
const (
AuthTypeAccessKeySecretKey AuthType = "AccessKeySecretKey"
AuthTypeAnonymous AuthType = "Anonymous"
AuthTypeUserPassword AuthType = "UserPassword"
)
@ -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

@ -61,6 +61,8 @@ type upstreamProxyConfigDB struct {
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"`
@ -82,6 +84,8 @@ type upstreamProxyDB struct {
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"`
@ -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
@ -368,6 +378,8 @@ func (r UpstreamproxyDao) mapToInternalUpstreamProxy(
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(),
@ -406,6 +418,24 @@ 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,
@ -421,6 +451,9 @@ func (r UpstreamproxyDao) mapToUpstreamProxy(
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),

View File

@ -28,6 +28,8 @@ type UpstreamProxyConfig struct {
URL string
AuthType string
UserName string
UserNameSecretIdentifier string
UserNameSecretSpaceID int
Password string
SecretIdentifier string
SecretSpaceID int
@ -50,6 +52,9 @@ type UpstreamProxy struct {
RepoURL string
RepoAuthType string
UserName string
UserNameSecretIdentifier string
UserNameSecretSpaceID int64
UserNameSecretSpacePath string
SecretIdentifier string
SecretSpaceID int64
SecretSpacePath string