Compare commits
10 Commits
33c73aaa7d
...
6d81103b26
Author | SHA1 | Date |
---|---|---|
|
6d81103b26 | |
|
a7b6cc6b6b | |
|
fd12beb1cd | |
|
d3c99d5dfc | |
|
0f153fe35c | |
|
a50792fb2b | |
|
c1f13b0d55 | |
|
d8f439268e | |
|
e00a85d0ec | |
|
62c2ee06a3 |
|
@ -42,7 +42,7 @@ jobs:
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: Buildx
|
id: Buildx
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
|
@ -92,7 +92,7 @@ jobs:
|
||||||
|
|
||||||
- name: Install Cosign
|
- name: Install Cosign
|
||||||
if: ${{ inputs.sign }}
|
if: ${{ inputs.sign }}
|
||||||
uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
|
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
|
||||||
|
|
||||||
- name: Sign the images with GitHub OIDC Token
|
- name: Sign the images with GitHub OIDC Token
|
||||||
if: ${{ inputs.sign }}
|
if: ${{ inputs.sign }}
|
||||||
|
|
|
@ -64,7 +64,7 @@ jobs:
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
steps:
|
steps:
|
||||||
- name: Install the verifier
|
- name: Install the verifier
|
||||||
uses: slsa-framework/slsa-verifier/actions/installer@v2.7.0
|
uses: slsa-framework/slsa-verifier/actions/installer@v2.7.1
|
||||||
|
|
||||||
- name: Download assets
|
- name: Download assets
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
"oras.land/oras-go/v2/registry/remote/auth"
|
||||||
"oras.land/oras-go/v2/registry/remote/credentials"
|
"oras.land/oras-go/v2/registry/remote/credentials"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/internal/config"
|
"github.com/falcosecurity/falcoctl/internal/config"
|
||||||
|
@ -41,6 +42,7 @@ type loginOptions struct {
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
passwordFromStdin bool
|
passwordFromStdin bool
|
||||||
|
insecure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBasicCmd returns the basic command.
|
// NewBasicCmd returns the basic command.
|
||||||
|
@ -66,16 +68,21 @@ Example - Login with username and password from stdin:
|
||||||
|
|
||||||
Example - Login with username and password in an interactive prompt:
|
Example - Login with username and password in an interactive prompt:
|
||||||
falcoctl registry auth basic localhost:5000
|
falcoctl registry auth basic localhost:5000
|
||||||
|
|
||||||
|
Example - Login to an insecure registry:
|
||||||
|
falcoctl registry auth basic --insecure localhost:5000
|
||||||
`,
|
`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
_ = viper.BindPFlag("registry.auth.basic.username", cmd.Flags().Lookup("username"))
|
_ = viper.BindPFlag("registry.auth.basic.username", cmd.Flags().Lookup("username"))
|
||||||
_ = viper.BindPFlag("registry.auth.basic.password", cmd.Flags().Lookup("password"))
|
_ = viper.BindPFlag("registry.auth.basic.password", cmd.Flags().Lookup("password"))
|
||||||
_ = viper.BindPFlag("registry.auth.basic.password_stdin", cmd.Flags().Lookup("password-stdin"))
|
_ = viper.BindPFlag("registry.auth.basic.password_stdin", cmd.Flags().Lookup("password-stdin"))
|
||||||
|
_ = viper.BindPFlag("registry.auth.basic.insecure", cmd.Flags().Lookup("insecure"))
|
||||||
|
|
||||||
o.username = viper.GetString("registry.auth.basic.username")
|
o.username = viper.GetString("registry.auth.basic.username")
|
||||||
o.password = viper.GetString("registry.auth.basic.password")
|
o.password = viper.GetString("registry.auth.basic.password")
|
||||||
o.passwordFromStdin = viper.GetBool("registry.auth.basic.password_stdin")
|
o.passwordFromStdin = viper.GetBool("registry.auth.basic.password_stdin")
|
||||||
|
o.insecure = viper.GetBool("registry.auth.basic.insecure")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -87,6 +94,7 @@ Example - Login with username and password in an interactive prompt:
|
||||||
cmd.Flags().StringVarP(&o.username, "username", "u", "", "registry username")
|
cmd.Flags().StringVarP(&o.username, "username", "u", "", "registry username")
|
||||||
cmd.Flags().StringVarP(&o.password, "password", "p", "", "registry password")
|
cmd.Flags().StringVarP(&o.password, "password", "p", "", "registry password")
|
||||||
cmd.Flags().BoolVar(&o.passwordFromStdin, "password-stdin", false, "read password from stdin")
|
cmd.Flags().BoolVar(&o.passwordFromStdin, "password-stdin", false, "read password from stdin")
|
||||||
|
cmd.Flags().BoolVar(&o.insecure, "insecure", false, "enables plain HTTP and skips TLS verification")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -96,18 +104,26 @@ func (o *loginOptions) RunBasic(ctx context.Context, args []string) error {
|
||||||
var reg string
|
var reg string
|
||||||
logger := o.Printer.Logger
|
logger := o.Printer.Logger
|
||||||
|
|
||||||
|
// Remove scheme if present
|
||||||
|
registryArg := strings.TrimPrefix(strings.TrimPrefix(args[0], "http://"), "https://")
|
||||||
|
|
||||||
// Allow to have the registry expressed as a ref, but actually extract it.
|
// Allow to have the registry expressed as a ref, but actually extract it.
|
||||||
reg, err := utils.GetRegistryFromRef(args[0])
|
reg, err := utils.GetRegistryFromRef(registryArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reg = args[0]
|
reg = registryArg
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := getCredentials(o.Printer, o); err != nil {
|
if err := getCredentials(o.Printer, o); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// create empty client
|
// create empty client with insecure option if specified
|
||||||
client := authn.NewClient()
|
var client *auth.Client
|
||||||
|
if o.insecure {
|
||||||
|
client = authn.NewClient(authn.WithInsecure())
|
||||||
|
} else {
|
||||||
|
client = authn.NewClient()
|
||||||
|
}
|
||||||
|
|
||||||
// create credential store
|
// create credential store
|
||||||
credentialStore, err := credentials.NewStore(config.RegistryCredentialConfPath(), credentials.StoreOptions{
|
credentialStore, err := credentials.NewStore(config.RegistryCredentialConfPath(), credentials.StoreOptions{
|
||||||
|
|
|
@ -73,11 +73,15 @@ Example - Login with username and password from stdin:
|
||||||
Example - Login with username and password in an interactive prompt:
|
Example - Login with username and password in an interactive prompt:
|
||||||
falcoctl registry auth basic localhost:5000
|
falcoctl registry auth basic localhost:5000
|
||||||
|
|
||||||
|
Example - Login to an insecure registry:
|
||||||
|
falcoctl registry auth basic --insecure localhost:5000
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
falcoctl registry auth basic [hostname]
|
falcoctl registry auth basic [hostname]
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for basic
|
-h, --help help for basic
|
||||||
|
--insecure allow connections to SSL registry without certs
|
||||||
-p, --password string registry password
|
-p, --password string registry password
|
||||||
--password-stdin read password from stdin
|
--password-stdin read password from stdin
|
||||||
-u, --username string registry username
|
-u, --username string registry username
|
||||||
|
@ -127,8 +131,54 @@ var registryAuthBasicTests = Describe("auth", func() {
|
||||||
Expect(output).Should(gbytes.Say(regexp.QuoteMeta(registryAuthBasicHelp)))
|
Expect(output).Should(gbytes.Say(regexp.QuoteMeta(registryAuthBasicHelp)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Context("failure", func() {
|
|
||||||
|
|
||||||
|
Context("insecure flag", func() {
|
||||||
|
When("using HTTP with --insecure", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
args = []string{registryCmd, authCmd, basicCmd, "--insecure", "-u", "username", "-p", "password", "--config", configFile, registry}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should succeed with plain HTTP", func() {
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
Expect(output).Should(gbytes.Say("Login succeeded"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("using HTTPS with self-signed cert and --insecure", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
// The registry is already configured for HTTPS in the test suite
|
||||||
|
args = []string{registryCmd, authCmd, basicCmd, "--insecure", "-u", "username", "-p", "password", "--config", configFile, registryBasic}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should succeed with insecure HTTPS", func() {
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
Expect(output).Should(gbytes.Say("Login succeeded"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("using HTTPS without --insecure", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
args = []string{registryCmd, authCmd, basicCmd, "-u", "username", "-p", "password", "--config", configFile, registryBasic}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should fail with certificate verification error", func() {
|
||||||
|
Expect(err).Should(HaveOccurred())
|
||||||
|
Expect(output).Should(gbytes.Say("certificate"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("using HTTP without --insecure", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
args = []string{registryCmd, authCmd, basicCmd, "-u", "username", "-p", "password", "--config", configFile, "http://" + registry}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should fail when trying plain HTTP without insecure flag", func() {
|
||||||
|
Expect(err).Should(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("failure", func() {
|
||||||
When("without hostname", func() {
|
When("without hostname", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
args = []string{registryCmd, authCmd, basicCmd}
|
args = []string{registryCmd, authCmd, basicCmd}
|
||||||
|
@ -137,5 +187,4 @@ var registryAuthBasicTests = Describe("auth", func() {
|
||||||
"ERROR accepts 1 arg(s), received 0")
|
"ERROR accepts 1 arg(s), received 0")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -184,7 +184,7 @@ require (
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -442,8 +442,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
|
|
@ -18,6 +18,8 @@ package basic
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"oras.land/oras-go/v2/registry/remote/auth"
|
"oras.land/oras-go/v2/registry/remote/auth"
|
||||||
"oras.land/oras-go/v2/registry/remote/credentials"
|
"oras.land/oras-go/v2/registry/remote/credentials"
|
||||||
|
@ -34,9 +36,42 @@ func Login(ctx context.Context, client *auth.Client, credStore credentials.Store
|
||||||
|
|
||||||
client.Credential = auth.StaticCredential(reg, cred)
|
client.Credential = auth.StaticCredential(reg, cred)
|
||||||
|
|
||||||
r, err := registry.NewRegistry(reg, registry.WithClient(client))
|
// Check if client is configured for insecure connections
|
||||||
|
transport, ok := client.Client.Transport.(*http.Transport)
|
||||||
|
insecure := ok && transport.TLSClientConfig != nil && transport.TLSClientConfig.InsecureSkipVerify
|
||||||
|
|
||||||
|
// If the registry URL starts with https://, force HTTPS
|
||||||
|
forceHTTPS := strings.HasPrefix(reg, "https://")
|
||||||
|
// If the registry URL starts with http://, force HTTP
|
||||||
|
forceHTTP := strings.HasPrefix(reg, "http://")
|
||||||
|
// Strip scheme if present
|
||||||
|
reg = strings.TrimPrefix(strings.TrimPrefix(reg, "http://"), "https://")
|
||||||
|
|
||||||
|
// Create registry client with appropriate settings
|
||||||
|
var r *registry.Registry
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case forceHTTPS:
|
||||||
|
// For explicit HTTPS URLs, use HTTPS with insecure setting from client
|
||||||
|
r, err = registry.NewRegistry(reg, registry.WithClient(client), registry.WithPlainHTTP(false))
|
||||||
|
case forceHTTP:
|
||||||
|
// For explicit HTTP URLs, use HTTP if insecure is enabled
|
||||||
|
if !insecure {
|
||||||
|
return fmt.Errorf("cannot use plain HTTP for %q without --insecure flag", reg)
|
||||||
|
}
|
||||||
|
r, err = registry.NewRegistry(reg, registry.WithClient(client), registry.WithPlainHTTP(true))
|
||||||
|
default:
|
||||||
|
// For URLs without scheme, try HTTPS first, then fall back to HTTP if insecure is enabled
|
||||||
|
r, err = registry.NewRegistry(reg, registry.WithClient(client), registry.WithPlainHTTP(false))
|
||||||
|
if err != nil && insecure {
|
||||||
|
// If HTTPS failed and insecure is enabled, try HTTP
|
||||||
|
r, err = registry.NewRegistry(reg, registry.WithClient(client), registry.WithPlainHTTP(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("unable to connect to registry %q: %w", reg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.CheckConnection(ctx); err != nil {
|
if err := r.CheckConnection(ctx); err != nil {
|
||||||
|
|
|
@ -22,6 +22,9 @@ import (
|
||||||
|
|
||||||
// GetRegistryFromRef extracts the registry from a ref string.
|
// GetRegistryFromRef extracts the registry from a ref string.
|
||||||
func GetRegistryFromRef(ref string) (string, error) {
|
func GetRegistryFromRef(ref string) (string, error) {
|
||||||
|
// Remove scheme if present
|
||||||
|
ref = strings.TrimPrefix(strings.TrimPrefix(ref, "http://"), "https://")
|
||||||
|
|
||||||
index := strings.Index(ref, "/")
|
index := strings.Index(ref, "/")
|
||||||
if index <= 0 {
|
if index <= 0 {
|
||||||
return "", fmt.Errorf("cannot extract registry name from ref %q", ref)
|
return "", fmt.Errorf("cannot extract registry name from ref %q", ref)
|
||||||
|
|
|
@ -17,6 +17,7 @@ package authn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -36,6 +37,7 @@ type Options struct {
|
||||||
CredentialsFuncs []func(context.Context, string) (auth.Credential, error)
|
CredentialsFuncs []func(context.Context, string) (auth.Credential, error)
|
||||||
AutoLoginHandler *AutoLoginHandler
|
AutoLoginHandler *AutoLoginHandler
|
||||||
ClientTokenCache auth.Cache
|
ClientTokenCache auth.Cache
|
||||||
|
Insecure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new authenticated client to interact with a remote registry.
|
// NewClient creates a new authenticated client to interact with a remote registry.
|
||||||
|
@ -48,21 +50,29 @@ func NewClient(options ...func(*Options)) *auth.Client {
|
||||||
o(opt)
|
o(opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
}).DialContext,
|
||||||
|
ForceAttemptHTTP2: true,
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.Insecure {
|
||||||
|
//nolint:gosec // InsecureSkipVerify is intentionally set to true when --insecure flag is used
|
||||||
|
transport.TLSClientConfig = &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
authClient := auth.Client{
|
authClient := auth.Client{
|
||||||
Client: &http.Client{
|
Client: &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: transport,
|
||||||
Proxy: http.ProxyFromEnvironment,
|
|
||||||
DialContext: (&net.Dialer{
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
}).DialContext,
|
|
||||||
ForceAttemptHTTP2: true,
|
|
||||||
MaxIdleConns: 100,
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
|
||||||
// TODO(loresuso, alacuku): tls config.
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Cache: opt.ClientTokenCache,
|
Cache: opt.ClientTokenCache,
|
||||||
Credential: func(ctx context.Context, reg string) (auth.Credential, error) {
|
Credential: func(ctx context.Context, reg string) (auth.Credential, error) {
|
||||||
|
@ -151,3 +161,10 @@ func WithClientTokenCache(cache auth.Cache) func(c *Options) {
|
||||||
c.ClientTokenCache = cache
|
c.ClientTokenCache = cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithInsecure configures the client to skip TLS verification.
|
||||||
|
func WithInsecure() func(c *Options) {
|
||||||
|
return func(c *Options) {
|
||||||
|
c.Insecure = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue