Skip to content

Commit

Permalink
Enable custom certificates option for OCI
Browse files Browse the repository at this point in the history
If implemented, users will be able to use custom certificates and CA to
while interacting with OCI registries.

Signed-off-by: Soule BA <bah.soule@gmail.com>
  • Loading branch information
souleb authored and sabre1041 committed Mar 3, 2023
1 parent e007c90 commit b0ecb21
Show file tree
Hide file tree
Showing 26 changed files with 1,117 additions and 353 deletions.
2 changes: 1 addition & 1 deletion cmd/helm/install.go
Expand Up @@ -203,7 +203,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
}
client.ReleaseName = name

cp, err := client.ChartPathOptions.LocateChart(chart, settings)
cp, err := client.ChartPathOptions.LocateChart(chart, out, settings)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/pull.go
Expand Up @@ -43,7 +43,7 @@ result in an error, and the chart will not be saved locally.
`

func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewPullWithOpts(action.WithConfig(cfg))
client := action.NewPullWithOpts(action.WithConfig(cfg), action.WithPullOptWriter(out))

cmd := &cobra.Command{
Use: "pull [chart URL | repo/chartname] [...]",
Expand Down
16 changes: 15 additions & 1 deletion cmd/helm/push.go
Expand Up @@ -34,8 +34,14 @@ If the chart has an associated provenance file,
it will also be uploaded.
`

type registryPushOptions struct {
certFile string
keyFile string
caFile string
}

func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewPushWithOpts(action.WithPushConfig(cfg))
o := &registryPushOptions{}

cmd := &cobra.Command{
Use: "push [chart] [remote]",
Expand All @@ -62,6 +68,9 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
chartRef := args[0]
remote := args[1]
client := action.NewPushWithOpts(action.WithPushConfig(cfg),
action.WithTLSClientConfig(o.certFile, o.keyFile, o.caFile),
action.WithPushOptWriter(out))
client.Settings = settings
output, err := client.Run(chartRef, remote)
if err != nil {
Expand All @@ -72,5 +81,10 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
},
}

f := cmd.Flags()
f.StringVar(&o.certFile, "cert-file", "", "identify registry client using this SSL certificate file")
f.StringVar(&o.keyFile, "key-file", "", "identify registry client using this SSL key file")
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")

return cmd
}
29 changes: 21 additions & 8 deletions cmd/helm/registry_login.go
Expand Up @@ -36,9 +36,17 @@ const registryLoginDesc = `
Authenticate to a remote registry.
`

type registryLoginOptions struct {
username string
password string
passwordFromStdinOpt bool
certFile string
keyFile string
caFile string
}

func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
var usernameOpt, passwordOpt string
var passwordFromStdinOpt, insecureOpt bool
o := &registryLoginOptions{}

cmd := &cobra.Command{
Use: "login [host]",
Expand All @@ -49,20 +57,25 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
RunE: func(cmd *cobra.Command, args []string) error {
hostname := args[0]

username, password, err := getUsernamePassword(usernameOpt, passwordOpt, passwordFromStdinOpt)
username, password, err := getUsernamePassword(o.username, o.password, o.passwordFromStdinOpt)
if err != nil {
return err
}

return action.NewRegistryLogin(cfg).Run(out, hostname, username, password, insecureOpt)
return action.NewRegistryLogin(cfg).Run(out, hostname, username, password,
action.WithCertFile(o.certFile),
action.WithKeyFile(o.keyFile),
action.WithCAFile(o.caFile))
},
}

f := cmd.Flags()
f.StringVarP(&usernameOpt, "username", "u", "", "registry username")
f.StringVarP(&passwordOpt, "password", "p", "", "registry password or identity token")
f.BoolVarP(&passwordFromStdinOpt, "password-stdin", "", false, "read password or identity token from stdin")
f.BoolVarP(&insecureOpt, "insecure", "", false, "allow connections to TLS registry without certs")
f.StringVarP(&o.username, "username", "u", "", "registry username")
f.StringVarP(&o.password, "password", "p", "", "registry password or identity token")
f.BoolVarP(&o.passwordFromStdinOpt, "password-stdin", "", false, "read password or identity token from stdin")
f.StringVar(&o.certFile, "cert-file", "", "identify registry client using this SSL certificate file")
f.StringVar(&o.keyFile, "key-file", "", "identify registry client using this SSL key file")
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")

return cmd
}
Expand Down
21 changes: 15 additions & 6 deletions cmd/helm/root.go
Expand Up @@ -152,12 +152,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
flags.ParseErrorsWhitelist.UnknownFlags = true
flags.Parse(args)

registryClient, err := registry.NewClient(
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
)
registryClient, err := newRegistryClient(out)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -261,3 +256,17 @@ func checkForExpiredRepos(repofile string) {
}

}

func newRegistryClient(out io.Writer) (*registry.Client, error) {
// Create a new registry client
registryClient, err := registry.NewClient(
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
)
if err != nil {
return nil, err
}
return registryClient, nil
}
14 changes: 7 additions & 7 deletions cmd/helm/show.go
Expand Up @@ -84,7 +84,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
client.OutputFormat = action.ShowAll
output, err := runShow(args, client)
output, err := runShow(args, client, out)
if err != nil {
return err
}
Expand All @@ -101,7 +101,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
client.OutputFormat = action.ShowValues
output, err := runShow(args, client)
output, err := runShow(args, client, out)
if err != nil {
return err
}
Expand All @@ -118,7 +118,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
client.OutputFormat = action.ShowChart
output, err := runShow(args, client)
output, err := runShow(args, client, out)
if err != nil {
return err
}
Expand All @@ -135,7 +135,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
client.OutputFormat = action.ShowReadme
output, err := runShow(args, client)
output, err := runShow(args, client, out)
if err != nil {
return err
}
Expand All @@ -152,7 +152,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
client.OutputFormat = action.ShowCRDs
output, err := runShow(args, client)
output, err := runShow(args, client, out)
if err != nil {
return err
}
Expand Down Expand Up @@ -191,14 +191,14 @@ func addShowFlags(subCmd *cobra.Command, client *action.Show) {
}
}

func runShow(args []string, client *action.Show) (string, error) {
func runShow(args []string, client *action.Show, out io.Writer) (string, error) {
debug("Original chart version: %q", client.Version)
if client.Version == "" && client.Devel {
debug("setting version to >0.0.0-0")
client.Version = ">0.0.0-0"
}

cp, err := client.ChartPathOptions.LocateChart(args[0], settings)
cp, err := client.ChartPathOptions.LocateChart(args[0], out, settings)
if err != nil {
return "", err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/upgrade.go
Expand Up @@ -136,7 +136,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.Version = ">0.0.0-0"
}

chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings)
chartPath, err := client.ChartPathOptions.LocateChart(args[1], out, settings)
if err != nil {
return err
}
Expand Down
18 changes: 14 additions & 4 deletions pkg/action/install.go
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
Expand Down Expand Up @@ -675,13 +676,22 @@ OUTER:
// - URL
//
// If 'verify' was set on ChartPathOptions, this will attempt to also verify the chart.
func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (string, error) {
func (c *ChartPathOptions) LocateChart(name string, out io.Writer, settings *cli.EnvSettings) (string, error) {
// If there is no registry client and the name is in an OCI registry return
// an error and a lookup will not occur.
if registry.IsOCI(name) && c.registryClient == nil {
return "", fmt.Errorf("unable to lookup chart %q, missing registry client", name)
if registry.IsOCI(name) {
if (c.CertFile != "" && c.KeyFile != "") || c.CaFile != "" {
registryClient, err := registry.NewRegistryClientWithTLS(out, c.CertFile, c.KeyFile, c.CaFile,
settings.RegistryConfig, settings.Debug)
if err != nil {
return "", err
}
c.registryClient = registryClient
}
if c.registryClient == nil {
return "", fmt.Errorf("unable to lookup chart %q, missing registry client", name)
}
}

name = strings.TrimSpace(name)
version := strings.TrimSpace(c.Version)

Expand Down
19 changes: 19 additions & 0 deletions pkg/action/pull.go
Expand Up @@ -18,6 +18,7 @@ package action

import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
Expand Down Expand Up @@ -47,6 +48,7 @@ type Pull struct {
UntarDir string
DestDir string
cfg *Configuration
out io.Writer
}

type PullOpt func(*Pull)
Expand All @@ -57,6 +59,13 @@ func WithConfig(cfg *Configuration) PullOpt {
}
}

// WithOptWriter sets the registryOut field on the push configuration object.
func WithPullOptWriter(out io.Writer) PullOpt {
return func(p *Pull) {
p.out = out
}
}

// NewPull creates a new Pull object.
func NewPull() *Pull {
return NewPullWithOpts()
Expand Down Expand Up @@ -93,6 +102,16 @@ func (p *Pull) Run(chartRef string) (string, error) {
}

if registry.IsOCI(chartRef) {
// Provide a tls enabled client for the pull command if the user has
// specified the cert file or key file or ca file.
if (p.ChartPathOptions.CertFile != "" && p.ChartPathOptions.KeyFile != "") || p.ChartPathOptions.CaFile != "" {
registryClient, err := registry.NewRegistryClientWithTLS(p.out, p.ChartPathOptions.CertFile, p.ChartPathOptions.KeyFile, p.ChartPathOptions.CaFile,
p.Settings.RegistryConfig, p.Settings.Debug)
if err != nil {
return out.String(), err
}
p.cfg.RegistryClient = registryClient
}
c.Options = append(c.Options,
getter.WithRegistryClient(p.cfg.RegistryClient))
}
Expand Down
37 changes: 36 additions & 1 deletion pkg/action/push.go
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package action

import (
"io"
"strings"

"helm.sh/helm/v3/pkg/cli"
Expand All @@ -31,6 +32,10 @@ import (
type Push struct {
Settings *cli.EnvSettings
cfg *Configuration
certFile string
keyFile string
caFile string
out io.Writer
}

// PushOpt is a type of function that sets options for a push action.
Expand All @@ -43,6 +48,22 @@ func WithPushConfig(cfg *Configuration) PushOpt {
}
}

// WithTLSClientConfig sets the certFile, keyFile, and caFile fields on the push configuration object.
func WithTLSClientConfig(certFile, keyFile, caFile string) PushOpt {
return func(p *Push) {
p.certFile = certFile
p.keyFile = keyFile
p.caFile = caFile
}
}

// WithOptWriter sets the registryOut field on the push configuration object.
func WithPushOptWriter(out io.Writer) PushOpt {
return func(p *Push) {
p.out = out
}
}

// NewPushWithOpts creates a new push, with configuration options.
func NewPushWithOpts(opts ...PushOpt) *Push {
p := &Push{}
Expand All @@ -59,10 +80,24 @@ func (p *Push) Run(chartRef string, remote string) (string, error) {
c := uploader.ChartUploader{
Out: &out,
Pushers: pusher.All(p.Settings),
Options: []pusher.Option{},
Options: []pusher.Option{
pusher.WithTLSClientConfig(p.certFile, p.keyFile, p.caFile),
},
}

if registry.IsOCI(remote) {
// Provide a tls enabled client for the pull command if the user has
// specified the cert file or key file or ca file.
if (p.certFile != "" && p.keyFile != "") || p.caFile != "" {
registryClient, err := registry.NewRegistryClientWithTLS(p.out, p.certFile, p.keyFile, p.caFile,
p.Settings.RegistryConfig, p.Settings.Debug)
if err != nil {
return out.String(), err
}
p.cfg.RegistryClient = registryClient
}

// Don't use the default registry client if tls options are set.
c.Options = append(c.Options, pusher.WithRegistryClient(p.cfg.RegistryClient))
}

Expand Down

0 comments on commit b0ecb21

Please sign in to comment.