Add --cacert flag to velero cli commands (#2364)
* Add --cacert flag to velero cli commands 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>pull/2398/head
parent
016868ecd3
commit
c8223608ba
|
@ -0,0 +1 @@
|
|||
Added a `--cacert` flag to the velero client describe, download, and logs commands to allow passing a path to a certificate to use when verifying TLS connections to object storage. Also added a corresponding client config option called `cacert` which takes a path to a certificate bundle to use as a default when `--cacert` is not specified.
|
|
@ -28,6 +28,7 @@ import (
|
|||
const (
|
||||
ConfigKeyNamespace = "namespace"
|
||||
ConfigKeyFeatures = "features"
|
||||
ConfigKeyCACert = "cacert"
|
||||
)
|
||||
|
||||
// VeleroConfig is a map of strings to interface{} for deserializing Velero client config options.
|
||||
|
@ -110,6 +111,19 @@ func (c VeleroConfig) Features() []string {
|
|||
return strings.Split(features, ",")
|
||||
}
|
||||
|
||||
func (c VeleroConfig) CACertFile() string {
|
||||
val, ok := c[ConfigKeyCACert]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
caCertFile, ok := val.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return caCertFile
|
||||
}
|
||||
|
||||
func configFileName() string {
|
||||
return filepath.Join(os.Getenv("HOME"), ".config", "velero", "config.json")
|
||||
}
|
||||
|
|
|
@ -38,6 +38,12 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command {
|
|||
insecureSkipTLSVerify bool
|
||||
)
|
||||
|
||||
config, err := client.LoadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: Error reading config file: %v\n", err)
|
||||
}
|
||||
caCertFile := config.CACertFile()
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: use + " [NAME1] [NAME2] [NAME...]",
|
||||
Short: "Describe backups",
|
||||
|
@ -72,7 +78,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, caCertFile)
|
||||
if first {
|
||||
first = false
|
||||
fmt.Print(s)
|
||||
|
@ -87,6 +93,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(&caCertFile, "cacert", caCertFile, "path to a certificate bundle to use when verifying TLS connections")
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -34,7 +34,13 @@ import (
|
|||
)
|
||||
|
||||
func NewDownloadCommand(f client.Factory) *cobra.Command {
|
||||
config, err := client.LoadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: Error reading config file: %v\n", err)
|
||||
}
|
||||
o := NewDownloadOptions()
|
||||
o.caCertFile = config.CACertFile()
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: "download NAME",
|
||||
Short: "Download a backup",
|
||||
|
@ -58,6 +64,7 @@ type DownloadOptions struct {
|
|||
Timeout time.Duration
|
||||
InsecureSkipTLSVerify bool
|
||||
writeOptions int
|
||||
caCertFile string
|
||||
}
|
||||
|
||||
func NewDownloadOptions() *DownloadOptions {
|
||||
|
@ -71,6 +78,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.caCertFile, "cacert", o.caCertFile, "path to a certificate bundle to use when verifying TLS connections")
|
||||
|
||||
}
|
||||
|
||||
func (o *DownloadOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
|
||||
|
@ -113,7 +122,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.caCertFile)
|
||||
if err != nil {
|
||||
os.Remove(o.Output)
|
||||
cmd.CheckError(err)
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package backup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
@ -31,8 +32,14 @@ import (
|
|||
)
|
||||
|
||||
func NewLogsCommand(f client.Factory) *cobra.Command {
|
||||
config, err := client.LoadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: Error reading config file: %v\n", err)
|
||||
}
|
||||
|
||||
timeout := time.Minute
|
||||
insecureSkipTLSVerify := false
|
||||
caCertFile := config.CACertFile()
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: "logs BACKUP",
|
||||
|
@ -59,13 +66,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, caCertFile)
|
||||
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(&caCertFile, "cacert", caCertFile, "path to a certificate bundle to use when verifying TLS connections")
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -37,6 +37,12 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command {
|
|||
insecureSkipTLSVerify bool
|
||||
)
|
||||
|
||||
config, err := client.LoadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: Error reading config file: %v\n", err)
|
||||
}
|
||||
caCertFile := config.CACertFile()
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: use + " [NAME1] [NAME2] [NAME...]",
|
||||
Short: "Describe restores",
|
||||
|
@ -65,7 +71,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, caCertFile)
|
||||
if first {
|
||||
first = false
|
||||
fmt.Print(s)
|
||||
|
@ -80,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, "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(&caCertFile, "cacert", caCertFile, "path to a certificate bundle to use when verifying TLS connections")
|
||||
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package restore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
@ -31,8 +32,14 @@ import (
|
|||
)
|
||||
|
||||
func NewLogsCommand(f client.Factory) *cobra.Command {
|
||||
config, err := client.LoadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: Error reading config file: %v\n", err)
|
||||
}
|
||||
|
||||
timeout := time.Minute
|
||||
insecureSkipTLSVerify := false
|
||||
caCertFile := config.CACertFile()
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: "logs RESTORE",
|
||||
|
@ -59,13 +66,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, caCertFile)
|
||||
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(&caCertFile, "cacert", caCertFile, "path to a certificate bundle to use when verifying TLS connections")
|
||||
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -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, caCertFile string) error {
|
||||
req := &v1.DownloadRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
|
@ -99,11 +99,38 @@ Loop:
|
|||
return ErrNotFound
|
||||
}
|
||||
|
||||
httpClient := new(http.Client)
|
||||
if insecureSkipTLSVerify {
|
||||
httpClient.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
var caPool *x509.CertPool
|
||||
if len(caCertFile) > 0 {
|
||||
caCert, err := ioutil.ReadFile(caCertFile)
|
||||
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)
|
||||
}
|
||||
|
||||
defaultTransport := http.DefaultTransport.(*http.Transport)
|
||||
// same settings as the default transport
|
||||
// aside from timeout and TLSClientConfig
|
||||
httpClient := new(http.Client)
|
||||
httpClient.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: insecureSkipTLSVerify,
|
||||
RootCAs: caPool,
|
||||
},
|
||||
IdleConnTimeout: timeout,
|
||||
DialContext: defaultTransport.DialContext,
|
||||
ForceAttemptHTTP2: defaultTransport.ForceAttemptHTTP2,
|
||||
MaxIdleConns: defaultTransport.MaxIdleConns,
|
||||
Proxy: defaultTransport.Proxy,
|
||||
TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout,
|
||||
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequest("GET", req.Status.DownloadURL, nil)
|
||||
|
|
|
@ -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, false)
|
||||
err := Stream(client.VeleroV1(), "namespace", "name", test.kind, output, timeout, false, "")
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ func DescribeBackup(
|
|||
details bool,
|
||||
veleroClient clientset.Interface,
|
||||
insecureSkipTLSVerify bool,
|
||||
caCertFile string,
|
||||
) string {
|
||||
return Describe(func(d *Describer) {
|
||||
d.DescribeMetadata(backup.ObjectMeta)
|
||||
|
@ -75,7 +76,7 @@ func DescribeBackup(
|
|||
DescribeBackupSpec(d, backup.Spec)
|
||||
|
||||
d.Println()
|
||||
DescribeBackupStatus(d, backup, details, veleroClient, insecureSkipTLSVerify)
|
||||
DescribeBackupStatus(d, backup, details, veleroClient, insecureSkipTLSVerify, caCertFile)
|
||||
|
||||
if len(deleteRequests) > 0 {
|
||||
d.Println()
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
|
|
@ -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, caCertFile string) 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, insecureSkipTLSVerify)
|
||||
describeRestoreResults(d, restore, veleroClient, insecureSkipTLSVerify, caCertFile)
|
||||
|
||||
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, 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
|
||||
}
|
||||
|
@ -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, 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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue