From 8e35ce0bde22275ebb41242155f992366d81280d Mon Sep 17 00:00:00 2001 From: Steve Chung Date: Tue, 20 Aug 2019 13:29:02 -0400 Subject: [PATCH] allow self signed certs with insecureSkipVerify (#1769) * patch velero to handle self-signed certs on client you'll get this error otherwise: x509: certificate signed by unknown authority Signed-off-by: Steven Chung --- changelogs/unreleased/1769-s12chung | 1 + pkg/cloudprovider/aws/object_store.go | 54 +++++++++++++------ pkg/cmd/cli/backup/describe.go | 8 +-- pkg/cmd/cli/backup/download.go | 14 ++--- pkg/cmd/cli/backup/logs.go | 4 +- pkg/cmd/cli/restore/describe.go | 8 +-- pkg/cmd/cli/restore/logs.go | 4 +- .../util/downloadrequest/downloadrequest.go | 15 +++++- .../downloadrequest/downloadrequest_test.go | 2 +- pkg/cmd/util/output/backup_describer.go | 13 ++--- pkg/cmd/util/output/restore_describer.go | 8 +-- .../master/api-types/backupstoragelocation.md | 1 + 12 files changed, 89 insertions(+), 43 deletions(-) create mode 100644 changelogs/unreleased/1769-s12chung diff --git a/changelogs/unreleased/1769-s12chung b/changelogs/unreleased/1769-s12chung new file mode 100644 index 000000000..5dd8dc65b --- /dev/null +++ b/changelogs/unreleased/1769-s12chung @@ -0,0 +1 @@ +adds `insecureSkipTLSVerify` server config for AWS storage and `--insecure-skip-tls-verify` flag on client for self-signed certs diff --git a/pkg/cloudprovider/aws/object_store.go b/pkg/cloudprovider/aws/object_store.go index 032eae07a..0f4e3a36b 100644 --- a/pkg/cloudprovider/aws/object_store.go +++ b/pkg/cloudprovider/aws/object_store.go @@ -17,7 +17,9 @@ limitations under the License. package aws import ( + "crypto/tls" "io" + "net/http" "sort" "strconv" "time" @@ -36,13 +38,14 @@ import ( ) const ( - s3URLKey = "s3Url" - publicURLKey = "publicUrl" - kmsKeyIDKey = "kmsKeyId" - s3ForcePathStyleKey = "s3ForcePathStyle" - bucketKey = "bucket" - signatureVersionKey = "signatureVersion" - credentialProfileKey = "profile" + s3URLKey = "s3Url" + publicURLKey = "publicUrl" + kmsKeyIDKey = "kmsKeyId" + s3ForcePathStyleKey = "s3ForcePathStyle" + bucketKey = "bucket" + signatureVersionKey = "signatureVersion" + credentialProfileKey = "profile" + insecureSkipTLSVerifyKey = "insecureSkipTLSVerify" ) type s3Interface interface { @@ -83,26 +86,29 @@ func (o *ObjectStore) Init(config map[string]string) error { s3ForcePathStyleKey, signatureVersionKey, credentialProfileKey, + insecureSkipTLSVerifyKey, ); err != nil { return err } var ( - region = config[regionKey] - s3URL = config[s3URLKey] - publicURL = config[publicURLKey] - kmsKeyID = config[kmsKeyIDKey] - s3ForcePathStyleVal = config[s3ForcePathStyleKey] - signatureVersion = config[signatureVersionKey] - credentialProfile = config[credentialProfileKey] + region = config[regionKey] + s3URL = config[s3URLKey] + publicURL = config[publicURLKey] + kmsKeyID = config[kmsKeyIDKey] + s3ForcePathStyleVal = config[s3ForcePathStyleKey] + signatureVersion = config[signatureVersionKey] + credentialProfile = config[credentialProfileKey] + insecureSkipTLSVerifyVal = config[insecureSkipTLSVerifyKey] // note that bucket is automatically added to the config map // by the server from the ObjectStorageProviderConfig so // doesn't need to be explicitly set by the user within // config. - bucket = config[bucketKey] - s3ForcePathStyle bool - err error + bucket = config[bucketKey] + s3ForcePathStyle bool + insecureSkipTLSVerify bool + err error ) if s3ForcePathStyleVal != "" { @@ -127,6 +133,20 @@ func (o *ObjectStore) Init(config map[string]string) error { return err } + if insecureSkipTLSVerifyVal != "" { + if insecureSkipTLSVerify, err = strconv.ParseBool(insecureSkipTLSVerifyVal); err != nil { + return errors.Wrapf(err, "could not parse %s (expected bool)", insecureSkipTLSVerifyKey) + } + } + + if insecureSkipTLSVerify { + serverConfig.HTTPClient = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + } + serverSession, err := getSession(serverConfig, credentialProfile) if err != nil { return err diff --git a/pkg/cmd/cli/backup/describe.go b/pkg/cmd/cli/backup/describe.go index ecc040d45..a12254960 100644 --- a/pkg/cmd/cli/backup/describe.go +++ b/pkg/cmd/cli/backup/describe.go @@ -33,8 +33,9 @@ import ( func NewDescribeCommand(f client.Factory, use string) *cobra.Command { var ( - listOptions metav1.ListOptions - details bool + listOptions metav1.ListOptions + details bool + insecureSkipTLSVerify bool ) c := &cobra.Command{ @@ -71,7 +72,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { fmt.Fprintf(os.Stderr, "error getting PodVolumeBackups for backup %s: %v\n", backup.Name, err) } - s := output.DescribeBackup(&backup, deleteRequestList.Items, podVolumeBackupList.Items, details, veleroClient) + s := output.DescribeBackup(&backup, deleteRequestList.Items, podVolumeBackupList.Items, details, veleroClient, insecureSkipTLSVerify) if first { first = false fmt.Print(s) @@ -85,6 +86,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { c.Flags().StringVarP(&listOptions.LabelSelector, "selector", "l", listOptions.LabelSelector, "only show items matching this label selector") c.Flags().BoolVar(&details, "details", details, "display additional detail in the command output") + c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "do not verify the TLS certificate for storage requests only. This is susceptible to man-in-the-middle attacks.") return c } diff --git a/pkg/cmd/cli/backup/download.go b/pkg/cmd/cli/backup/download.go index e1d99e50b..e0b8d9b64 100644 --- a/pkg/cmd/cli/backup/download.go +++ b/pkg/cmd/cli/backup/download.go @@ -52,11 +52,12 @@ func NewDownloadCommand(f client.Factory) *cobra.Command { } type DownloadOptions struct { - Name string - Output string - Force bool - Timeout time.Duration - writeOptions int + Name string + Output string + Force bool + Timeout time.Duration + InsecureSkipTLSVerify bool + writeOptions int } func NewDownloadOptions() *DownloadOptions { @@ -69,6 +70,7 @@ func (o *DownloadOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVarP(&o.Output, "output", "o", o.Output, "path to output file. Defaults to -data.tar.gz in the current directory") flags.BoolVar(&o.Force, "force", o.Force, "forces the download and will overwrite file if it exists already") flags.DurationVar(&o.Timeout, "timeout", o.Timeout, "maximum time to wait to process download request") + flags.BoolVar(&o.InsecureSkipTLSVerify, "insecure-skip-tls-verify", o.InsecureSkipTLSVerify, "do not verify the TLS certificate for storage requests only. This is susceptible to man-in-the-middle attacks.") } func (o *DownloadOptions) Validate(c *cobra.Command, args []string, f client.Factory) error { @@ -111,7 +113,7 @@ func (o *DownloadOptions) Run(c *cobra.Command, f client.Factory) error { } defer backupDest.Close() - err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), o.Name, v1.DownloadTargetKindBackupContents, backupDest, o.Timeout) + err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), o.Name, v1.DownloadTargetKindBackupContents, backupDest, o.Timeout, o.InsecureSkipTLSVerify) if err != nil { os.Remove(o.Output) cmd.CheckError(err) diff --git a/pkg/cmd/cli/backup/logs.go b/pkg/cmd/cli/backup/logs.go index 16753536d..6d32849e3 100644 --- a/pkg/cmd/cli/backup/logs.go +++ b/pkg/cmd/cli/backup/logs.go @@ -32,6 +32,7 @@ import ( func NewLogsCommand(f client.Factory) *cobra.Command { timeout := time.Minute + insecureSkipTLSVerify := false c := &cobra.Command{ Use: "logs BACKUP", @@ -58,12 +59,13 @@ func NewLogsCommand(f client.Factory) *cobra.Command { "until the backup has a phase of Completed or Failed and try again.", backupName) } - err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), backupName, v1.DownloadTargetKindBackupLog, os.Stdout, timeout) + err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), backupName, v1.DownloadTargetKindBackupLog, os.Stdout, timeout, insecureSkipTLSVerify) cmd.CheckError(err) }, } c.Flags().DurationVar(&timeout, "timeout", timeout, "how long to wait to receive logs") + c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "do not verify the TLS certificate for storage requests only. This is susceptible to man-in-the-middle attacks.") return c } diff --git a/pkg/cmd/cli/restore/describe.go b/pkg/cmd/cli/restore/describe.go index e58f1e146..a4b47d447 100644 --- a/pkg/cmd/cli/restore/describe.go +++ b/pkg/cmd/cli/restore/describe.go @@ -32,8 +32,9 @@ import ( func NewDescribeCommand(f client.Factory, use string) *cobra.Command { var ( - listOptions metav1.ListOptions - details bool + listOptions metav1.ListOptions + details bool + insecureSkipTLSVerify bool ) c := &cobra.Command{ @@ -64,7 +65,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { fmt.Fprintf(os.Stderr, "error getting PodVolumeRestores for restore %s: %v\n", restore.Name, err) } - s := output.DescribeRestore(&restore, podvolumeRestoreList.Items, details, veleroClient) + s := output.DescribeRestore(&restore, podvolumeRestoreList.Items, details, veleroClient, insecureSkipTLSVerify) if first { first = false fmt.Print(s) @@ -78,6 +79,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command { c.Flags().StringVarP(&listOptions.LabelSelector, "selector", "l", listOptions.LabelSelector, "only show items matching this label selector") c.Flags().BoolVar(&details, "details", details, "display additional detail in the command output") + c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "do not verify the TLS certificate for storage requests only. This is susceptible to man-in-the-middle attacks.") return c } diff --git a/pkg/cmd/cli/restore/logs.go b/pkg/cmd/cli/restore/logs.go index 2b6f6a14e..0eebf26a1 100644 --- a/pkg/cmd/cli/restore/logs.go +++ b/pkg/cmd/cli/restore/logs.go @@ -32,6 +32,7 @@ import ( func NewLogsCommand(f client.Factory) *cobra.Command { timeout := time.Minute + insecureSkipTLSVerify := false c := &cobra.Command{ Use: "logs RESTORE", @@ -58,12 +59,13 @@ func NewLogsCommand(f client.Factory) *cobra.Command { "until the restore has a phase of Completed or Failed and try again.", restoreName) } - err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), restoreName, v1.DownloadTargetKindRestoreLog, os.Stdout, timeout) + err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), restoreName, v1.DownloadTargetKindRestoreLog, os.Stdout, timeout, insecureSkipTLSVerify) cmd.CheckError(err) }, } c.Flags().DurationVar(&timeout, "timeout", timeout, "how long to wait to receive logs") + c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "do not verify the TLS certificate for storage requests only. This is susceptible to man-in-the-middle attacks.") return c } diff --git a/pkg/cmd/util/downloadrequest/downloadrequest.go b/pkg/cmd/util/downloadrequest/downloadrequest.go index 2cf129e83..eff9f2dd6 100644 --- a/pkg/cmd/util/downloadrequest/downloadrequest.go +++ b/pkg/cmd/util/downloadrequest/downloadrequest.go @@ -18,10 +18,13 @@ package downloadrequest import ( "compress/gzip" + "crypto/tls" + "crypto/x509" "fmt" "io" "io/ioutil" "net/http" + "net/url" "time" "github.com/pkg/errors" @@ -36,7 +39,7 @@ import ( // not found var ErrNotFound = errors.New("file not found") -func Stream(client velerov1client.DownloadRequestsGetter, namespace, name string, kind v1.DownloadTargetKind, w io.Writer, timeout time.Duration) error { +func Stream(client velerov1client.DownloadRequestsGetter, namespace, name string, kind v1.DownloadTargetKind, w io.Writer, timeout time.Duration, insecureSkipTLSVerify bool) error { req := &v1.DownloadRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, @@ -105,6 +108,11 @@ Loop: } httpClient := new(http.Client) + if insecureSkipTLSVerify { + httpClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + } httpReq, err := http.NewRequest("GET", req.Status.DownloadURL, nil) if err != nil { @@ -118,6 +126,11 @@ Loop: resp, err := httpClient.Do(httpReq) if err != nil { + if urlErr, ok := err.(*url.Error); ok { + if _, ok := urlErr.Err.(x509.UnknownAuthorityError); ok { + return fmt.Errorf(err.Error() + "\n\nThe --insecure-skip-tls-verify flag can also be used to accept any TLS certificate for the download, but it is susceptible to man-in-the-middle attacks.") + } + } return err } defer resp.Body.Close() diff --git a/pkg/cmd/util/downloadrequest/downloadrequest_test.go b/pkg/cmd/util/downloadrequest/downloadrequest_test.go index 278b54c69..49094a5c2 100644 --- a/pkg/cmd/util/downloadrequest/downloadrequest_test.go +++ b/pkg/cmd/util/downloadrequest/downloadrequest_test.go @@ -151,7 +151,7 @@ func TestStream(t *testing.T) { output := new(bytes.Buffer) errCh := make(chan error) go func() { - err := Stream(client.VeleroV1(), "namespace", "name", test.kind, output, timeout) + err := Stream(client.VeleroV1(), "namespace", "name", test.kind, output, timeout, false) errCh <- err }() diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index c1fd8b1bf..27aaf7698 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -38,6 +38,7 @@ func DescribeBackup( podVolumeBackups []velerov1api.PodVolumeBackup, details bool, veleroClient clientset.Interface, + insecureSkipTLSVerify bool, ) string { return Describe(func(d *Describer) { d.DescribeMetadata(backup.ObjectMeta) @@ -74,7 +75,7 @@ func DescribeBackup( DescribeBackupSpec(d, backup.Spec) d.Println() - DescribeBackupStatus(d, backup, details, veleroClient) + DescribeBackupStatus(d, backup, details, veleroClient, insecureSkipTLSVerify) if len(deleteRequests) > 0 { d.Println() @@ -211,7 +212,7 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) { } // DescribeBackupStatus describes a backup status in human-readable format. -func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool, veleroClient clientset.Interface) { +func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool) { status := backup.Status d.Printf("Backup Format Version:\t%d\n", status.Version) @@ -234,7 +235,7 @@ func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool d.Println() if details { - describeBackupResourceList(d, backup, veleroClient) + describeBackupResourceList(d, backup, veleroClient, insecureSkipTLSVerify) d.Println() } @@ -245,7 +246,7 @@ func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool } buf := new(bytes.Buffer) - if err := downloadrequest.Stream(veleroClient.VeleroV1(), backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeSnapshots, buf, downloadRequestTimeout); err != nil { + if err := downloadrequest.Stream(veleroClient.VeleroV1(), backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeSnapshots, buf, downloadRequestTimeout, insecureSkipTLSVerify); err != nil { d.Printf("Persistent Volumes:\t\n", err) return } @@ -266,9 +267,9 @@ func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool d.Printf("Persistent Volumes: \n") } -func describeBackupResourceList(d *Describer, backup *velerov1api.Backup, veleroClient clientset.Interface) { +func describeBackupResourceList(d *Describer, backup *velerov1api.Backup, veleroClient clientset.Interface, insecureSkipTLSVerify bool) { buf := new(bytes.Buffer) - if err := downloadrequest.Stream(veleroClient.VeleroV1(), backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout); err != nil { + if err := downloadrequest.Stream(veleroClient.VeleroV1(), backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify); err != nil { if err == downloadrequest.ErrNotFound { d.Println("Resource List:\t") } else { diff --git a/pkg/cmd/util/output/restore_describer.go b/pkg/cmd/util/output/restore_describer.go index dada2aca4..5182e219a 100644 --- a/pkg/cmd/util/output/restore_describer.go +++ b/pkg/cmd/util/output/restore_describer.go @@ -31,7 +31,7 @@ import ( pkgrestore "github.com/heptio/velero/pkg/restore" ) -func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestore, details bool, veleroClient clientset.Interface) string { +func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestore, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool) string { return Describe(func(d *Describer) { d.DescribeMetadata(restore.ObjectMeta) @@ -56,7 +56,7 @@ func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestor } } - describeRestoreResults(d, restore, veleroClient) + describeRestoreResults(d, restore, veleroClient, insecureSkipTLSVerify) d.Println() d.Printf("Backup:\t%s\n", restore.Spec.BackupName) @@ -114,7 +114,7 @@ func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestor }) } -func describeRestoreResults(d *Describer, restore *v1.Restore, veleroClient clientset.Interface) { +func describeRestoreResults(d *Describer, restore *v1.Restore, veleroClient clientset.Interface, insecureSkipTLSVerify bool) { if restore.Status.Warnings == 0 && restore.Status.Errors == 0 { return } @@ -122,7 +122,7 @@ func describeRestoreResults(d *Describer, restore *v1.Restore, veleroClient clie var buf bytes.Buffer var resultMap map[string]pkgrestore.Result - if err := downloadrequest.Stream(veleroClient.VeleroV1(), restore.Namespace, restore.Name, v1.DownloadTargetKindRestoreResults, &buf, downloadRequestTimeout); err != nil { + if err := downloadrequest.Stream(veleroClient.VeleroV1(), restore.Namespace, restore.Name, v1.DownloadTargetKindRestoreResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify); err != nil { d.Printf("Warnings:\t\n\nErrors:\t\n", err, err) return } diff --git a/site/docs/master/api-types/backupstoragelocation.md b/site/docs/master/api-types/backupstoragelocation.md index 07b65ccf5..0cd4b4b80 100644 --- a/site/docs/master/api-types/backupstoragelocation.md +++ b/site/docs/master/api-types/backupstoragelocation.md @@ -53,6 +53,7 @@ The configurable parameters are as follows: | `kmsKeyId` | string | Empty | *Example*: "502b409c-4da1-419f-a16e-eif453b3i49f" or "alias/``"

Specify an [AWS KMS key][10] id or alias to enable encryption of the backups stored in S3. Only works with AWS S3 and may require explicitly granting key usage rights.| | `signatureVersion` | string | `"4"` | Version of the signature algorithm used to create signed URLs that are used by velero cli to download backups or fetch logs. Possible versions are "1" and "4". Usually the default version 4 is correct, but some S3-compatible providers like Quobyte only support version 1.| | `profile` | string | "default" | AWS profile within the credential file to use for given store | +| `insecureSkipTLSVerify` | bool | `false` | Set this to `true` if you do not want to verify the TLS certificate for storage requests only--like self-signed certs in Minio. This is susceptible to man-in-the-middle attacks and is not recommended for production. | #### Azure