From 71ad82d3cf94b1906c6e6a03e8b6e8cde14363da Mon Sep 17 00:00:00 2001 From: Aaron Meihm Date: Tue, 5 Jul 2016 09:35:51 -0500 Subject: [PATCH] [medium] change agent proxy behavior to prefer proxies If any proxies have been configured, try those first before attempting direct connections. This also adds specifying proxies in the agent config file, where as before they were only compile time options in the built-in configuration. Resolves #249 --- conf/mig-agent-conf.go.inc | 5 +++- conf/mig-agent.cfg.inc | 5 ++++ doc/configuration.rst | 13 +++++----- mig-agent/agentcontext/env.go | 23 +++++++++++++----- mig-agent/config.go | 5 ++++ mig-agent/context.go | 46 ++++++++++++++++++++--------------- tools/standalone_install.sh | 2 +- 7 files changed, 65 insertions(+), 34 deletions(-) diff --git a/conf/mig-agent-conf.go.inc b/conf/mig-agent-conf.go.inc index f234715d..f41cf8b9 100644 --- a/conf/mig-agent-conf.go.inc +++ b/conf/mig-agent-conf.go.inc @@ -59,7 +59,10 @@ var APIURL string = "http://localhost:1664/api/v1/" // if the connection still fails after looking for a HTTP_PROXY, try to use the // proxies listed below -var PROXIES = [...]string{`proxy.example.net:3128`, `proxy2.example.net:8080`} +var PROXIES = []string{"proxy.example.net:3128", "proxy2.example.net:8080"} +// If you don't want proxies in the built-in configuration, use the following +// instead. +// var PROXIES = []string{} // local socket used to retrieve stat information from a running agent var SOCKET = "127.0.0.1:51664" diff --git a/conf/mig-agent.cfg.inc b/conf/mig-agent.cfg.inc index 4b31f931..653978f2 100644 --- a/conf/mig-agent.cfg.inc +++ b/conf/mig-agent.cfg.inc @@ -20,6 +20,11 @@ ; will attempt to restart itself instead of just shutting down isimmortal = on + ; comma delimited list of host:port proxies to use, if desired + ; the agent will attempt to try to proxies for public ip retrieval + ; and the relay connection before a direct connection + ; proxies = "proxy1:8888,proxy2:8888" + ; installservice orders the agent to deploy a service init configuration ; and start itself during the endpoint's boot process installservice = on diff --git a/doc/configuration.rst b/doc/configuration.rst index eebbac2b..4ed48ad2 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -856,11 +856,13 @@ public IP during startup. Proxy support ~~~~~~~~~~~~~ -The agent supports connecting to the relay via a CONNECT proxy. It will attempt -a direct connection first, and if this fails, will look for the environment -variable `HTTP_PROXY` to use as a proxy. A list of proxies can be manually -added to the configuration of the agent in the `PROXIES` parameters. These -proxies will be used if the two previous connections fail. +The agent supports connecting to the relay via a CONNECT proxy. If proxies are +configured, it will attempt to use them before attemping a direct connection. The +agent will also attempt to use any proxy noted in the environment via the +`HTTP_PROXY` environment variable. A list of proxies can be manually +added to the configuration of the agent in the `PROXIES` parameters. Proxies can +also be specified in the agent configuration file, and will override any built-in +configuration. An agent using a proxy will reference the name of the proxy in the environment fields of the heartbeat sent to the scheduler. @@ -1635,7 +1637,6 @@ The following parameters are **not** controlable by the configuration file: * list of investigators public keys in `PUBLICPGPKEYS` * list of access control lists in `AGENTACL` -* list of proxies in `PROXIES` All other parameters can be overriden in the configuration file. Check out the sample file `mig-agent.cfg.inc` in the **conf** folder. diff --git a/mig-agent/agentcontext/env.go b/mig-agent/agentcontext/env.go index b4e74c91..4e90857f 100644 --- a/mig-agent/agentcontext/env.go +++ b/mig-agent/agentcontext/env.go @@ -44,13 +44,12 @@ func findPublicIP(orig_ctx AgentContext, hints AgentContextHints) (ctx AgentCont Dial: (&net.Dialer{Timeout: 10 * time.Second}).Dial, } client := &http.Client{Transport: tr} - resp, err := client.Get(hints.APIUrl + "/ip") - if err != nil { - logChan <- mig.Log{Desc: fmt.Sprintf("Public IP retrieval from API failed. Trying with proxy next. Error was: %v", err)}.Info() - } else { - goto parseBody - } + var resp *http.Response + + // If any proxies have been configured, try to use those first, fall back to a + // direct connection. for _, proxy := range hints.Proxies { + logChan <- mig.Log{Desc: fmt.Sprintf("Trying proxy %v for public IP retrieval", proxy)}.Debug() pu, err := url.Parse("http://" + proxy) if err != nil { logChan <- mig.Log{Desc: fmt.Sprintf("Failed to parse proxy url http://%s - %v", proxy, err)}.Info() @@ -65,6 +64,18 @@ func findPublicIP(orig_ctx AgentContext, hints AgentContextHints) (ctx AgentCont goto parseBody } } + + // Try a direct connection, but also take into consideration any proxies that may + // have been configured in the proxy related environment variables. + logChan <- mig.Log{Desc: "Trying proxy from environment otherwise direct connection for public IP retrieval"}.Debug() + tr.Proxy = http.ProxyFromEnvironment + resp, err = client.Get(hints.APIUrl + "/ip") + if err != nil { + logChan <- mig.Log{Desc: fmt.Sprintf("Public IP retrieval from API failed. Error was: %v", err)}.Info() + } else { + goto parseBody + } + // exit here if no connection succeeded logChan <- mig.Log{Desc: fmt.Sprintf("Failed to retrieve public ip from api: %v", err)}.Err() return diff --git a/mig-agent/config.go b/mig-agent/config.go index 1a724ed2..4cd0aa36 100644 --- a/mig-agent/config.go +++ b/mig-agent/config.go @@ -9,6 +9,7 @@ package main import ( "fmt" "io/ioutil" + "strings" "time" "mig.ninja/mig" @@ -23,6 +24,7 @@ type config struct { DiscoverPublicIP bool DiscoverAWSMeta bool CheckIn bool + Proxies string Relay string Socket string HeartbeatFreq string @@ -80,6 +82,9 @@ func configLoad(path string) (err error) { MUSTINSTALLSERVICE = config.Agent.InstallService DISCOVERPUBLICIP = config.Agent.DiscoverPublicIP DISCOVERAWSMETA = config.Agent.DiscoverAWSMeta + if config.Agent.Proxies != "" { + PROXIES = strings.Split(config.Agent.Proxies, ",") + } CHECKIN = config.Agent.CheckIn LOGGINGCONF = config.Logging AMQPBROKER = config.Agent.Relay diff --git a/mig-agent/context.go b/mig-agent/context.go index e375c6cc..6448084c 100644 --- a/mig-agent/context.go +++ b/mig-agent/context.go @@ -215,29 +215,35 @@ func Init(foreground, upgrade bool) (ctx Context, err error) { connected := false // connect to the message broker - ctx, err = initMQ(ctx, false, "") - if err != nil { - ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("Failed to connect to relay directly: '%v'", err)}.Debug() - // if the connection failed, look for a proxy - // in the environment variables, and try again - ctx, err = initMQ(ctx, true, "") + // + // If any proxies have been configured, we try to use those first. If they fail, or + // no proxies have been setup, just attempt a direct connection. + for _, proxy := range PROXIES { + ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("Trying proxy %v for relay connection", proxy)}.Debug() + ctx, err = initMQ(ctx, true, proxy) if err != nil { - ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("Failed to connect to relay using HTTP_PROXY: '%v'", err)}.Debug() - // still failing, try connecting using the proxies in the configuration - for _, proxy := range PROXIES { - ctx, err = initMQ(ctx, true, proxy) - if err != nil { - ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("Failed to connect to relay using proxy %s: '%v'", proxy, err)}.Debug() - continue - } - connected = true - goto mqdone - } - } else { - connected = true + ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("Failed to connect to relay using proxy %s: '%v'", proxy, err)}.Info() + continue } - } else { connected = true + goto mqdone + } + // Try and proxy that has been specified in the environment + ctx.Channels.Log <- mig.Log{Desc: "Trying proxies from environment for relay connection"}.Debug() + ctx, err = initMQ(ctx, true, "") + if err == nil { + connected = true + goto mqdone + } else { + ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("Failed to connect to relay using HTTP_PROXY: '%v'", err)}.Info() + } + // Fall back to a direct connection + ctx.Channels.Log <- mig.Log{Desc: "Trying direct relay connection"}.Debug() + ctx, err = initMQ(ctx, false, "") + if err == nil { + connected = true + } else { + ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("Failed to connect to relay directly: '%v'", err)}.Info() } mqdone: if !connected { diff --git a/tools/standalone_install.sh b/tools/standalone_install.sh index e56e2fc2..0ba876b4 100644 --- a/tools/standalone_install.sh +++ b/tools/standalone_install.sh @@ -302,7 +302,7 @@ var LOGGINGCONF = mig.Logging{ File: "/var/cache/mig/mig-agent.log", } var AMQPBROKER string = "amqp://agent:$mqpass@localhost:5672/mig" -var PROXIES = [...]string{``} +var PROXIES = []string{} var SOCKET string = "127.0.0.1:51664" var HEARTBEATFREQ time.Duration = 30 * time.Second var REFRESHENV time.Duration = 60 * time.Second