Skip to content

Commit

Permalink
Add --cacert flag to velero cli commands
Browse files Browse the repository at this point in the history
Adds a --cacert flag to the log and describe commands
that takes a path to a PEM-encoded certificate bundle
as an alternative to --insecure-skip-tls-verify for
dealing with self-signed certificates.

Signed-off-by: Sam Lucidi <slucidi@redhat.com>
  • Loading branch information
mansam committed Mar 25, 2020
1 parent 4d49b59 commit 32be406
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 22 deletions.
5 changes: 3 additions & 2 deletions pkg/cmd/cli/backup/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command {
listOptions metav1.ListOptions
details bool
insecureSkipTLSVerify bool
caCertPath string
)

c := &cobra.Command{
Expand Down Expand Up @@ -72,7 +73,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, insecureSkipTLSVerify)
s := output.DescribeBackup(&backup, deleteRequestList.Items, podVolumeBackupList.Items, details, veleroClient, insecureSkipTLSVerify, caCertPath)
if first {
first = false
fmt.Print(s)
Expand All @@ -87,6 +88,6 @@ 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, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")

c.Flags().StringVar(&caCertPath, "cacert", caCertPath, "path to a certificate bundle to use when verifying TLS connections")
return c
}
5 changes: 4 additions & 1 deletion pkg/cmd/cli/backup/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type DownloadOptions struct {
Timeout time.Duration
InsecureSkipTLSVerify bool
writeOptions int
caCertPath string
}

func NewDownloadOptions() *DownloadOptions {
Expand All @@ -71,6 +72,8 @@ func (o *DownloadOptions) BindFlags(flags *pflag.FlagSet) {
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, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
flags.StringVar(&o.caCertPath, "cacert", o.caCertPath, "path to a certificate bundle to use when verifying TLS connections")

}

func (o *DownloadOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
Expand Down Expand Up @@ -113,7 +116,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, o.InsecureSkipTLSVerify)
err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), o.Name, v1.DownloadTargetKindBackupContents, backupDest, o.Timeout, o.InsecureSkipTLSVerify, o.caCertPath)
if err != nil {
os.Remove(o.Output)
cmd.CheckError(err)
Expand Down
5 changes: 3 additions & 2 deletions pkg/cmd/cli/backup/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
func NewLogsCommand(f client.Factory) *cobra.Command {
timeout := time.Minute
insecureSkipTLSVerify := false
caCertPath := ""

c := &cobra.Command{
Use: "logs BACKUP",
Expand All @@ -59,13 +60,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, insecureSkipTLSVerify)
err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), backupName, v1.DownloadTargetKindBackupLog, os.Stdout, timeout, insecureSkipTLSVerify, caCertPath)
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, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")

c.Flags().StringVar(&caCertPath, "cacert", caCertPath, "path to a certificate bundle to use when verifying TLS connections")
return c
}
4 changes: 3 additions & 1 deletion pkg/cmd/cli/restore/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command {
listOptions metav1.ListOptions
details bool
insecureSkipTLSVerify bool
caCertPath string
)

c := &cobra.Command{
Expand Down Expand Up @@ -65,7 +66,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, insecureSkipTLSVerify)
s := output.DescribeRestore(&restore, podvolumeRestoreList.Items, details, veleroClient, insecureSkipTLSVerify, caCertPath)
if first {
first = false
fmt.Print(s)
Expand All @@ -80,6 +81,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, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
c.Flags().StringVar(&caCertPath, "cacert", caCertPath, "path to a certificate bundle to use when verifying TLS connections")

return c
}
4 changes: 3 additions & 1 deletion pkg/cmd/cli/restore/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
func NewLogsCommand(f client.Factory) *cobra.Command {
timeout := time.Minute
insecureSkipTLSVerify := false
caCertPath := ""

c := &cobra.Command{
Use: "logs RESTORE",
Expand All @@ -59,13 +60,14 @@ 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, insecureSkipTLSVerify)
err = downloadrequest.Stream(veleroClient.VeleroV1(), f.Namespace(), restoreName, v1.DownloadTargetKindRestoreLog, os.Stdout, timeout, insecureSkipTLSVerify, caCertPath)
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, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
c.Flags().StringVar(&caCertPath, "cacert", caCertPath, "path to a certificate bundle to use when verifying TLS connections")

return c
}
26 changes: 21 additions & 5 deletions pkg/cmd/util/downloadrequest/downloadrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,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, insecureSkipTLSVerify bool) error {
func Stream(client velerov1client.DownloadRequestsGetter, namespace, name string, kind v1.DownloadTargetKind, w io.Writer, timeout time.Duration, insecureSkipTLSVerify bool, caCertPath string) error {
req := &v1.DownloadRequest{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Expand Down Expand Up @@ -99,11 +99,27 @@ Loop:
return ErrNotFound
}

httpClient := new(http.Client)
if insecureSkipTLSVerify {
httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
var caPool *x509.CertPool
if len(caCertPath) > 0 {
caCert, err := ioutil.ReadFile(caCertPath)
if err != nil {
return errors.Wrapf(err, "couldn't open cacert")
}
// bundle the passed in cert with the system cert pool
// if it's available, otherwise create a new pool just
// for this.
caPool, err = x509.SystemCertPool()
if err != nil {
caPool = x509.NewCertPool()
}
caPool.AppendCertsFromPEM(caCert)
}
httpClient := new(http.Client)
httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecureSkipTLSVerify,
RootCAs: caPool,
},
}

httpReq, err := http.NewRequest("GET", req.Status.DownloadURL, nil)
Expand Down
13 changes: 7 additions & 6 deletions pkg/cmd/util/output/backup_describer.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func DescribeBackup(
details bool,
veleroClient clientset.Interface,
insecureSkipTLSVerify bool,
caCertPath string,
) string {
return Describe(func(d *Describer) {
d.DescribeMetadata(backup.ObjectMeta)
Expand Down Expand Up @@ -75,7 +76,7 @@ func DescribeBackup(
DescribeBackupSpec(d, backup.Spec)

d.Println()
DescribeBackupStatus(d, backup, details, veleroClient, insecureSkipTLSVerify)
DescribeBackupStatus(d, backup, details, veleroClient, insecureSkipTLSVerify, caCertPath)

if len(deleteRequests) > 0 {
d.Println()
Expand Down Expand Up @@ -212,7 +213,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, insecureSkipTLSVerify bool) {
func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertPath string) {
status := backup.Status

d.Printf("Backup Format Version:\t%d\n", status.Version)
Expand All @@ -238,7 +239,7 @@ func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool
d.Println()

if details {
describeBackupResourceList(d, backup, veleroClient, insecureSkipTLSVerify)
describeBackupResourceList(d, backup, veleroClient, insecureSkipTLSVerify, caCertPath)
d.Println()
}

Expand All @@ -249,7 +250,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, insecureSkipTLSVerify); err != nil {
if err := downloadrequest.Stream(veleroClient.VeleroV1(), backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeSnapshots, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
d.Printf("Persistent Volumes:\t<error getting volume snapshot info: %v>\n", err)
return
}
Expand All @@ -270,9 +271,9 @@ func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool
d.Printf("Persistent Volumes: <none included>\n")
}

func describeBackupResourceList(d *Describer, backup *velerov1api.Backup, veleroClient clientset.Interface, insecureSkipTLSVerify bool) {
func describeBackupResourceList(d *Describer, backup *velerov1api.Backup, veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertPath string) {
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(veleroClient.VeleroV1(), backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify); err != nil {
if err := downloadrequest.Stream(veleroClient.VeleroV1(), backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
if err == downloadrequest.ErrNotFound {
// the backup resource list could be missing if (other reasons may exist as well):
// - the backup was taken prior to v1.1; or
Expand Down
8 changes: 4 additions & 4 deletions pkg/cmd/util/output/restore_describer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
pkgrestore "github.com/vmware-tanzu/velero/pkg/restore"
)

func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestore, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool) string {
func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestore, details bool, veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertPath string) string {
return Describe(func(d *Describer) {
d.DescribeMetadata(restore.ObjectMeta)

Expand All @@ -56,7 +56,7 @@ func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestor
}
}

describeRestoreResults(d, restore, veleroClient, insecureSkipTLSVerify)
describeRestoreResults(d, restore, veleroClient, insecureSkipTLSVerify, caCertPath)

d.Println()
d.Printf("Backup:\t%s\n", restore.Spec.BackupName)
Expand Down Expand Up @@ -114,15 +114,15 @@ func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestor
})
}

func describeRestoreResults(d *Describer, restore *v1.Restore, veleroClient clientset.Interface, insecureSkipTLSVerify bool) {
func describeRestoreResults(d *Describer, restore *v1.Restore, veleroClient clientset.Interface, insecureSkipTLSVerify bool, caCertPath string) {
if restore.Status.Warnings == 0 && restore.Status.Errors == 0 {
return
}

var buf bytes.Buffer
var resultMap map[string]pkgrestore.Result

if err := downloadrequest.Stream(veleroClient.VeleroV1(), restore.Namespace, restore.Name, v1.DownloadTargetKindRestoreResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify); err != nil {
if err := downloadrequest.Stream(veleroClient.VeleroV1(), restore.Namespace, restore.Name, v1.DownloadTargetKindRestoreResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
d.Printf("Warnings:\t<error getting warnings: %v>\n\nErrors:\t<error getting errors: %v>\n", err, err)
return
}
Expand Down

0 comments on commit 32be406

Please sign in to comment.