Commit f1d07c3f authored by Matt Butcher's avatar Matt Butcher
Browse files

feat(helm): add --verify flag to commands

This adds the --verify and --keyring flags to:

helm fetch
helm inspect
helm install
helm upgrade

Each of these commands can now make cryptographic verification a
prerequisite for using a chart.
parent d80df934
main Release add-codeql dependabot/go_modules/github.com/docker/distribution-2.8.2incompatible dependabot/go_modules/github.com/lib/pq-1.10.9 dependabot/go_modules/github.com/rubenv/sql-migrate-1.4.0 dependabot/go_modules/golang.org/x/crypto-0.9.0 dependabot/go_modules/golang.org/x/term-0.8.0 dependabot/go_modules/k8s.io/klog/v2-2.100.1 dev-v2 feat-v3/event-emitter-lua kube-update-test release-2.0 release-2.1 release-2.10 release-2.11 release-2.12 release-2.13 release-2.14 release-2.15 release-2.16 release-2.17 release-2.2 release-2.3 release-2.4 release-2.5 release-2.6 release-2.7 release-2.8 release-2.9 release-3.0 release-3.1 release-3.10 release-3.11 release-3.12 release-3.2 release-3.3 release-3.4 release-3.5 release-3.6 release-3.6.1 release-3.6.2 release-3.7 release-3.8 release-3.9 release-v3.0.0-beta.4 v3.12.0 v3.12.0-rc.1 v3.12.0-dev.1 v3.11.3 v3.11.2 v3.11.1 v3.11.0 v3.11.0-rc.2 v3.11.0-rc.1 v3.10.3 v3.10.2 v3.10.1 v3.10.0 v3.10.0-rc.1 v3.9.4 v3.9.3 v3.9.2 v3.9.1 v3.9.0 v3.9.0-rc.1 v3.8.2 v3.8.1 v3.8.0 v3.8.0-rc.2 v3.8.0-rc.1 v3.7.2 v3.7.1 v3.7.0 v3.7.0-rc.3 v3.7.0-rc.2 v3.7.0-rc.1 v3.6.3 v3.6.2 v3.6.1 v3.6.0 v3.6.0-rc.1 v3.5.4 v3.5.3 v3.5.2 v3.5.1 v3.5.0 v3.5.0-rc.2 v3.5.0-rc.1 v3.4.2 v3.4.1 v3.4.0 v3.4.0-rc.1 v3.3.4 v3.3.3 v3.3.2 v3.3.1 v3.3.0 v3.3.0-rc.2 v3.3.0-rc.1 v3.2.4 v3.2.3 v3.2.2 v3.2.1 v3.2.0 v3.2.0-rc.1 v3.1.3 v3.1.2 v3.1.1 v3.1.0 v3.1.0-rc.3 v3.1.0-rc.2 v3.1.0-rc.1 v3.0.3 v3.0.2 v3.0.1 v3.0.0 v3.0.0-rc.4 v3.0.0-rc.3 v3.0.0-rc.2 v3.0.0-rc.1 v3.0.0-beta.5 v3.0.0-beta.4 v3.0.0-beta.3 v3.0.0-beta.2 v3.0.0-beta.1 v3.0.0-alpha.2 v3.0.0-alpha.1 v2.17.0 v2.17.0-rc.1 v2.16.12 v2.16.11 v2.16.10 v2.16.9 v2.16.8 v2.16.7 v2.16.6 v2.16.5 v2.16.4 v2.16.3 v2.16.2 v2.16.1 v2.16.0 v2.16.0-rc.2 v2.16.0-rc.1 v2.15.2 v2.15.1 v2.15.0 v2.15.0-rc.2 v2.15.0-rc.1 v2.14.3 v2.14.2 v2.14.1 v2.14.0 v2.14.0-rc.2 v2.14.0-rc.1 v2.13.1 v2.13.1-rc.1 v2.13.0 v2.13.0-rc.2 v2.13.0-rc.1 v2.12.3 v2.12.2 v2.12.1 v2.12.0 v2.12.0-rc.2 v2.12.0-rc.1 v2.11.0 v2.11.0-rc.4 v2.11.0-rc.3 v2.11.0-rc.2 v2.11.0-rc.1 v2.10.0 v2.10.0-rc.3 v2.10.0-rc.2 v2.10.0-rc.1 v2.9.1 v2.9.0 v2.9.0-rc5 v2.9.0-rc4 v2.9.0-rc3 v2.9.0-rc2 v2.9.0-rc1 v2.8.2 v2.8.2-rc1 v2.8.1 v2.8.0 v2.8.0-rc.1 v2.7.2 v2.7.1 v2.7.0 v2.7.0-rc1 v2.6.2 v2.6.1 v2.6.0 v2.5.1 v2.5.0 v2.4.2 v2.4.1 v2.4.0 v2.3.1 v2.3.0 v2.2.3 v2.2.2 v2.2.1 v2.2.0 v2.1.3 v2.1.2 v2.1.1 v2.1.0 v2.0.2 v2.0.1 v2.0.0 v2.0.0-rc.2 v2.0.0-rc.1 v2.0.0-beta.2 v2.0.0-beta.1 v2.0.0-alpha.5 v2.0.0-alpha.4
No related merge requests found
Showing with 360 additions and 48 deletions
+360 -48
...@@ -17,8 +17,11 @@ limitations under the License. ...@@ -17,8 +17,11 @@ limitations under the License.
package main package main
import ( import (
"bytes"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
...@@ -27,65 +30,179 @@ import ( ...@@ -27,65 +30,179 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/provenance"
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
) )
var untarFile bool const fetchDesc = `
var untarDir string Retrieve a package from a package repository, and download it locally.
func init() { This is useful for fetching packages to inspect, modify, or repackage. It can
RootCommand.AddCommand(fetchCmd) also be used to perform cryptographic verification of a chart without installing
fetchCmd.Flags().BoolVar(&untarFile, "untar", false, "If set to true, will untar the chart after downloading it.") the chart.
fetchCmd.Flags().StringVar(&untarDir, "untardir", ".", "If untar is specified, this flag specifies where to untar the chart.")
} There are options for unpacking the chart after download. This will create a
directory for the chart and uncomparess into that directory.
If the --verify flag is specified, the requested chart MUST have a provenance
file, and MUST pass the verification process. Failure in any part of this will
result in an error, and the chart will not be saved locally.
`
var fetchCmd = &cobra.Command{ type fetchCmd struct {
Use: "fetch [chart URL | repo/chartname]", untar bool
Short: "download a chart from a repository and (optionally) unpack it in local directory", untardir string
Long: "", chartRef string
RunE: fetch,
verify bool
keyring string
out io.Writer
} }
func fetch(cmd *cobra.Command, args []string) error { func newFetchCmd(out io.Writer) *cobra.Command {
if len(args) == 0 { fch := &fetchCmd{out: out}
return fmt.Errorf("This command needs at least one argument, url or repo/name of the chart.")
cmd := &cobra.Command{
Use: "fetch [chart URL | repo/chartname]",
Short: "download a chart from a repository and (optionally) unpack it in local directory",
Long: fetchDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("This command needs at least one argument, url or repo/name of the chart.")
}
fch.chartRef = args[0]
return fch.run()
},
} }
pname := args[0] f := cmd.Flags()
f.BoolVar(&fch.untar, "untar", false, "If set to true, will untar the chart after downloading it.")
f.StringVar(&fch.untardir, "untardir", ".", "If untar is specified, this flag specifies where to untar the chart.")
f.BoolVar(&fch.verify, "verify", false, "Verify the package against its signature.")
f.StringVar(&fch.keyring, "keyring", defaultKeyring(), "The keyring containing public keys.")
return cmd
}
func (f *fetchCmd) run() error {
pname := f.chartRef
if filepath.Ext(pname) != ".tgz" { if filepath.Ext(pname) != ".tgz" {
pname += ".tgz" pname += ".tgz"
} }
return fetchChart(pname)
return downloadChart(pname, f.untar, f.untardir, f.verify, f.keyring)
} }
func fetchChart(pname string) error { // downloadChart fetches a chart over HTTP, and then (if verify is true) verifies it.
//
f, err := repo.LoadRepositoriesFile(repositoriesFile()) // If untar is true, it also unpacks the file into untardir.
func downloadChart(pname string, untar bool, untardir string, verify bool, keyring string) error {
r, err := repo.LoadRepositoriesFile(repositoriesFile())
if err != nil { if err != nil {
return err return err
} }
// get download url // get download url
u, err := mapRepoArg(pname, f.Repositories) u, err := mapRepoArg(pname, r.Repositories)
if err != nil { if err != nil {
return err return err
} }
resp, err := http.Get(u.String()) href := u.String()
buf, err := fetchChart(href)
if err != nil { if err != nil {
return err return err
} }
if resp.StatusCode != 200 {
return fmt.Errorf("Failed to fetch %s : %s", u.String(), resp.Status) if verify {
basename := filepath.Base(pname)
sigref := href + ".prov"
sig, err := fetchChart(sigref)
if err != nil {
return fmt.Errorf("provenance data not downloaded from %s: %s", sigref, err)
}
if err := ioutil.WriteFile(basename+".prov", sig.Bytes(), 0755); err != nil {
return fmt.Errorf("provenance data not saved: %s", err)
}
if err := verifyChart(basename, keyring); err != nil {
return err
}
} }
defer resp.Body.Close() return saveChart(pname, buf, untar, untardir)
if untarFile { }
return chartutil.Expand(untarDir, resp.Body)
// verifyChart takes a path to a chart archive and a keyring, and verifies the chart.
//
// It assumes that a chart archive file is accompanied by a provenance file whose
// name is the archive file name plus the ".prov" extension.
func verifyChart(path string, keyring string) error {
// For now, error out if it's not a tar file.
if fi, err := os.Stat(path); err != nil {
return err
} else if fi.IsDir() {
return errors.New("unpacked charts cannot be verified")
} else if !isTar(path) {
return errors.New("chart must be a tgz file")
} }
p := strings.Split(u.String(), "/")
return saveChartFile(p[len(p)-1], resp.Body) provfile := path + ".prov"
if _, err := os.Stat(provfile); err != nil {
return fmt.Errorf("could not load provenance file %s: %s", provfile, err)
}
sig, err := provenance.NewFromKeyring(keyring, "")
if err != nil {
return fmt.Errorf("failed to load keyring: %s", err)
}
ver, err := sig.Verify(path, provfile)
if flagDebug {
for name := range ver.SignedBy.Identities {
fmt.Printf("Signed by %q\n", name)
}
}
return err
}
// defaultKeyring returns the expanded path to the default keyring.
func defaultKeyring() string {
return os.ExpandEnv("$HOME/.gnupg/pubring.gpg")
}
// isTar tests whether the given file is a tar file.
//
// Currently, this simply checks extension, since a subsequent function will
// untar the file and validate its binary format.
func isTar(filename string) bool {
return strings.ToLower(filepath.Ext(filename)) == ".tgz"
}
// saveChart saves a chart locally.
func saveChart(name string, buf *bytes.Buffer, untar bool, untardir string) error {
if untar {
return chartutil.Expand(untardir, buf)
}
p := strings.Split(name, "/")
return saveChartFile(p[len(p)-1], buf)
}
// fetchChart retrieves a chart over HTTP.
func fetchChart(href string) (*bytes.Buffer, error) {
buf := bytes.NewBuffer(nil)
resp, err := http.Get(href)
if err != nil {
return buf, err
}
if resp.StatusCode != 200 {
return buf, fmt.Errorf("Failed to fetch %s : %s", href, resp.Status)
}
_, err = io.Copy(buf, resp.Body)
resp.Body.Close()
return buf, err
} }
// mapRepoArg figures out which format the argument is given, and creates a fetchable // mapRepoArg figures out which format the argument is given, and creates a fetchable
......
...@@ -95,6 +95,8 @@ func newRootCmd(out io.Writer) *cobra.Command { ...@@ -95,6 +95,8 @@ func newRootCmd(out io.Writer) *cobra.Command {
newUpgradeCmd(nil, out), newUpgradeCmd(nil, out),
newRollbackCmd(nil, out), newRollbackCmd(nil, out),
newPackageCmd(nil, out), newPackageCmd(nil, out),
newFetchCmd(out),
newVerifyCmd(out),
) )
return cmd return cmd
} }
......
...@@ -46,6 +46,8 @@ of the Charts.yaml file ...@@ -46,6 +46,8 @@ of the Charts.yaml file
type inspectCmd struct { type inspectCmd struct {
chartpath string chartpath string
output string output string
verify bool
keyring string
out io.Writer out io.Writer
client helm.Interface client helm.Interface
} }
...@@ -71,7 +73,7 @@ func newInspectCmd(c helm.Interface, out io.Writer) *cobra.Command { ...@@ -71,7 +73,7 @@ func newInspectCmd(c helm.Interface, out io.Writer) *cobra.Command {
if err := checkArgsLength(1, len(args), "chart name"); err != nil { if err := checkArgsLength(1, len(args), "chart name"); err != nil {
return err return err
} }
cp, err := locateChartPath(args[0]) cp, err := locateChartPath(args[0], insp.verify, insp.keyring)
if err != nil { if err != nil {
return err return err
} }
...@@ -86,7 +88,7 @@ func newInspectCmd(c helm.Interface, out io.Writer) *cobra.Command { ...@@ -86,7 +88,7 @@ func newInspectCmd(c helm.Interface, out io.Writer) *cobra.Command {
Long: inspectValuesDesc, Long: inspectValuesDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
insp.output = valuesOnly insp.output = valuesOnly
cp, err := locateChartPath(args[0]) cp, err := locateChartPath(args[0], insp.verify, insp.keyring)
if err != nil { if err != nil {
return err return err
} }
...@@ -101,7 +103,7 @@ func newInspectCmd(c helm.Interface, out io.Writer) *cobra.Command { ...@@ -101,7 +103,7 @@ func newInspectCmd(c helm.Interface, out io.Writer) *cobra.Command {
Long: inspectChartDesc, Long: inspectChartDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
insp.output = chartOnly insp.output = chartOnly
cp, err := locateChartPath(args[0]) cp, err := locateChartPath(args[0], insp.verify, insp.keyring)
if err != nil { if err != nil {
return err return err
} }
...@@ -110,6 +112,19 @@ func newInspectCmd(c helm.Interface, out io.Writer) *cobra.Command { ...@@ -110,6 +112,19 @@ func newInspectCmd(c helm.Interface, out io.Writer) *cobra.Command {
}, },
} }
vflag := "verify"
vdesc := "verify the provenance data for this chart"
inspectCommand.Flags().BoolVar(&insp.verify, vflag, false, vdesc)
valuesSubCmd.Flags().BoolVar(&insp.verify, vflag, false, vdesc)
chartSubCmd.Flags().BoolVar(&insp.verify, vflag, false, vdesc)
kflag := "keyring"
kdesc := "the path to the keyring containing public verification keys"
kdefault := defaultKeyring()
inspectCommand.Flags().StringVar(&insp.keyring, kflag, kdefault, kdesc)
valuesSubCmd.Flags().StringVar(&insp.keyring, kflag, kdefault, kdesc)
chartSubCmd.Flags().StringVar(&insp.keyring, kflag, kdefault, kdesc)
inspectCommand.AddCommand(valuesSubCmd) inspectCommand.AddCommand(valuesSubCmd)
inspectCommand.AddCommand(chartSubCmd) inspectCommand.AddCommand(chartSubCmd)
......
...@@ -18,6 +18,7 @@ package main ...@@ -18,6 +18,7 @@ package main
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
...@@ -54,6 +55,9 @@ or ...@@ -54,6 +55,9 @@ or
To check the generated manifests of a release without installing the chart, To check the generated manifests of a release without installing the chart,
the '--debug' and '--dry-run' flags can be combined. This will still require a the '--debug' and '--dry-run' flags can be combined. This will still require a
round-trip to the Tiller server. round-trip to the Tiller server.
If --verify is set, the chart MUST have a provenance file, and the provenenace
fall MUST pass all verification steps.
` `
type installCmd struct { type installCmd struct {
...@@ -64,6 +68,8 @@ type installCmd struct { ...@@ -64,6 +68,8 @@ type installCmd struct {
dryRun bool dryRun bool
disableHooks bool disableHooks bool
replace bool replace bool
verify bool
keyring string
out io.Writer out io.Writer
client helm.Interface client helm.Interface
values *values values *values
...@@ -86,7 +92,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { ...@@ -86,7 +92,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
if err := checkArgsLength(1, len(args), "chart name"); err != nil { if err := checkArgsLength(1, len(args), "chart name"); err != nil {
return err return err
} }
cp, err := locateChartPath(args[0]) cp, err := locateChartPath(args[0], inst.verify, inst.keyring)
if err != nil { if err != nil {
return err return err
} }
...@@ -106,6 +112,8 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { ...@@ -106,6 +112,8 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&inst.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production") f.BoolVar(&inst.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production")
f.Var(inst.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2") f.Var(inst.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2")
f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release") f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release")
f.BoolVar(&inst.verify, "verify", false, "verify the package before installing it")
f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
return cmd return cmd
} }
...@@ -171,6 +179,7 @@ func (i *installCmd) vals() ([]byte, error) { ...@@ -171,6 +179,7 @@ func (i *installCmd) vals() ([]byte, error) {
return buffer.Bytes(), nil return buffer.Bytes(), nil
} }
// printRelease prints info about a release if the flagDebug is true.
func (i *installCmd) printRelease(rel *release.Release) { func (i *installCmd) printRelease(rel *release.Release) {
if rel == nil { if rel == nil {
return return
...@@ -251,9 +260,23 @@ func splitPair(item string) (name string, value interface{}) { ...@@ -251,9 +260,23 @@ func splitPair(item string) (name string, value interface{}) {
// - current working directory // - current working directory
// - if path is absolute or begins with '.', error out here // - if path is absolute or begins with '.', error out here
// - chart repos in $HELM_HOME // - chart repos in $HELM_HOME
func locateChartPath(name string) (string, error) { //
if _, err := os.Stat(name); err == nil { // If 'verify' is true, this will attempt to also verify the chart.
return filepath.Abs(name) func locateChartPath(name string, verify bool, keyring string) (string, error) {
if fi, err := os.Stat(name); err == nil {
abs, err := filepath.Abs(name)
if err != nil {
return abs, err
}
if verify {
if fi.IsDir() {
return "", errors.New("cannot verify a directory")
}
if err := verifyChart(abs, keyring); err != nil {
return "", err
}
}
return abs, nil
} }
if filepath.IsAbs(name) || strings.HasPrefix(name, ".") { if filepath.IsAbs(name) || strings.HasPrefix(name, ".") {
return name, fmt.Errorf("path %q not found", name) return name, fmt.Errorf("path %q not found", name)
...@@ -269,7 +292,7 @@ func locateChartPath(name string) (string, error) { ...@@ -269,7 +292,7 @@ func locateChartPath(name string) (string, error) {
if filepath.Ext(name) != ".tgz" { if filepath.Ext(name) != ".tgz" {
name += ".tgz" name += ".tgz"
} }
if err := fetchChart(name); err == nil { if err := downloadChart(name, false, ".", verify, keyring); err == nil {
lname, err := filepath.Abs(filepath.Base(name)) lname, err := filepath.Abs(filepath.Base(name))
if err != nil { if err != nil {
return lname, err return lname, err
......
...@@ -74,6 +74,24 @@ func TestInstall(t *testing.T) { ...@@ -74,6 +74,24 @@ func TestInstall(t *testing.T) {
expected: "FOOBAR", expected: "FOOBAR",
resp: releaseMock(&releaseOptions{name: "FOOBAR"}), resp: releaseMock(&releaseOptions{name: "FOOBAR"}),
}, },
// Install, perform chart verification along the way.
{
name: "install with verification, missing provenance",
args: []string{"testdata/testcharts/compressedchart-0.1.0.tgz"},
flags: strings.Split("--verify --keyring testdata/helm-test-key.pub", " "),
err: true,
},
{
name: "install with verification, directory instead of file",
args: []string{"testdata/testcharts/signtest"},
flags: strings.Split("--verify --keyring testdata/helm-test-key.pub", " "),
err: true,
},
{
name: "install with verification, valid",
args: []string{"testdata/testcharts/signtest-0.1.0.tgz"},
flags: strings.Split("--verify --keyring testdata/helm-test-key.pub", " "),
},
} }
runReleaseCases(t, tests, func(c *fakeReleaseClient, out io.Writer) *cobra.Command { runReleaseCases(t, tests, func(c *fakeReleaseClient, out io.Writer) *cobra.Command {
......
...@@ -28,7 +28,6 @@ func TestListCmd(t *testing.T) { ...@@ -28,7 +28,6 @@ func TestListCmd(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args []string args []string
flags map[string]string
resp []*release.Release resp []*release.Release
expected string expected string
err bool err bool
...@@ -41,8 +40,9 @@ func TestListCmd(t *testing.T) { ...@@ -41,8 +40,9 @@ func TestListCmd(t *testing.T) {
expected: "thomas-guide", expected: "thomas-guide",
}, },
{ {
name: "list --long", name: "list --long",
flags: map[string]string{"long": "1"}, //flags: map[string]string{"long": "1"},
args: []string{"--long"},
resp: []*release.Release{ resp: []*release.Release{
releaseMock(&releaseOptions{name: "atlas"}), releaseMock(&releaseOptions{name: "atlas"}),
}, },
...@@ -56,7 +56,7 @@ func TestListCmd(t *testing.T) { ...@@ -56,7 +56,7 @@ func TestListCmd(t *testing.T) {
rels: tt.resp, rels: tt.resp,
} }
cmd := newListCmd(c, &buf) cmd := newListCmd(c, &buf)
setFlags(cmd, tt.flags) cmd.ParseFlags(tt.args)
err := cmd.RunE(cmd, tt.args) err := cmd.RunE(cmd, tt.args)
if (err != nil) != tt.err { if (err != nil) != tt.err {
t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err)
......
...@@ -43,11 +43,6 @@ Chart.yaml file, and (if found) build the current directory into a chart. ...@@ -43,11 +43,6 @@ Chart.yaml file, and (if found) build the current directory into a chart.
Versioned chart archives are used by Helm package repositories. Versioned chart archives are used by Helm package repositories.
` `
const (
envSigningKey = "HELM_SIGNING_KEY"
envKeyring = "HELM_KEYRING"
)
type packageCmd struct { type packageCmd struct {
save bool save bool
sign bool sign bool
...@@ -86,7 +81,7 @@ func newPackageCmd(client helm.Interface, out io.Writer) *cobra.Command { ...@@ -86,7 +81,7 @@ func newPackageCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository") f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository")
f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package") f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package")
f.StringVar(&pkg.key, "key", "", "the name of the key to use when signing. Used if --sign is true.") f.StringVar(&pkg.key, "key", "", "the name of the key to use when signing. Used if --sign is true.")
f.StringVar(&pkg.keyring, "keyring", os.ExpandEnv("$HOME/.gnupg/pubring.gpg"), "the location of a public keyring") f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "the location of a public keyring")
return cmd return cmd
} }
......
File added
File added
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
description: A Helm chart for Kubernetes
name: signtest
version: 0.1.0
...
files:
signtest-0.1.0.tgz: sha256:dee72947753628425b82814516bdaa37aef49f25e8820dd2a6e15a33a007823b
-----BEGIN PGP SIGNATURE-----
wsBcBAEBCgAQBQJXomNHCRCEO7+YH8GHYgAALywIAG1Me852Fpn1GYu8Q1GCcw4g
l2k7vOFchdDwDhdSVbkh4YyvTaIO3iE2Jtk1rxw+RIJiUr0eLO/rnIJuxZS8WKki
DR1LI9J1VD4dxN3uDETtWDWq7ScoPsRY5mJvYZXC8whrWEt/H2kfqmoA9LloRPWp
flOE0iktA4UciZOblTj6nAk3iDyjh/4HYL4a6tT0LjjKI7OTw4YyHfjHad1ywVCz
9dMUc1rPgTnl+fnRiSPSrlZIWKOt1mcQ4fVrU3nwtRUwTId2k8FtygL0G6M+Y6t0
S6yaU7qfk9uTxkdkUF7Bf1X3ukxfe+cNBC32vf4m8LY4NkcYfSqK2fGtQsnVr6s=
=NyOM
-----END PGP SIGNATURE-----
\ No newline at end of file
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
.git
description: A Helm chart for Kubernetes
name: signtest
version: 0.1.0
description: Deploy a basic Alpine Linux pod
home: https://k8s.io/helm
name: alpine
sources:
- https://github.com/kubernetes/helm
version: 0.1.0
This example was generated using the command `helm create alpine`.
The `templates/` directory contains a very simple pod resource with a
couple of parameters.
The `values.yaml` file contains the default values for the
`alpine-pod.yaml` template.
You can install this example using `helm install docs/examples/alpine`.
apiVersion: v1
kind: Pod
metadata:
name: {{.Release.Name}}-{{.Chart.Name}}
labels:
heritage: {{.Release.Service}}
chartName: {{.Chart.Name}}
chartVersion: {{.Chart.Version | quote}}
annotations:
"helm.sh/created": "{{.Release.Time.Seconds}}"
spec:
restartPolicy: {{default "Never" .restart_policy}}
containers:
- name: waiter
image: "alpine:3.3"
command: ["/bin/sleep","9000"]
# The pod name
name: my-alpine
apiVersion: v1
kind: Pod
metadata:
name: signtest
spec:
restartPolicy: Never
containers:
- name: waiter
image: "alpine:3.3"
command: ["/bin/sleep","9000"]
...@@ -46,6 +46,8 @@ type upgradeCmd struct { ...@@ -46,6 +46,8 @@ type upgradeCmd struct {
disableHooks bool disableHooks bool
valuesFile string valuesFile string
values *values values *values
verify bool
keyring string
} }
func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
...@@ -79,6 +81,8 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { ...@@ -79,6 +81,8 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade") f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade")
f.Var(upgrade.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2") f.Var(upgrade.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2")
f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks") f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks")
f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading")
f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "the path to the keyring that contains public singing keys")
return cmd return cmd
} }
...@@ -109,7 +113,7 @@ func (u *upgradeCmd) vals() ([]byte, error) { ...@@ -109,7 +113,7 @@ func (u *upgradeCmd) vals() ([]byte, error) {
} }
func (u *upgradeCmd) run() error { func (u *upgradeCmd) run() error {
chartPath, err := locateChartPath(u.chart) chartPath, err := locateChartPath(u.chart, u.verify, u.keyring)
if err != nil { if err != nil {
return err return err
} }
......
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 main
import (
"errors"
"io"
"github.com/spf13/cobra"
)
const verifyDesc = `
Verify that the given chart has a valid provenance file.
Provenance files provide crytographic verification that a chart has not been
tampered with, and was packaged by a trusted provider.
This command can be used to verify a local chart. Several other commands provide
'--verify' flags that run the same validation. To generate a signed package, use
the 'helm package --sign' command.
`
type verifyCmd struct {
keyring string
chartfile string
out io.Writer
}
func newVerifyCmd(out io.Writer) *cobra.Command {
vc := &verifyCmd{out: out}
cmd := &cobra.Command{
Use: "verify [flags] PATH",
Short: "verify that a chart at the given path has been signed and is valid",
Long: verifyDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("a path to a package file is required")
}
vc.chartfile = args[0]
return vc.run()
},
}
f := cmd.Flags()
f.StringVar(&vc.keyring, "keyring", defaultKeyring(), "the keyring containing public keys.")
return cmd
}
func (v *verifyCmd) run() error {
return verifyChart(v.chartfile, v.keyring)
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment