Skip to content

Commit cce1704

Browse files
CR-8730 ingress host validation (#206)
* ingress host validation * bump * add insecure-ingress-host flag * insecure-ingress-host flag in store and added on config flags * docs * typo fix * handle error types * wip * check certificate for http ingress * wip * bump go-sdk * ensureIngressHost * improve error * fixed a typo * print error * added logs * add log * true, nil * do not validate http * improve errors Co-authored-by: Noam Gal <noam.gal@codefresh.io>
1 parent 9a0b1af commit cce1704

38 files changed

+176
-18
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
VERSION=v0.0.196
1+
VERSION=v0.0.197
22

33
OUT_DIR=dist
44
YEAR?=$(shell date +"%Y")

cmd/commands/common.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,16 @@ package commands
3030

3131
import (
3232
"context"
33+
"crypto/tls"
34+
"crypto/x509"
3335
_ "embed"
3436
"fmt"
37+
"net/http"
38+
"net/url"
3539
"os"
3640
"regexp"
3741
"strings"
42+
"time"
3843

3944
"github.com/argoproj-labs/argocd-autopilot/pkg/git"
4045
"github.com/codefresh-io/cli-v2/pkg/config"
@@ -90,6 +95,10 @@ func IsValidName(s string) (bool, error) {
9095
return regexp.MatchString(`^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$`, s)
9196
}
9297

98+
func IsValidIngressHost(ingress string) (bool, error) {
99+
return regexp.MatchString(`^(http|https)://`, ingress)
100+
}
101+
93102
func askUserIfToInstallDemoResources(cmd *cobra.Command, sampleInstall *bool) error {
94103
if !store.Get().Silent && !cmd.Flags().Changed("sample-install") {
95104
templates := &promptui.SelectTemplates{
@@ -360,3 +369,94 @@ func getIngressHostFromUserInput(cmd *cobra.Command, ingressHost *string) error
360369

361370
return nil
362371
}
372+
373+
func checkIngressHostCertificate(ctx context.Context, ingress string) (bool, error) {
374+
var err error
375+
match, _ := regexp.MatchString(`^http://`, ingress)
376+
if match { //if user provided http ingress
377+
log.G(ctx).Warn("The ingress host uses an insecure protocol. The browser may block subsequent runtime requests from the UI unless explicitly approved.")
378+
379+
return true, nil
380+
}
381+
382+
res, err := http.Get(ingress)
383+
384+
if err == nil {
385+
res.Body.Close()
386+
return true, nil
387+
}
388+
389+
urlErr, ok := err.(*url.Error)
390+
if !ok {
391+
return false, err
392+
}
393+
_, ok1 := urlErr.Err.(x509.CertificateInvalidError)
394+
_, ok2 := urlErr.Err.(x509.SystemRootsError)
395+
_, ok3 := urlErr.Err.(x509.UnknownAuthorityError)
396+
_, ok4 := urlErr.Err.(x509.ConstraintViolationError)
397+
_, ok5 := urlErr.Err.(x509.HostnameError)
398+
399+
certErr := ok1 || ok2 || ok3 || ok4 || ok5
400+
if !certErr {
401+
return false, fmt.Errorf("failed with non-certificate error: %w", err)
402+
}
403+
404+
insecureOk := checkIngressHostWithInsecure(ingress)
405+
if !insecureOk {
406+
return false, fmt.Errorf("insecure call failed: %w", err)
407+
}
408+
409+
return false, nil
410+
}
411+
412+
func checkIngressHostWithInsecure(ingress string) bool {
413+
httpClient := &http.Client{}
414+
httpClient.Timeout = 10 * time.Second
415+
customTransport := http.DefaultTransport.(*http.Transport).Clone()
416+
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
417+
httpClient.Transport = customTransport
418+
req, err := http.NewRequest("GET", ingress, nil)
419+
if err != nil {
420+
return false
421+
}
422+
res, err := httpClient.Do(req)
423+
if err != nil {
424+
return false
425+
}
426+
res.Body.Close()
427+
return true
428+
}
429+
430+
func askUserIfToProceedWithInsecure(ctx context.Context) error {
431+
if store.Get().InsecureIngressHost {
432+
return nil
433+
}
434+
if store.Get().Silent {
435+
return fmt.Errorf("cancelled installation due to invalid ingress host certificate")
436+
}
437+
438+
templates := &promptui.SelectTemplates{
439+
Selected: "{{ . | yellow }} ",
440+
}
441+
442+
log.G(ctx).Warnf("The provided ingressHost does not have a valid certificate.")
443+
labelStr := fmt.Sprintf("%vDo you wish to continue the installation with insecure ingress host mode?%v", CYAN, COLOR_RESET)
444+
445+
prompt := promptui.Select{
446+
Label: labelStr,
447+
Items: []string{"Yes", "Cancel installation"},
448+
Templates: templates,
449+
}
450+
451+
_, result, err := prompt.Run()
452+
if err != nil {
453+
return fmt.Errorf("Prompt error: %w", err)
454+
}
455+
456+
if result == "Yes" {
457+
store.Get().InsecureIngressHost = true
458+
} else {
459+
return fmt.Errorf("cancelled installation due to invalid ingress host certificate")
460+
}
461+
return nil
462+
}

cmd/commands/integrations.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"strings"
3737

3838
"github.com/codefresh-io/cli-v2/pkg/log"
39+
"github.com/codefresh-io/cli-v2/pkg/store"
3940
"github.com/codefresh-io/cli-v2/pkg/util"
4041
sdk "github.com/codefresh-io/go-sdk/pkg/codefresh"
4142
model "github.com/codefresh-io/go-sdk/pkg/codefresh/model/app-proxy"
@@ -401,7 +402,7 @@ func getAppProxyClient(runtime *string, client *sdk.AppProxyAPI) func(*cobra.Com
401402
*runtime = cur.DefaultRuntime
402403
}
403404

404-
appProxy, err := cfConfig.NewClient().AppProxy(cmd.Context(), *runtime)
405+
appProxy, err := cfConfig.NewClient().AppProxy(cmd.Context(), *runtime, store.Get().InsecureIngressHost)
405406
if err != nil {
406407
return err
407408
}

cmd/commands/runtime.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ func runtimeInstallCommandPreRunHandler(cmd *cobra.Command, opts *RuntimeInstall
268268
return err
269269
}
270270

271-
if err := getIngressHostFromUserInput(cmd, &opts.IngressHost); err != nil {
271+
if err := ensureIngressHost(cmd, opts); err != nil {
272272
return err
273273
}
274274

@@ -298,6 +298,32 @@ func runtimeInstallCommandPreRunHandler(cmd *cobra.Command, opts *RuntimeInstall
298298
return nil
299299
}
300300

301+
func ensureIngressHost(cmd *cobra.Command, opts *RuntimeInstallOptions) error {
302+
if err := getIngressHostFromUserInput(cmd, &opts.IngressHost); err != nil {
303+
return err
304+
}
305+
306+
isValid, err := IsValidIngressHost(opts.IngressHost)
307+
if err != nil {
308+
log.G(cmd.Context()).Fatal("failed to check the validity of the ingress host")
309+
} else if !isValid {
310+
log.G(cmd.Context()).Fatal("ingress host must begin with a protocol http:// or https://")
311+
}
312+
313+
certValid, err := checkIngressHostCertificate(cmd.Context(), opts.IngressHost)
314+
if err != nil {
315+
log.G(cmd.Context()).Fatalf("failed to check ingress host: %v", err)
316+
}
317+
318+
if !certValid {
319+
if err = askUserIfToProceedWithInsecure(cmd.Context()); err != nil {
320+
return err
321+
}
322+
}
323+
324+
return nil
325+
}
326+
301327
func getComponents(rt *runtime.Runtime, opts *RuntimeInstallOptions) []string {
302328
var componentNames []string
303329
for _, component := range rt.Spec.Components {
@@ -343,7 +369,7 @@ func RunRuntimeInstall(ctx context.Context, opts *RuntimeInstallOptions) error {
343369
}
344370

345371
rt, err := runtime.Download(opts.Version, opts.RuntimeName)
346-
appendLogToSummary("Dowmloading runtime definition", err)
372+
appendLogToSummary("Downloading runtime definition", err)
347373
if err != nil {
348374
return fmt.Errorf("failed to download runtime definition: %w", err)
349375
}
@@ -490,7 +516,7 @@ func RunRuntimeInstall(ctx context.Context, opts *RuntimeInstallOptions) error {
490516
}
491517

492518
func addDefaultGitIntegration(ctx context.Context, runtime string, opts *apmodel.AddGitIntegrationArgs) error {
493-
appProxyClient, err := cfConfig.NewClient().AppProxy(ctx, runtime)
519+
appProxyClient, err := cfConfig.NewClient().AppProxy(ctx, runtime, store.Get().InsecureIngressHost)
494520
if err != nil {
495521
return fmt.Errorf("failed to build app-proxy client: %w", err)
496522
}
@@ -1267,7 +1293,7 @@ func createReporter(ctx context.Context, cloneOpts *git.CloneOptions, opts *Runt
12671293
return err
12681294
}
12691295

1270-
log.G(ctx).Info("Pushing Codefresh ", strings.Title(reporterCreateOpts.reporterName), " mainifests")
1296+
log.G(ctx).Info("Pushing Codefresh ", strings.Title(reporterCreateOpts.reporterName), " manifests")
12711297

12721298
pushMessage := "Created Codefresh" + strings.Title(reporterCreateOpts.reporterName) + "Reporter"
12731299

docs/commands/cli-v2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ cli-v2 [flags]
2626
--cfconfig string Custom path for authentication contexts config file (default "/home/user")
2727
-h, --help help for cli-v2
2828
--insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io)
29+
--insecure-ingress-host Disable certificate validation of ingress host (default: false)
2930
--request-timeout duration Request timeout (default 30s)
3031
```
3132

docs/commands/cli-v2_component.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ cli-v2 component [flags]
1818
--auth-context string Run the next command using a specific authentication context
1919
--cfconfig string Custom path for authentication contexts config file (default "/home/user")
2020
--insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io)
21+
--insecure-ingress-host Disable certificate validation of ingress host (default: false)
2122
--request-timeout duration Request timeout (default 30s)
2223
```
2324

docs/commands/cli-v2_component_list.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ cli-v2 component list [runtime_name] [flags]
2626
--auth-context string Run the next command using a specific authentication context
2727
--cfconfig string Custom path for authentication contexts config file (default "/home/user")
2828
--insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io)
29+
--insecure-ingress-host Disable certificate validation of ingress host (default: false)
2930
--request-timeout duration Request timeout (default 30s)
3031
```
3132

docs/commands/cli-v2_config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ cli-v2 config [flags]
3131
--auth-context string Run the next command using a specific authentication context
3232
--cfconfig string Custom path for authentication contexts config file (default "/home/user")
3333
--insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io)
34+
--insecure-ingress-host Disable certificate validation of ingress host (default: false)
3435
--request-timeout duration Request timeout (default 30s)
3536
```
3637

docs/commands/cli-v2_config_create-context.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ cli-v2 config create-context [flags]
2929
--auth-context string Run the next command using a specific authentication context
3030
--cfconfig string Custom path for authentication contexts config file (default "/home/user")
3131
--insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io)
32+
--insecure-ingress-host Disable certificate validation of ingress host (default: false)
3233
--request-timeout duration Request timeout (default 30s)
3334
```
3435

docs/commands/cli-v2_config_current-context.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ cli-v2 config current-context [flags]
2727
--auth-context string Run the next command using a specific authentication context
2828
--cfconfig string Custom path for authentication contexts config file (default "/home/user")
2929
--insecure Disable certificate validation for TLS connections (e.g. to g.codefresh.io)
30+
--insecure-ingress-host Disable certificate validation of ingress host (default: false)
3031
--request-timeout duration Request timeout (default 30s)
3132
```
3233

0 commit comments

Comments
 (0)