* feat: check licenses header with skywalking-eye and support check tools. Signed-off-by: mango <xu.weiKyrie@foxmail.com> * feat: check licenses header with skywalking-eye and support check tools. Signed-off-by: mango <xu.weiKyrie@foxmail.com> * feat: check licenses header with skywalking-eye and support check tools. Signed-off-by: mango <xu.weiKyrie@foxmail.com> * remove verify-licenses because verify-all exist. Signed-off-by: mango <xu.weiKyrie@foxmail.com> * update modules.txt Signed-off-by: mango <xu.weiKyrie@foxmail.com> * revert go.mod Signed-off-by: mango <xu.weiKyrie@foxmail.com> * update vendor directory. Signed-off-by: mango <xu.weiKyrie@foxmail.com> * revert go.sum Signed-off-by: mango <xu.weiKyrie@foxmail.com> * revert go.sum Signed-off-by: mango <xu.weiKyrie@foxmail.com> * ignore `pkg/controller/application/status.go` Signed-off-by: mango <xu.weiKyrie@foxmail.com> * add license header. Signed-off-by: mango <xu.weiKyrie@foxmail.com>
159 lines
4.0 KiB
Go
159 lines
4.0 KiB
Go
// Copyright 2022 The KubeSphere 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 v2
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"github.com/google/go-containerregistry/pkg/authn"
|
|
"github.com/google/go-containerregistry/pkg/name"
|
|
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
|
|
v1 "k8s.io/api/core/v1"
|
|
)
|
|
|
|
const (
|
|
forceInsecure = "secret.kubesphere.io/force-insecure"
|
|
)
|
|
|
|
type SecretAuthenticator interface {
|
|
Options() []Option
|
|
|
|
Auth() (bool, error)
|
|
|
|
Authorization() (*authn.AuthConfig, error)
|
|
}
|
|
|
|
type secretAuthenticator struct {
|
|
auths DockerConfig
|
|
insecure bool // force using insecure when talk to the remote registry, even registry address starts with https
|
|
}
|
|
|
|
func NewSecretAuthenticator(secret *v1.Secret) (SecretAuthenticator, error) {
|
|
|
|
if secret == nil {
|
|
return &secretAuthenticator{}, nil
|
|
}
|
|
|
|
sa := &secretAuthenticator{
|
|
insecure: false,
|
|
}
|
|
|
|
if secret.Type != v1.SecretTypeDockerConfigJson {
|
|
return nil, fmt.Errorf("expected secret type: %s, got: %s", v1.SecretTypeDockerConfigJson, secret.Type)
|
|
}
|
|
|
|
// force insecure if secret has annotation forceInsecure
|
|
if val, ok := secret.Annotations[forceInsecure]; ok && val == "true" {
|
|
sa.insecure = true
|
|
}
|
|
|
|
configJson, ok := secret.Data[v1.DockerConfigJsonKey]
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected key %s in data, found none", v1.DockerConfigJsonKey)
|
|
}
|
|
|
|
dockerConfigJSON := DockerConfigJSON{}
|
|
if err := json.Unmarshal(configJson, &dockerConfigJSON); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(dockerConfigJSON.Auths) == 0 {
|
|
return nil, fmt.Errorf("not found valid auth in secret, %v", dockerConfigJSON)
|
|
}
|
|
|
|
sa.auths = dockerConfigJSON.Auths
|
|
|
|
return sa, nil
|
|
}
|
|
|
|
func (s *secretAuthenticator) Authorization() (*authn.AuthConfig, error) {
|
|
for _, v := range s.auths {
|
|
return &authn.AuthConfig{
|
|
Username: v.Username,
|
|
Password: v.Password,
|
|
Auth: v.Auth,
|
|
}, nil
|
|
}
|
|
return &authn.AuthConfig{}, nil
|
|
}
|
|
|
|
func (s *secretAuthenticator) Auth() (bool, error) {
|
|
for k := range s.auths {
|
|
return s.AuthRegistry(k)
|
|
}
|
|
return false, fmt.Errorf("no registry found in secret")
|
|
}
|
|
|
|
func (s *secretAuthenticator) AuthRegistry(reg string) (bool, error) {
|
|
url, err := url.Parse(reg) // in case reg is unformatted like http://docker.index.io
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
options := make([]name.Option, 0)
|
|
if url.Scheme == "http" {
|
|
// allows image references to be fetched without TLS
|
|
// transport.NewWithContext will auto-select the right scheme
|
|
options = append(options, name.Insecure)
|
|
}
|
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
|
// skip tls verify
|
|
if s.insecure {
|
|
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
}
|
|
|
|
registry, err := name.NewRegistry(url.Host, options...)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
ctx := context.TODO()
|
|
_, err = transport.NewWithContext(ctx, registry, s, tr, []string{})
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (s *secretAuthenticator) Options() []Option {
|
|
options := make([]Option, 0)
|
|
options = append(options, WithAuth(s))
|
|
if s.registryScheme() == "http" {
|
|
options = append(options, Insecure)
|
|
}
|
|
if s.insecure {
|
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
|
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
options = append(options, WithTransport(tr))
|
|
}
|
|
return options
|
|
}
|
|
|
|
func (s *secretAuthenticator) registryScheme() string {
|
|
for registry := range s.auths {
|
|
u, err := url.Parse(registry)
|
|
if err == nil {
|
|
return u.Scheme
|
|
}
|
|
}
|
|
return "https"
|
|
}
|