Bug fix for client certificate not reaching for mutual tls handshake in helm oci pull

What this PR does / why we need it:
Fix for (#10597)

Added 2-way TLS Support for oci pull for artifact repository which causes TLS handshake failure error.
Special notes for your reviewer:

Added flag for two-way authentication (--mtls-enabled) .
eg: helm pull oci://nginx.testharbor.com:9443/testrepo/sslcharttest --version 0.1.0 --ca-file /etc/docker/certs.d/nginx.testharbor.com/ca.crt --cert-file /etc/docker/certs.d/nginx.testharbor.com/root_client.crt --key-file /etc/docker/certs.d/nginx.testharbor.com/root_client.key --mtls-enabled
Signed-off-by: Vignesh2017 svignesh899@gmail.com
pull/11184/head
Vignesh2017 3 years ago
parent 9377988685
commit 0c2c30cd91

@ -61,6 +61,7 @@ func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download") f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains") f.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains")
f.BoolVar(&c.TlsEnabled, "mtls-enabled", false, "if two-way tls authentication enabled then trying to send client certificate")
} }
// bindOutputFlag will add the output flag to the given command and bind the // bindOutputFlag will add the output flag to the given command and bind the

@ -117,7 +117,7 @@ type ChartPathOptions struct {
Username string // --username Username string // --username
Verify bool // --verify Verify bool // --verify
Version string // --version Version string // --version
TlsEnabled bool // --mtls-enabled
// registryClient provides a registry client but is not added with // registryClient provides a registry client but is not added with
// options from a flag // options from a flag
registryClient *registry.Client registryClient *registry.Client

@ -25,6 +25,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"helm.sh/helm/v3/internal/tlsutil"
"helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/downloader" "helm.sh/helm/v3/pkg/downloader"
@ -86,6 +87,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
getter.WithPassCredentialsAll(p.PassCredentialsAll), getter.WithPassCredentialsAll(p.PassCredentialsAll),
getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify), getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify),
getter.WithTwoWayTLSEnable(p.TlsEnabled),
}, },
RegistryClient: p.cfg.RegistryClient, RegistryClient: p.cfg.RegistryClient,
RepositoryConfig: p.Settings.RepositoryConfig, RepositoryConfig: p.Settings.RepositoryConfig,
@ -93,8 +95,24 @@ func (p *Pull) Run(chartRef string) (string, error) {
} }
if registry.IsOCI(chartRef) { if registry.IsOCI(chartRef) {
if !p.TlsEnabled {
c.Options = append(c.Options, c.Options = append(c.Options,
getter.WithRegistryClient(p.cfg.RegistryClient)) getter.WithRegistryClient(p.cfg.RegistryClient),
)
} else {
registryClient, err := registry.NewClient(
registry.ClientOptDebug(p.Settings.Debug),
registry.ClientOptCredentialsFile(p.Settings.RegistryConfig),
registry.ClientOptWriter(&out),
registry.ClientOptTwoWayTLSEnable(p.TlsEnabled),
registry.ClientOptChartRef(chartRef),
registry.ClientOptWithTLSOpts(tlsutil.Options{CaCertFile: p.CaFile, KeyFile: p.KeyFile, CertFile: p.CertFile, InsecureSkipVerify: p.InsecureSkipTLSverify}),
)
if err != nil {
return out.String(), err
}
c.Options = append(c.Options, getter.WithRegistryClient(registryClient))
}
} }
if p.Verify { if p.Verify {

@ -42,6 +42,7 @@ type options struct {
passCredentialsAll bool passCredentialsAll bool
userAgent string userAgent string
version string version string
tlsEnabled bool
registryClient *registry.Client registryClient *registry.Client
timeout time.Duration timeout time.Duration
transport *http.Transport transport *http.Transport
@ -87,6 +88,12 @@ func WithInsecureSkipVerifyTLS(insecureSkipVerifyTLS bool) Option {
} }
} }
func WithTwoWayTLSEnable(tlsEnabled bool) Option {
return func(opts *options) {
opts.tlsEnabled = tlsEnabled
}
}
// WithTLSClientConfig sets the client auth with the provided credentials. // WithTLSClientConfig sets the client auth with the provided credentials.
func WithTLSClientConfig(certFile, keyFile, caFile string) Option { func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
return func(opts *options) { return func(opts *options) {

@ -12,6 +12,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
"helm.sh/helm/v3/internal/tlsutil"
*/ */
package registry // import "helm.sh/helm/v3/pkg/registry" package registry // import "helm.sh/helm/v3/pkg/registry"
@ -22,9 +23,11 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"sort" "sort"
"strings" "strings"
"time"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
"github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
@ -38,6 +41,7 @@ import (
registryremote "oras.land/oras-go/pkg/registry/remote" registryremote "oras.land/oras-go/pkg/registry/remote"
registryauth "oras.land/oras-go/pkg/registry/remote/auth" registryauth "oras.land/oras-go/pkg/registry/remote/auth"
"helm.sh/helm/v3/internal/tlsutil"
"helm.sh/helm/v3/internal/version" "helm.sh/helm/v3/internal/version"
"helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/helmpath"
@ -61,6 +65,9 @@ type (
authorizer auth.Client authorizer auth.Client
registryAuthorizer *registryauth.Client registryAuthorizer *registryauth.Client
resolver remotes.Resolver resolver remotes.Resolver
tlsEnabled bool
chartRef string
utilOpts tlsutil.Options
} }
// ClientOption allows specifying various settings configurable by the user for overriding the defaults // ClientOption allows specifying various settings configurable by the user for overriding the defaults
@ -87,6 +94,31 @@ func NewClient(options ...ClientOption) (*Client, error) {
client.authorizer = authClient client.authorizer = authClient
} }
if client.resolver == nil { if client.resolver == nil {
if client.tlsEnabled {
cfgtls, err := tlsutil.ClientConfig(client.utilOpts)
if err != nil {
fmt.Printf("error :%v\n", err)
}
var rt http.RoundTripper = &http.Transport{
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 30 * time.Second,
TLSClientConfig: cfgtls,
ResponseHeaderTimeout: time.Duration(30 * time.Second),
DisableKeepAlives: true,
}
sClient := http.Client{Transport: rt, Timeout: 30 * time.Second}
headers := http.Header{}
headers.Set("User-Agent", version.GetUserAgent())
opts := []auth.ResolverOption{auth.WithResolverHeaders(headers), auth.WithResolverClient(&sClient)}
resolver, err := client.authorizer.ResolverWithOpts(opts...)
if err != nil {
return nil, err
}
client.resolver = resolver
} else {
headers := http.Header{} headers := http.Header{}
headers.Set("User-Agent", version.GetUserAgent()) headers.Set("User-Agent", version.GetUserAgent())
opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)} opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)}
@ -96,6 +128,7 @@ func NewClient(options ...ClientOption) (*Client, error) {
} }
client.resolver = resolver client.resolver = resolver
} }
}
// allocate a cache if option is set // allocate a cache if option is set
var cache registryauth.Cache var cache registryauth.Cache
@ -145,6 +178,12 @@ func ClientOptDebug(debug bool) ClientOption {
} }
} }
func ClientOptChartRef(chartRef string) ClientOption {
return func(client *Client) {
client.chartRef = chartRef
}
}
// ClientOptEnableCache returns a function that sets the enableCache setting on a client options set // ClientOptEnableCache returns a function that sets the enableCache setting on a client options set
func ClientOptEnableCache(enableCache bool) ClientOption { func ClientOptEnableCache(enableCache bool) ClientOption {
return func(client *Client) { return func(client *Client) {
@ -166,6 +205,20 @@ func ClientOptCredentialsFile(credentialsFile string) ClientOption {
} }
} }
//ClientOptTwoWayTLSEnable returns a function that sets the client certificate when two-way tls authentication enable
func ClientOptTwoWayTLSEnable(tlsEnabled bool) ClientOption {
return func(client *Client) {
client.tlsEnabled = tlsEnabled
}
}
//ClientOptTwoWayTLSEnable returns a function that sets the client certificate when two-way tls authentication enable
func ClientOptWithTLSOpts(tlsOpts tlsutil.Options) ClientOption {
return func(client *Client) {
client.utilOpts = tlsOpts
}
}
type ( type (
// LoginOption allows specifying various settings on login // LoginOption allows specifying various settings on login
LoginOption func(*loginOperation) LoginOption func(*loginOperation)

Loading…
Cancel
Save