Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

automatically detect node ip during install for noproxy #1186

Merged
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
bf5bd9e
add a note to readme specifying a folder to place the license
laverya Sep 17, 2024
4e17c8a
get default interface
laverya Sep 17, 2024
95b484e
wip default noproxies and prompts
laverya Sep 17, 2024
9479324
disable most tests
laverya Sep 17, 2024
e7f5491
prompt for new noproxy
laverya Sep 17, 2024
978ca16
store provided noproxy values and total noproxy values independently
laverya Sep 17, 2024
7030d94
dedup code
laverya Sep 17, 2024
b77595d
simplify noproxy subnet
laverya Sep 17, 2024
18c599b
regen CRDs
laverya Sep 17, 2024
1f3a106
update unit tests
laverya Sep 17, 2024
1273355
fix no-prompt case
laverya Sep 17, 2024
9cadfcd
?
laverya Sep 17, 2024
1d1e1f8
FFFF
laverya Sep 17, 2024
0948a2e
???
laverya Sep 18, 2024
3ed457c
test bigger noproxy
laverya Sep 18, 2024
65cdeae
reuse proxy object, don't rederive from flags
laverya Sep 18, 2024
9ae2cc2
reorder imports
laverya Sep 18, 2024
4b973ba
Revert "disable most tests"
laverya Sep 18, 2024
bfda1e5
misspelling
laverya Sep 18, 2024
fc36e37
rename to combineNoProxySuppliedValuesAndDefaults, remove debug logs
laverya Sep 18, 2024
3fbaf4e
add link to k0s logic
laverya Sep 18, 2024
c22cbbf
recursively prompt for noproxy
laverya Sep 18, 2024
9d6e4cf
remove prompts for proxy, just add the default subnet if the current …
laverya Sep 18, 2024
296e592
maybePromptForNoProxy -> includeLocalIPInNoProxy
laverya Sep 18, 2024
b345951
check the noproxy config on node join
laverya Sep 18, 2024
207f65e
test that should fail to ensure that join command preflights are work…
laverya Sep 18, 2024
561883a
fix 'adding "blah" to the noproxy you provided' message
laverya Sep 18, 2024
ebb410b
test failed successfully, restoring it
laverya Sep 18, 2024
060b3f2
restore all tests, again
laverya Sep 18, 2024
eac4573
Accept Alex's suggestions for noproxy messaging
laverya Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Additionally, it includes a Registry when deployed in air gap mode.
```

1. In the Vendor Portal, create and download a license that is assigned to the channel.
We recommend storing this license in the `local-dev/` directory, as it is gitignored and not otherwise used by the CI.
Copy link
Member

@sgalsaleh sgalsaleh Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

take it or leave it:

Suggested change
We recommend storing this license in the `local-dev/` directory, as it is gitignored and not otherwise used by the CI.
We recommend storing this license in the `local-dev/` directory that is dedicated to storing local development artifacts, as it is gitignored and not otherwise used by the CI.


1. Install Embedded Cluster:
```bash
Expand Down
17 changes: 11 additions & 6 deletions cmd/embedded-cluster/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ func waitForK0s() error {
}

// installAndWaitForK0s installs the k0s binary and waits for it to be ready
func installAndWaitForK0s(c *cli.Context, applier *addons.Applier) (*k0sconfig.ClusterConfig, error) {
func installAndWaitForK0s(c *cli.Context, applier *addons.Applier, proxy *ecv1beta1.ProxySpec) (*k0sconfig.ClusterConfig, error) {
loading := spinner.Start()
defer loading.Close()
loading.Infof("Installing %s node", defaults.BinaryName())
Expand All @@ -448,7 +448,6 @@ func installAndWaitForK0s(c *cli.Context, applier *addons.Applier) (*k0sconfig.C
metrics.ReportApplyFinished(c, err)
return nil, err
}
proxy := getProxySpecFromFlags(c)
logrus.Debugf("creating systemd unit files")
if err := createSystemdUnitFiles(false, proxy); err != nil {
err := fmt.Errorf("unable to create systemd unit files: %w", err)
Expand Down Expand Up @@ -613,12 +612,19 @@ var installCommand = &cli.Command{
metrics.ReportApplyFinished(c, err)
return err
}
proxy, err = maybePromptForNoProxy(c, proxy)
if err != nil {
metrics.ReportApplyFinished(c, err)
return err
}
setProxyEnv(proxy)

logrus.Debugf("materializing binaries")
if err := materializeFiles(c); err != nil {
metrics.ReportApplyFinished(c, err)
return err
}
applier, err := getAddonsApplier(c, adminConsolePwd)
applier, err := getAddonsApplier(c, adminConsolePwd, proxy)
if err != nil {
metrics.ReportApplyFinished(c, err)
return err
Expand All @@ -633,7 +639,7 @@ var installCommand = &cli.Command{
metrics.ReportApplyFinished(c, err)
return err
}
cfg, err := installAndWaitForK0s(c, applier)
cfg, err := installAndWaitForK0s(c, applier, proxy)
if err != nil {
return err
}
Expand All @@ -647,7 +653,7 @@ var installCommand = &cli.Command{
},
}

func getAddonsApplier(c *cli.Context, adminConsolePwd string) (*addons.Applier, error) {
func getAddonsApplier(c *cli.Context, adminConsolePwd string, proxy *ecv1beta1.ProxySpec) (*addons.Applier, error) {
opts := []addons.Option{}
if c.Bool("no-prompt") {
opts = append(opts, addons.WithoutPrompt())
Expand All @@ -658,7 +664,6 @@ func getAddonsApplier(c *cli.Context, adminConsolePwd string) (*addons.Applier,
if ab := c.String("airgap-bundle"); ab != "" {
opts = append(opts, addons.WithAirgapBundle(ab))
}
proxy := getProxySpecFromFlags(c)
if proxy != nil {
opts = append(opts, addons.WithProxy(proxy.HTTPProxy, proxy.HTTPSProxy, proxy.NoProxy))
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/embedded-cluster/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ var joinCommand = &cli.Command{
return err
}

applier, err := getAddonsApplier(c, "")
applier, err := getAddonsApplier(c, "", jcmd.Proxy)
if err != nil {
metrics.ReportJoinFailed(c.Context, jcmd.MetricsBaseURL, jcmd.ClusterID, err)
return err
Expand Down
4 changes: 2 additions & 2 deletions cmd/embedded-cluster/preflights.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ var installRunPreflightsCommand = &cli.Command{
return err
}

applier, err := getAddonsApplier(c, "")
applier, err := getAddonsApplier(c, "", proxy)
if err != nil {
return err
}
Expand Down Expand Up @@ -121,7 +121,7 @@ var joinRunPreflightsCommand = &cli.Command{
return err
}

applier, err := getAddonsApplier(c, "")
applier, err := getAddonsApplier(c, "", jcmd.Proxy)
if err != nil {
return err
}
Expand Down
117 changes: 110 additions & 7 deletions cmd/embedded-cluster/proxy.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package main

import (
"fmt"
"net"
"os"
"strings"

ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
"github.com/replicatedhq/embedded-cluster/pkg/defaults"
"github.com/replicatedhq/embedded-cluster/pkg/netutils"
"github.com/replicatedhq/embedded-cluster/pkg/prompts"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)

Expand Down Expand Up @@ -36,12 +41,12 @@ func withProxyFlags(flags []cli.Flag) []cli.Flag {

func getProxySpecFromFlags(c *cli.Context) *ecv1beta1.ProxySpec {
proxy := &ecv1beta1.ProxySpec{}
var noProxy []string
var providedNoProxy []string
if c.Bool("proxy") {
proxy.HTTPProxy = os.Getenv("HTTP_PROXY")
proxy.HTTPSProxy = os.Getenv("HTTPS_PROXY")
if os.Getenv("NO_PROXY") != "" {
noProxy = append(noProxy, os.Getenv("NO_PROXY"))
providedNoProxy = append(providedNoProxy, os.Getenv("NO_PROXY"))
}
}
if c.IsSet("http-proxy") {
Expand All @@ -51,17 +56,26 @@ func getProxySpecFromFlags(c *cli.Context) *ecv1beta1.ProxySpec {
proxy.HTTPSProxy = c.String("https-proxy")
}
if c.String("no-proxy") != "" {
noProxy = append(noProxy, c.String("no-proxy"))
providedNoProxy = append(providedNoProxy, c.String("no-proxy"))
}
proxy.ProvidedNoProxy = strings.Join(providedNoProxy, ",")
combineNoProxySuppliedValuesAndDefaults(c, proxy)
if proxy.HTTPProxy == "" && proxy.HTTPSProxy == "" && proxy.NoProxy == "" {
return nil
}
return proxy
}

func combineNoProxySuppliedValuesAndDefaults(c *cli.Context, proxy *ecv1beta1.ProxySpec) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name is too long 😆, but i guess it's fine. i would've gone with something like injectNoProxyDefaults or something.

Suggested change
func combineNoProxySuppliedValuesAndDefaults(c *cli.Context, proxy *ecv1beta1.ProxySpec) {
func combineNoProxySuppliedValuesAndDefaults(c *cli.Context, proxy *ecv1beta1.ProxySpec) {

if proxy.ProvidedNoProxy == "" {
return
}
noProxy := strings.Split(proxy.ProvidedNoProxy, ",")
if len(noProxy) > 0 || proxy.HTTPProxy != "" || proxy.HTTPSProxy != "" {
noProxy = append(defaults.DefaultNoProxy, noProxy...)
noProxy = append(noProxy, c.String("pod-cidr"), c.String("service-cidr"))
proxy.NoProxy = strings.Join(noProxy, ",")
}
if proxy.HTTPProxy == "" && proxy.HTTPSProxy == "" && proxy.NoProxy == "" {
return nil
}
return proxy
}

// setProxyEnv sets the HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables based on the provided ProxySpec.
Expand All @@ -80,3 +94,92 @@ func setProxyEnv(proxy *ecv1beta1.ProxySpec) {
os.Setenv("NO_PROXY", proxy.NoProxy)
}
}

func maybePromptForNoProxy(c *cli.Context, proxy *ecv1beta1.ProxySpec) (*ecv1beta1.ProxySpec, error) {
if proxy != nil && (proxy.HTTPProxy != "" || proxy.HTTPSProxy != "") {
// if there is a proxy set, then there needs to be a no proxy set
// if it is not set, prompt with a default (the local IP or subnet)
// if it is set, we need to check that it covers the local IP
defaultIPNet, err := netutils.GetDefaultIPNet()
if err != nil {
return nil, fmt.Errorf("failed to get default IPNet: %w", err)
}
cleanDefaultIPNet, err := cleanCIDR(defaultIPNet)
if err != nil {
return nil, fmt.Errorf("failed to clean subnet: %w", err)
}
if proxy.ProvidedNoProxy == "" {
if c.Bool("no-prompt") {
logrus.Infof("no-proxy was not set, using default no proxy %s", cleanDefaultIPNet)
proxy.ProvidedNoProxy = cleanDefaultIPNet
combineNoProxySuppliedValuesAndDefaults(c, proxy)
return proxy, nil
} else {
return promptForNoProxy(c, proxy, cleanDefaultIPNet, defaultIPNet.IP.String(), 0)
}
} else {
shouldPrompt := false
isValid, err := validateNoProxy(proxy.NoProxy, defaultIPNet.IP.String())
if err != nil {
logrus.Infof("The provided no-proxy %q failed to parse with error %q, falling back to prompt", proxy.NoProxy, err)
shouldPrompt = true
} else if !isValid {
logrus.Infof("The provided no-proxy %q does not cover the local IP %q, falling back to prompt", proxy.NoProxy, defaultIPNet.IP.String())
shouldPrompt = true
}
if shouldPrompt {
return promptForNoProxy(c, proxy, cleanDefaultIPNet, defaultIPNet.IP.String(), 0)
}
}
}
return proxy, nil
}

// cleanCIDR returns a `.0/x` subnet instead of a `.2/x` etc subnet
func cleanCIDR(defaultIPNet *net.IPNet) (string, error) {
_, newNet, err := net.ParseCIDR(defaultIPNet.String())
if err != nil {
return "", fmt.Errorf("failed to parse local inet CIDR %q: %w", defaultIPNet.String(), err)
}
return newNet.String(), nil
}

func promptForNoProxy(c *cli.Context, proxy *ecv1beta1.ProxySpec, subnet, IP string, count int) (*ecv1beta1.ProxySpec, error) {
if count == 0 {
logrus.Infof("A no-proxy address is required when a proxy is set. We suggest either the node subnet %q or the addresses of every node that will be a member of the cluster. The current node's IP address is %q.", subnet, IP)
} else if count >= 3 {
return nil, fmt.Errorf("failed to prompt for no-proxy after 3 attempts")
}
newProxy := prompts.New().Input("No proxy:", "", true)
isValid, err := validateNoProxy(newProxy, IP)
if err != nil {
logrus.Errorf("failed to validate no-proxy: %v", err)
return promptForNoProxy(c, proxy, subnet, IP, count+1)
}
if !isValid {
logrus.Errorf("provided no-proxy %q does not cover the local IP %q", newProxy, IP)
return promptForNoProxy(c, proxy, subnet, IP, count+1)
}
proxy.ProvidedNoProxy = newProxy
combineNoProxySuppliedValuesAndDefaults(c, proxy)
return proxy, nil
}

func validateNoProxy(newNoProxy string, localIP string) (bool, error) {
foundLocal := false
for _, oneEntry := range strings.Split(newNoProxy, ",") {
if oneEntry == localIP {
foundLocal = true
} else if strings.Contains(oneEntry, "/") {
_, ipnet, err := net.ParseCIDR(oneEntry)
if err != nil {
return false, fmt.Errorf("failed to parse CIDR within no-proxy: %w", err)
}
if ipnet.Contains(net.ParseIP(localIP)) {
foundLocal = true
}
}
}

return foundLocal, nil
}
30 changes: 17 additions & 13 deletions cmd/embedded-cluster/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ func Test_getProxySpecFromFlags(t *testing.T) {
flagSet.Set("proxy", "true")
},
want: &ecv1beta1.ProxySpec{
HTTPProxy: "http://proxy",
HTTPSProxy: "https://proxy",
NoProxy: "localhost,127.0.0.1,.cluster.local,.svc,no-proxy-1,no-proxy-2,10.244.0.0/16,10.96.0.0/12",
HTTPProxy: "http://proxy",
HTTPSProxy: "https://proxy",
ProvidedNoProxy: "no-proxy-1,no-proxy-2",
NoProxy: "localhost,127.0.0.1,.cluster.local,.svc,no-proxy-1,no-proxy-2,10.244.0.0/16,10.96.0.0/12",
},
},
{
Expand All @@ -66,13 +67,14 @@ func Test_getProxySpecFromFlags(t *testing.T) {
flagSet.Set("no-proxy", "other-no-proxy-1,other-no-proxy-2")
},
want: &ecv1beta1.ProxySpec{
HTTPProxy: "http://other-proxy",
HTTPSProxy: "https://other-proxy",
NoProxy: "localhost,127.0.0.1,.cluster.local,.svc,other-no-proxy-1,other-no-proxy-2,10.244.0.0/16,10.96.0.0/12",
HTTPProxy: "http://other-proxy",
HTTPSProxy: "https://other-proxy",
ProvidedNoProxy: "other-no-proxy-1,other-no-proxy-2",
NoProxy: "localhost,127.0.0.1,.cluster.local,.svc,other-no-proxy-1,other-no-proxy-2,10.244.0.0/16,10.96.0.0/12",
},
},
{
name: "proxy flags should override proxy from env",
name: "proxy flags should override proxy from env, but merge no-proxy",
init: func(t *testing.T, flagSet *flag.FlagSet) {
t.Setenv("HTTP_PROXY", "http://proxy")
t.Setenv("HTTPS_PROXY", "https://proxy")
Expand All @@ -84,9 +86,10 @@ func Test_getProxySpecFromFlags(t *testing.T) {
flagSet.Set("no-proxy", "other-no-proxy-1,other-no-proxy-2")
},
want: &ecv1beta1.ProxySpec{
HTTPProxy: "http://other-proxy",
HTTPSProxy: "https://other-proxy",
NoProxy: "localhost,127.0.0.1,.cluster.local,.svc,no-proxy-1,no-proxy-2,other-no-proxy-1,other-no-proxy-2,10.244.0.0/16,10.96.0.0/12",
HTTPProxy: "http://other-proxy",
HTTPSProxy: "https://other-proxy",
ProvidedNoProxy: "no-proxy-1,no-proxy-2,other-no-proxy-1,other-no-proxy-2",
NoProxy: "localhost,127.0.0.1,.cluster.local,.svc,no-proxy-1,no-proxy-2,other-no-proxy-1,other-no-proxy-2,10.244.0.0/16,10.96.0.0/12",
},
},
{
Expand All @@ -100,9 +103,10 @@ func Test_getProxySpecFromFlags(t *testing.T) {
flagSet.Set("service-cidr", "2.2.2.2/24")
},
want: &ecv1beta1.ProxySpec{
HTTPProxy: "http://other-proxy",
HTTPSProxy: "https://other-proxy",
NoProxy: "localhost,127.0.0.1,.cluster.local,.svc,other-no-proxy-1,other-no-proxy-2,1.1.1.1/24,2.2.2.2/24",
HTTPProxy: "http://other-proxy",
HTTPSProxy: "https://other-proxy",
ProvidedNoProxy: "other-no-proxy-1,other-no-proxy-2",
NoProxy: "localhost,127.0.0.1,.cluster.local,.svc,other-no-proxy-1,other-no-proxy-2,1.1.1.1/24,2.2.2.2/24",
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/embedded-cluster/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ var restoreCommand = &cli.Command{
}
}

applier, err := getAddonsApplier(c, "")
applier, err := getAddonsApplier(c, "", proxy)
if err != nil {
return err
}
Expand Down
1 change: 0 additions & 1 deletion e2e/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ func TestProxiedEnvironment(t *testing.T) {
line := []string{"single-node-install.sh", "ui"}
line = append(line, "--http-proxy", cluster.HTTPProxy)
line = append(line, "--https-proxy", cluster.HTTPProxy)
line = append(line, "--no-proxy", strings.Join(tc.IPs, ","))
if _, _, err := RunCommandOnNode(t, tc, 0, line, withProxyEnv(tc.IPs)); err != nil {
t.Fatalf("fail to install embedded-cluster on node %s: %v", tc.Nodes[0], err)
}
Expand Down
7 changes: 4 additions & 3 deletions kinds/apis/v1beta1/installation_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ type ArtifactsLocation struct {

// ProxySpec holds the proxy configuration.
type ProxySpec struct {
HTTPProxy string `json:"httpProxy,omitempty"`
HTTPSProxy string `json:"httpsProxy,omitempty"`
NoProxy string `json:"noProxy,omitempty"`
HTTPProxy string `json:"httpProxy,omitempty"`
HTTPSProxy string `json:"httpsProxy,omitempty"`
ProvidedNoProxy string `json:"providedNoProxy,omitempty"`
NoProxy string `json:"noProxy,omitempty"`
}

// NetworkSpec holds the network configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,8 @@ spec:
type: string
noProxy:
type: string
providedNoProxy:
type: string
type: object
type: object
status:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ spec:
type: string
noProxy:
type: string
providedNoProxy:
type: string
type: object
type: object
status:
Expand Down
Loading
Loading