diff --git a/cmd/client.go b/cmd/client.go index 97b20476..88dc8535 100644 --- a/cmd/client.go +++ b/cmd/client.go @@ -28,6 +28,9 @@ type ClientConfig struct { // Verbose logging. By default logging output is kept to the bare minimum. // Use this flag to configure verbose logging throughout. Verbose bool + + // Allow insecure server connections when using SSL + InsecureSkipVerify bool } // ClientFactory defines a constructor which assists in the creation of a Client @@ -62,9 +65,9 @@ func NewClientFactory(n func() *fn.Client) ClientFactory { // defer done() func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) { var ( - p = progress.New(cfg.Verbose) // updates the CLI - t = newTransport() // may provide a custom impl which proxies - c = newCredentialsProvider(t) // for accessing registries + p = progress.New(cfg.Verbose) // updates the CLI + t = newTransport(cfg.InsecureSkipVerify) // may provide a custom impl which proxies + c = newCredentialsProvider(t) // for accessing registries d = newKnativeDeployer(cfg.Namespace, cfg.Verbose) pp = newTektonPipelinesProvider(cfg.Namespace, p, c, cfg.Verbose) o = []fn.Option{ // standard (shared) options for all commands @@ -103,14 +106,14 @@ func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) { // newTransport returns a transport with cluster-flavor-specific variations // which take advantage of additional features offered by cluster variants. -func newTransport() fnhttp.RoundTripCloser { +func newTransport(insecureSkipVerify bool) fnhttp.RoundTripCloser { if openshift.IsOpenShift() { - return fnhttp.NewRoundTripper(openshift.WithOpenShiftServiceCA()) + return fnhttp.NewRoundTripper(fnhttp.WithInsecureSkipVerify(insecureSkipVerify), openshift.WithOpenShiftServiceCA()) } // Other cluster variants ... - return fnhttp.NewRoundTripper() // Default (vanilla k8s) + return fnhttp.NewRoundTripper(fnhttp.WithInsecureSkipVerify(insecureSkipVerify)) // Default (vanilla k8s) } // newCredentialsProvider returns a credentials provider which possibly diff --git a/cmd/invoke.go b/cmd/invoke.go index 3091eda5..d80057bb 100644 --- a/cmd/invoke.go +++ b/cmd/invoke.go @@ -26,7 +26,7 @@ NAME SYNOPSIS {{.Name}} invoke [-t|--target] [-f|--format] [--id] [--source] [--type] [--data] [--file] [--content-type] - [-s|--save] [-p|--path] [-c|--confirm] [-v|--verbose] + [-s|--save] [-p|--path] [-i|--insecure] [-c|--confirm] [-v|--verbose] DESCRIPTION Invokes the function by sending a test request to the currently running @@ -97,9 +97,12 @@ EXAMPLES o Invoke an arbitrary endpoint (CloudEvent) $ {{.Name}} invoke -f=cloudevent -t="https://my-event-broker.example.com" + o Allow insecure server connections when using SSL + $ {{.Name}} invoke --insecure + `, SuggestFor: []string{"emit", "emti", "send", "emit", "exec", "nivoke", "onvoke", "unvoke", "knvoke", "imvoke", "ihvoke", "ibvoke"}, - PreRunE: bindEnv("path", "format", "target", "id", "source", "type", "data", "content-type", "file", "confirm"), + PreRunE: bindEnv("path", "format", "target", "id", "source", "type", "data", "content-type", "file", "insecure", "confirm"), } // Flags @@ -112,6 +115,7 @@ EXAMPLES cmd.Flags().StringP("content-type", "", fn.DefaultInvokeContentType, "Content Type of the data. (Env: $FUNC_CONTENT_TYPE)") cmd.Flags().StringP("data", "", fn.DefaultInvokeData, "Data to send in the request. (Env: $FUNC_DATA)") cmd.Flags().StringP("file", "", "", "Path to a file to use as data. Overrides --data flag and should be sent with a correct --content-type. (Env: $FUNC_FILE)") + cmd.Flags().BoolP("insecure", "i", false, "Allow insecure server connections when using SSL. (Env: $FUNC_INSECURE)") cmd.Flags().BoolP("confirm", "c", false, "Prompt to confirm all options interactively. (Env: $FUNC_CONFIRM)") cmd.SetHelpFunc(defaultTemplatedHelp) @@ -132,7 +136,7 @@ func runInvoke(cmd *cobra.Command, args []string, newClient ClientFactory) (err } // Client instance from env vars, flags, args and user prompts (if --confirm) - client, done := newClient(ClientConfig{Namespace: cfg.Namespace, Verbose: cfg.Verbose}) + client, done := newClient(ClientConfig{Namespace: cfg.Namespace, Verbose: cfg.Verbose, InsecureSkipVerify: cfg.Insecure}) defer done() // Message to send the running function built from parameters gathered @@ -201,6 +205,7 @@ type invokeConfig struct { Namespace string Confirm bool Verbose bool + Insecure bool } func newInvokeConfig(newClient ClientFactory) (cfg invokeConfig, err error) { @@ -217,6 +222,7 @@ func newInvokeConfig(newClient ClientFactory) (cfg invokeConfig, err error) { Confirm: viper.GetBool("confirm"), Verbose: viper.GetBool("verbose"), Namespace: viper.GetString("namespace"), + Insecure: viper.GetBool("insecure"), } // If file was passed, read it in as data @@ -234,7 +240,7 @@ func newInvokeConfig(newClient ClientFactory) (cfg invokeConfig, err error) { } // Client instance for use during prompting. - client, done := newClient(ClientConfig{Namespace: cfg.Namespace, Verbose: cfg.Verbose}) + client, done := newClient(ClientConfig{Namespace: cfg.Namespace, Verbose: cfg.Verbose, InsecureSkipVerify: cfg.Insecure}) defer done() // If in interactive terminal mode, prompt to modify defaults. @@ -252,6 +258,7 @@ func newInvokeConfig(newClient ClientFactory) (cfg invokeConfig, err error) { fmt.Printf("Data: %v\n", cfg.Data) fmt.Printf("Content Type: %v\n", cfg.ContentType) fmt.Printf("File: %v\n", cfg.File) + fmt.Printf("Insecure: %v\n", cfg.Insecure) return } @@ -375,5 +382,17 @@ func (c invokeConfig) prompt(client *fn.Client) (invokeConfig, error) { return c, err } + qs = []*survey.Question{ + { + Name: "Insecure", + Prompt: &survey.Confirm{ + Message: "Allow insecure server connections when using SSL", + Default: c.Insecure, + }, + }} + if err := survey.Ask(qs, &c); err != nil { + return c, err + } + return c, nil } diff --git a/docs/reference/func_invoke.md b/docs/reference/func_invoke.md index d3190a7f..7d547ab4 100644 --- a/docs/reference/func_invoke.md +++ b/docs/reference/func_invoke.md @@ -11,7 +11,7 @@ NAME SYNOPSIS func invoke [-t|--target] [-f|--format] [--id] [--source] [--type] [--data] [--file] [--content-type] - [-s|--save] [-p|--path] [-c|--confirm] [-v|--verbose] + [-s|--save] [-p|--path] [-i|--insecure] [-c|--confirm] [-v|--verbose] DESCRIPTION Invokes the function by sending a test request to the currently running @@ -82,6 +82,9 @@ EXAMPLES o Invoke an arbitrary endpoint (CloudEvent) $ func invoke -f=cloudevent -t="https://my-event-broker.example.com" + o Allow insecure server connections when using SSL + $ func invoke --insecure + ``` @@ -98,6 +101,7 @@ func invoke -f, --format string Format of message to send, 'http' or 'cloudevent'. Default is to choose automatically. (Env: $FUNC_FORMAT) -h, --help help for invoke --id string ID for the request data. (Env: $FUNC_ID) + -i, --insecure Allow insecure server connections when using SSL. (Env: $FUNC_INSECURE) -p, --path string Path to the project directory (Env: $FUNC_PATH) (default ".") --source string Source value for the request data. (Env: $FUNC_SOURCE) (default "/boson/fn") -t, --target string Function instance to invoke. Can be 'local', 'remote' or a URL. Defaults to auto-discovery if not provided. (Env: $FUNC_TARGET) diff --git a/http/transport.go b/http/transport.go index a46d64b9..5f64fbd5 100644 --- a/http/transport.go +++ b/http/transport.go @@ -25,8 +25,9 @@ type RoundTripCloser interface { } type options struct { - selectCA func(ctx context.Context, serverName string) (*x509.Certificate, error) - inClusterDialer ContextDialer + selectCA func(ctx context.Context, serverName string) (*x509.Certificate, error) + inClusterDialer ContextDialer + insecureSkipVerify bool } type Option func(*options) @@ -43,13 +44,20 @@ func WithInClusterDialer(inClusterDialer ContextDialer) Option { } } +func WithInsecureSkipVerify(insecureSkipVerify bool) Option { + return func(o *options) { + o.insecureSkipVerify = insecureSkipVerify + } +} + // NewRoundTripper returns new closable RoundTripper that first tries to dial connection in standard way, // if the dial operation fails due to hostname resolution the RoundTripper tries to dial from in cluster pod. // // This is useful for accessing cluster internal services (pushing a CloudEvent into Knative broker). func NewRoundTripper(opts ...Option) RoundTripCloser { o := options{ - inClusterDialer: k8s.NewLazyInitInClusterDialer(), + inClusterDialer: k8s.NewLazyInitInClusterDialer(), + insecureSkipVerify: false, } for _, option := range opts { option(&o) @@ -62,6 +70,8 @@ func NewRoundTripper(opts ...Option) RoundTripCloser { combinedDialer := newDialerWithFallback(primaryDialer, secondaryDialer) + httpTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: o.insecureSkipVerify} + httpTransport.DialContext = combinedDialer.DialContext httpTransport.DialTLSContext = newDialTLSContext(combinedDialer, httpTransport.TLSClientConfig, o.selectCA)