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.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.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

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

@ -25,6 +25,7 @@ import (
"github.com/pkg/errors"
"helm.sh/helm/v3/internal/tlsutil"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/downloader"
@ -86,6 +87,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
getter.WithPassCredentialsAll(p.PassCredentialsAll),
getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify),
getter.WithTwoWayTLSEnable(p.TlsEnabled),
},
RegistryClient: p.cfg.RegistryClient,
RepositoryConfig: p.Settings.RepositoryConfig,
@ -93,8 +95,24 @@ func (p *Pull) Run(chartRef string) (string, error) {
}
if registry.IsOCI(chartRef) {
c.Options = append(c.Options,
getter.WithRegistryClient(p.cfg.RegistryClient))
if !p.TlsEnabled {
c.Options = append(c.Options,
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 {

@ -42,6 +42,7 @@ type options struct {
passCredentialsAll bool
userAgent string
version string
tlsEnabled bool
registryClient *registry.Client
timeout time.Duration
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.
func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
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.
See the License for the specific language governing permissions and
limitations under the License.
"helm.sh/helm/v3/internal/tlsutil"
*/
package registry // import "helm.sh/helm/v3/pkg/registry"
@ -22,9 +23,11 @@ import (
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"sort"
"strings"
"time"
"github.com/Masterminds/semver/v3"
"github.com/containerd/containerd/remotes"
@ -38,6 +41,7 @@ import (
registryremote "oras.land/oras-go/pkg/registry/remote"
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/pkg/chart"
"helm.sh/helm/v3/pkg/helmpath"
@ -61,6 +65,9 @@ type (
authorizer auth.Client
registryAuthorizer *registryauth.Client
resolver remotes.Resolver
tlsEnabled bool
chartRef string
utilOpts tlsutil.Options
}
// ClientOption allows specifying various settings configurable by the user for overriding the defaults
@ -87,14 +94,40 @@ func NewClient(options ...ClientOption) (*Client, error) {
client.authorizer = authClient
}
if client.resolver == nil {
headers := http.Header{}
headers.Set("User-Agent", version.GetUserAgent())
opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)}
resolver, err := client.authorizer.ResolverWithOpts(opts...)
if err != nil {
return nil, err
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.Set("User-Agent", version.GetUserAgent())
opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)}
resolver, err := client.authorizer.ResolverWithOpts(opts...)
if err != nil {
return nil, err
}
client.resolver = resolver
}
client.resolver = resolver
}
// allocate a cache if option is set
@ -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
func ClientOptEnableCache(enableCache bool) ClientOption {
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 (
// LoginOption allows specifying various settings on login
LoginOption func(*loginOperation)

Loading…
Cancel
Save