Skip to content

Commit

Permalink
Add functionality to use custom certificate configuration while inter…
Browse files Browse the repository at this point in the history
…acting with central repository

- Added functionality to use the custom certificate configuration(configured by the user using tanzu config cert command) while interacting with central repository endpoint having self-signed certs/expired certs

Signed-off-by: Prem Kumar Kalle <[email protected]>
  • Loading branch information
prkalle committed Apr 27, 2023
1 parent 257b01c commit 7b04f5e
Show file tree
Hide file tree
Showing 10 changed files with 429 additions and 89 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ require (
github.com/vmware-tanzu/carvel-imgpkg v0.36.1
github.com/vmware-tanzu/carvel-ytt v0.40.0
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230415084831-9331f55d2999
github.com/vmware-tanzu/tanzu-plugin-runtime v0.90.0-alpha.0.0.20230425191535-014e58e69078
github.com/vmware-tanzu/tanzu-plugin-runtime v0.90.0-alpha.0.0.20230426214812-20c441a939f1
go.pinniped.dev v0.20.0
go.uber.org/multierr v1.8.0
golang.org/x/mod v0.9.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1289,8 +1289,8 @@ github.com/vmware-tanzu/tanzu-framework/apis/run v0.0.0-20221207131309-7323ca04b
github.com/vmware-tanzu/tanzu-framework/apis/run v0.0.0-20221207131309-7323ca04b86c/go.mod h1:ukZpKQ0hf5bjWdJLjn2M6qXP+9giZWQPxt8nOfrCR+o=
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230415084831-9331f55d2999 h1:WITDH+wpdl/clw1hwy+2jtq4Pt//i/Mq9lQXGwg3q4c=
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230415084831-9331f55d2999/go.mod h1:umFZBUfJ8VI3p0VO/xocuE+4fO9s9QbytEiOqFcH/Tw=
github.com/vmware-tanzu/tanzu-plugin-runtime v0.90.0-alpha.0.0.20230425191535-014e58e69078 h1:FnqG7kCmltbUgnGJiDcokvQT1Bbvs38IrmVIUFj4P34=
github.com/vmware-tanzu/tanzu-plugin-runtime v0.90.0-alpha.0.0.20230425191535-014e58e69078/go.mod h1:FlvOcF26rX4EA+ADjYTJdFh6WVur6O4jh25FDP9Lp7E=
github.com/vmware-tanzu/tanzu-plugin-runtime v0.90.0-alpha.0.0.20230426214812-20c441a939f1 h1:k3PPUHUwLcc7dG5swAhA5a6UsgqJPAEkNDS/kvDyUzc=
github.com/vmware-tanzu/tanzu-plugin-runtime v0.90.0-alpha.0.0.20230426214812-20c441a939f1/go.mod h1:FlvOcF26rX4EA+ADjYTJdFh6WVur6O4jh25FDP9Lp7E=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/go-gitlab v0.73.1 h1:UMagqUZLJdjss1SovIC+kJCH4k2AZWXl58gJd38Y/hI=
github.com/xanzy/go-gitlab v0.73.1/go.mod h1:d/a0vswScO7Agg1CZNz15Ic6SSvBG9vfw8egL99t4kA=
Expand Down
46 changes: 10 additions & 36 deletions pkg/carvelhelpers/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@
package carvelhelpers

import (
"os"
"runtime"
"strings"

"github.com/pkg/errors"

ctlimg "github.com/vmware-tanzu/carvel-imgpkg/pkg/imgpkg/registry"

"github.com/vmware-tanzu/tanzu-cli/pkg/clientconfighelpers"
"github.com/vmware-tanzu/tanzu-cli/pkg/configpaths"
"github.com/vmware-tanzu/tanzu-cli/pkg/constants"
"github.com/vmware-tanzu/tanzu-cli/pkg/registry"
)

Expand All @@ -36,40 +30,20 @@ func GetImageDigest(imageWithTag string) (string, string, error) {
return NewImageOperationsImpl().GetImageDigest(imageWithTag)
}

// newRegistry returns a new registry object by also
// taking into account for any custom registry or proxy
// environment variable provided by the user
func newRegistry() (registry.Registry, error) {
verifyCerts := true
skipVerifyCerts := os.Getenv(constants.ConfigVariableCustomImageRepositorySkipTLSVerify)
if strings.EqualFold(skipVerifyCerts, "true") {
verifyCerts = false
}

// newRegistry returns a new registry object by also taking
// into account for any custom registry provided by the user
func newRegistry(registryName string) (registry.Registry, error) {
registryOpts := &ctlimg.Opts{
VerifyCerts: verifyCerts,
VerifyCerts: true,
Anon: true,
}

if runtime.GOOS == "windows" {
err := clientconfighelpers.AddRegistryTrustedRootCertsFileForWindows(registryOpts)
if err != nil {
return nil, err
}
regCertOptions, err := clientconfighelpers.GetRegistryCertOptions(registryName)
if err != nil {
return nil, errors.Wrapf(err, "unable to get the registry certificate configuration")
}

caCertBytes, err := clientconfighelpers.GetCustomRepositoryCaCertificateForClient()
if err == nil && len(caCertBytes) != 0 {
filePath, err := configpaths.GetRegistryCertFile()
if err != nil {
return nil, err
}
err = os.WriteFile(filePath, caCertBytes, 0o644)
if err != nil {
return nil, errors.Wrapf(err, "failed to write the custom image registry CA cert to file '%s'", filePath)
}
registryOpts.CACertPaths = append(registryOpts.CACertPaths, filePath)
}

registryOpts.CACertPaths = regCertOptions.CACertPaths
registryOpts.VerifyCerts = !(regCertOptions.SkipCertVerify)
registryOpts.Insecure = regCertOptions.Insecure
return registry.New(registryOpts)
}
38 changes: 32 additions & 6 deletions pkg/carvelhelpers/image_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package carvelhelpers

import (
"github.com/pkg/errors"

"github.com/vmware-tanzu/tanzu-cli/pkg/clientconfighelpers"
)

// ImageOperationOptions implements the ImageOperationsImpl interface by using `imgpkg` library
Expand All @@ -18,7 +20,11 @@ func NewImageOperationsImpl() ImageOperationsImpl {
// CopyImageToTar downloads the image as tar file
// This is equivalent to `imgpkg copy --image <image> --to-tar <tar-file-path>` command
func (i *ImageOperationOptions) CopyImageToTar(sourceImageName, destTarFile string) error {
reg, err := newRegistry()
registryName, err := clientconfighelpers.GetRegistryName(sourceImageName)
if err != nil {
return err
}
reg, err := newRegistry(registryName)
if err != nil {
return errors.Wrapf(err, "unable to initialize registry")
}
Expand All @@ -28,7 +34,11 @@ func (i *ImageOperationOptions) CopyImageToTar(sourceImageName, destTarFile stri
// CopyImageFromTar publishes the image to destination repository from specified tar file
// This is equivalent to `imgpkg copy --tar <file> --to-repo <dest-repo>` command
func (i *ImageOperationOptions) CopyImageFromTar(sourceTarFile, destImageRepo string) error {
reg, err := newRegistry()
registryName, err := clientconfighelpers.GetRegistryName(destImageRepo)
if err != nil {
return err
}
reg, err := newRegistry(registryName)
if err != nil {
return errors.Wrapf(err, "unable to initialize registry")
}
Expand All @@ -38,7 +48,11 @@ func (i *ImageOperationOptions) CopyImageFromTar(sourceTarFile, destImageRepo st
// DownloadImageAndSaveFilesToDir reads a plain OCI image and saves its
// files to the specified location.
func (i *ImageOperationOptions) DownloadImageAndSaveFilesToDir(imageWithTag, destinationDir string) error {
reg, err := newRegistry()
registryName, err := clientconfighelpers.GetRegistryName(imageWithTag)
if err != nil {
return err
}
reg, err := newRegistry(registryName)
if err != nil {
return errors.Wrapf(err, "unable to initialize registry")
}
Expand All @@ -53,7 +67,11 @@ func (i *ImageOperationOptions) DownloadImageAndSaveFilesToDir(imageWithTag, des
// It takes os environment variables for custom repository and proxy
// configuration into account while downloading image from repository
func (i *ImageOperationOptions) GetFilesMapFromImage(imageWithTag string) (map[string][]byte, error) {
reg, err := newRegistry()
registryName, err := clientconfighelpers.GetRegistryName(imageWithTag)
if err != nil {
return nil, err
}
reg, err := newRegistry(registryName)
if err != nil {
return nil, errors.Wrapf(err, "unable to initialize registry")
}
Expand All @@ -62,7 +80,11 @@ func (i *ImageOperationOptions) GetFilesMapFromImage(imageWithTag string) (map[s

// GetImageDigest gets digest of the image
func (i *ImageOperationOptions) GetImageDigest(imageWithTag string) (string, string, error) {
reg, err := newRegistry()
registryName, err := clientconfighelpers.GetRegistryName(imageWithTag)
if err != nil {
return "", "", err
}
reg, err := newRegistry(registryName)
if err != nil {
return "", "", errors.Wrapf(err, "unable to initialize registry")
}
Expand All @@ -77,7 +99,11 @@ func (i *ImageOperationOptions) GetImageDigest(imageWithTag string) (string, str

// PushImage publishes the image to the specified location
func (i *ImageOperationOptions) PushImage(imageWithTag string, filePaths []string) error {
reg, err := newRegistry()
registryName, err := clientconfighelpers.GetRegistryName(imageWithTag)
if err != nil {
return err
}
reg, err := newRegistry(registryName)
if err != nil {
return errors.Wrapf(err, "unable to initialize registry")
}
Expand Down
107 changes: 77 additions & 30 deletions pkg/clientconfighelpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,87 @@ package clientconfighelpers
import (
"encoding/base64"
"os"
"runtime"
"strconv"

ctlimg "github.com/vmware-tanzu/carvel-imgpkg/pkg/imgpkg/registry"

regname "github.com/google/go-containerregistry/pkg/name"
"github.com/pkg/errors"

"github.com/vmware-tanzu/tanzu-cli/pkg/configpaths"
"github.com/vmware-tanzu/tanzu-cli/pkg/constants"
configlib "github.com/vmware-tanzu/tanzu-plugin-runtime/config"
configtypes "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types"
)

// GetCustomRepositoryCaCertificateForClient returns CA certificate to use with cli client
// This function reads the CA certificate from following variables in decreasing order of precedence:
// 1. PROXY_CA_CERT
// 2. TKG_PROXY_CA_CERT
// 3. TKG_CUSTOM_IMAGE_REPOSITORY_CA_CERTIFICATE
func GetCustomRepositoryCaCertificateForClient() ([]byte, error) {
caCert := ""
var errProxyCACert, errTkgProxyCACertValue, errCustomImageRepoCACert error
var proxyCACertValue, tkgProxyCACertValue, customImageRepoCACert string
type RegistryCertOptions struct {
CACertPaths []string
SkipCertVerify bool
Insecure bool
}

// Get the proxy configuration from os environment variable
proxyCACertValue = os.Getenv(constants.ProxyCACert)
tkgProxyCACertValue = os.Getenv(constants.TKGProxyCACert)
customImageRepoCACert = os.Getenv(constants.ConfigVariableCustomImageRepositoryCaCertificate)
func GetRegistryCertOptions(registryName string) (*RegistryCertOptions, error) {
registryCertOpts := &RegistryCertOptions{
SkipCertVerify: false,
Insecure: false,
}

if runtime.GOOS == "windows" {
err := AddRegistryTrustedRootCertsFileForWindows(registryCertOpts)
if err != nil {
return nil, err
}
}

if errProxyCACert == nil && proxyCACertValue != "" {
caCert = proxyCACertValue
} else if errTkgProxyCACertValue == nil && tkgProxyCACertValue != "" {
caCert = tkgProxyCACertValue
} else if errCustomImageRepoCACert == nil && customImageRepoCACert != "" {
caCert = customImageRepoCACert
} else {
// return empty content when none is specified
return []byte{}, nil
// check if the custom cert data is configured for the registry
if exists, _ := configlib.CertExists(registryName); !exists {
return registryCertOpts, nil
}
cert, err := configlib.GetCert(registryName)
if err != nil {
return nil, errors.Wrapf(err, "failed to get the custom certificate configuration for host %q", registryName)
}

decoded, err := base64.StdEncoding.DecodeString(caCert)
err = updateRegistryCertOptions(cert, registryCertOpts)
if err != nil {
return nil, errors.Wrap(err, "unable to decode the base64-encoded custom registry CA certificate string")
return nil, errors.Wrapf(err, "failed to updated the registry cert options")
}
return decoded, nil

return registryCertOpts, nil
}

// AddRegistryTrustedRootCertsFileForWindows adds CA certificate to registry options for windows environments
func AddRegistryTrustedRootCertsFileForWindows(registryOpts *ctlimg.Opts) error {
// updateRegistryCertOptions sets the registry options by taking the custom certificate data configured for registry as input
func updateRegistryCertOptions(cert *configtypes.Cert, registryCertOpts *RegistryCertOptions) error {
if cert.SkipCertVerify != "" {
skipVerifyCerts, _ := strconv.ParseBool(cert.SkipCertVerify)
registryCertOpts.SkipCertVerify = skipVerifyCerts
}
if cert.Insecure != "" {
insecure, _ := strconv.ParseBool(cert.Insecure)
registryCertOpts.Insecure = insecure
}

if cert.CACertData != "" {
caCertBytes, err := base64.StdEncoding.DecodeString(cert.CACertData)
if err != nil {
return errors.Wrap(err, "unable to decode the base64-encoded custom registry CA certificate string")
}
if len(caCertBytes) != 0 {
filePath, err := configpaths.GetRegistryCertFile()
if err != nil {
return err
}
err = os.WriteFile(filePath, caCertBytes, 0o644)
if err != nil {
return errors.Wrapf(err, "failed to write the custom image registry CA cert to file '%s'", filePath)
}
registryCertOpts.CACertPaths = append(registryCertOpts.CACertPaths, filePath)
}
}
return nil
}

// AddRegistryTrustedRootCertsFileForWindows adds CA certificate to registry options for Windows environments
func AddRegistryTrustedRootCertsFileForWindows(registryCertOpts *RegistryCertOptions) error {
filePath, err := configpaths.GetRegistryTrustedCACertFileForWindows()
if err != nil {
return err
Expand All @@ -60,6 +97,16 @@ func AddRegistryTrustedRootCertsFileForWindows(registryOpts *ctlimg.Opts) error
if err != nil {
return errors.Wrapf(err, "failed to write the registry trusted CA cert to file '%s'", filePath)
}
registryOpts.CACertPaths = append(registryOpts.CACertPaths, filePath)
registryCertOpts.CACertPaths = append(registryCertOpts.CACertPaths, filePath)
return nil
}

// GetRegistryName extracts the registry name from the image name with/without image tag
// (e.g. localhost:9876/tanzu-cli/plugins/central:small => localhost:9876)
func GetRegistryName(imageName string) (string, error) {
tag, err := regname.NewTag(imageName)
if err != nil {
return "", errors.Wrapf(err, "unable to fetch registry name from image %q", imageName)
}
return tag.Registry.Name(), nil
}
Loading

0 comments on commit 7b04f5e

Please sign in to comment.