From 20d2be1b0d90fc15afb6b56a053f8c158b03359e Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 24 Jan 2022 17:26:58 +1000 Subject: [PATCH 01/30] feat(kingpin) use kingpin to parse ENV vars, and thus allow cli-params _and_ --help Signed-off-by: Sven Dowideit --- cmd/agent/main.go | 3 +- go.mod | 3 + go.sum | 3 + os/options.go | 139 ++++++++++++---------------------------------- 4 files changed, 44 insertions(+), 104 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 1327abd6..c21aa3e7 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -81,7 +81,8 @@ func main() { log.Printf("[WARN] [main,podman] [message: Unable to retrieve local agent IP address, using '%s' instead] [error: %s]", options.AgentServerAddr, err) advertiseAddr = options.AgentServerAddr } else { - log.Fatalf("[ERROR] [main,docker] [message: Unable to retrieve local agent IP address] [error: %s]", err) + log.Printf("[ERROR] [main,docker] [message: Unable to retrieve local agent IP address] [error: %s]", err) + advertiseAddr = options.AgentServerAddr } } diff --git a/go.mod b/go.mod index 6739e6cd..4e401b7e 100644 --- a/go.mod +++ b/go.mod @@ -17,11 +17,14 @@ require ( github.com/portainer/docker-compose-wrapper v0.0.0-20210906052132-ef24824f7548 github.com/portainer/libcrypto v0.0.0-20190723020511-2cfe5519d14f github.com/portainer/libhttp v0.0.0-20190806161840-cde6e97fcd52 + gopkg.in/alecthomas/kingpin.v2 v2.2.6 k8s.io/api v0.20.6 k8s.io/client-go v0.20.6 ) require ( + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect github.com/containerd/containerd v1.5.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 8d101386..c4a96962 100644 --- a/go.sum +++ b/go.sum @@ -73,8 +73,10 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2/go.mod h1:jnzFpU88PccN/tPPhCpnNU8mZphvKxYM9lLNkd8e+os= @@ -1047,6 +1049,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/os/options.go b/os/options.go index 299f0af2..a34571dc 100644 --- a/os/options.go +++ b/os/options.go @@ -1,13 +1,10 @@ package os import ( - "errors" - "log" - "os" "strconv" - "time" "github.com/portainer/agent" + kingpin "gopkg.in/alecthomas/kingpin.v2" ) const ( @@ -34,104 +31,40 @@ func NewEnvOptionParser() *EnvOptionParser { return &EnvOptionParser{} } -func (parser *EnvOptionParser) Options() (*agent.Options, error) { - options := &agent.Options{ - AgentServerAddr: agent.DefaultAgentAddr, - AgentServerPort: agent.DefaultAgentPort, - ClusterAddress: os.Getenv(EnvKeyClusterAddr), - HostManagementEnabled: true, - SharedSecret: os.Getenv(EnvKeyAgentSecret), - EdgeID: os.Getenv(EnvKeyEdgeID), - EdgeServerAddr: agent.DefaultEdgeServerAddr, - EdgeServerPort: agent.DefaultEdgeServerPort, - EdgeInactivityTimeout: agent.DefaultEdgeSleepInterval, - EdgeInsecurePoll: false, - LogLevel: agent.DefaultLogLevel, - } - - agentSecurityShutdown, err := parseAgentSecurityShutdown() - if err != nil { - return nil, err - } - - options.AgentSecurityShutdown = agentSecurityShutdown - - if os.Getenv(EnvKeyCapHostManagement) != "" { - log.Println("[WARN] [os,options] [message: the CAP_HOST_MANAGEMENT environment variable is deprecated and will likely be removed in a future version of Portainer agent]") - } - - if os.Getenv(EnvKeyEdge) == "1" { - options.EdgeMode = true - } - - if os.Getenv(EnvKeyEdgeInsecurePoll) == "1" { - options.EdgeInsecurePoll = true - } - - if options.EdgeMode && options.EdgeID == "" { - return nil, errors.New("missing mandatory " + EnvKeyEdgeID + " environment variable") - } - - agentAddrEnv := os.Getenv(EnvKeyAgentHost) - if agentAddrEnv != "" { - options.AgentServerAddr = agentAddrEnv - } - - agentPortEnv := os.Getenv(EnvKeyAgentPort) - if agentPortEnv != "" { - _, err := strconv.Atoi(agentPortEnv) - if err != nil { - return nil, errors.New("invalid port format in " + EnvKeyAgentPort + " environment variable") - } - options.AgentServerPort = agentPortEnv - } - - edgeAddrEnv := os.Getenv(EnvKeyEdgeServerHost) - if edgeAddrEnv != "" { - options.EdgeServerAddr = edgeAddrEnv - } - - edgePortEnv := os.Getenv(EnvKeyEdgeServerPort) - if edgePortEnv != "" { - _, err := strconv.Atoi(edgePortEnv) - if err != nil { - return nil, errors.New("invalid port format in " + EnvKeyEdgeServerPort + " environment variable") - } - options.EdgeServerPort = edgePortEnv - } - - edgeKeyEnv := os.Getenv(EnvKeyEdgeKey) - if edgeKeyEnv != "" { - options.EdgeKey = edgeKeyEnv - } - - edgeSleepIntervalEnv := os.Getenv(EnvKeyEdgeInactivityTimeout) - if edgeSleepIntervalEnv != "" { - _, err := time.ParseDuration(edgeSleepIntervalEnv) - if err != nil { - return nil, errors.New("invalid time duration format in " + EnvKeyEdgeInactivityTimeout + " environment variable") - } - options.EdgeInactivityTimeout = edgeSleepIntervalEnv - } - - logLevelEnv := os.Getenv(EnvKeyLogLevel) - if logLevelEnv != "" { - options.LogLevel = logLevelEnv - } - - return options, nil -} - -func parseAgentSecurityShutdown() (time.Duration, error) { - agentSecurityShutdownStr := agent.DefaultAgentSecurityShutdown - if value := os.Getenv(EnvKeyAgentSecurityShutdown); value != "" { - agentSecurityShutdownStr = value - } - - duration, err := time.ParseDuration(agentSecurityShutdownStr) - if err != nil { - return time.Second, errors.New("invalid time duration format in " + EnvKeyAgentSecurityShutdown + " environment variable") - } +var ( + fAgentServerAddr = kingpin.Flag("AgentServerAddr", "").Envar(EnvKeyAgentHost).Default(agent.DefaultAgentAddr).IP() + fAgentServerPort = kingpin.Flag("AgentServerPort", "").Envar(EnvKeyAgentPort).Default(agent.DefaultAgentPort).Int() + fAgentSecurityShutdown = kingpin.Flag("AgentSecurityShutdown", "").Envar(EnvKeyAgentSecurityShutdown).Default(agent.DefaultAgentSecurityShutdown).Duration() + fClusterAddress = kingpin.Flag("ClusterAddress", "").Envar(EnvKeyClusterAddr).String() + fSharedSecret = kingpin.Flag("SharedSecret", "").Envar(EnvKeyAgentSecret).String() + fLogLevel = kingpin.Flag("LogLevel", "").Envar(EnvKeyLogLevel).Default(agent.DefaultLogLevel).Enum("INFO", "") + + // Edge mode + fEdgeMode = kingpin.Flag("EdgeMode", "").Envar(EnvKeyEdge).Bool() + fEdgeKey = kingpin.Flag("EdgeKey", "").Envar(EnvKeyEdgeKey).String() + fEdgeID = kingpin.Flag("EdgeID", "").Envar(EnvKeyEdgeID).String() + fEdgeServerAddr = kingpin.Flag("EdgeServerAddr", "").Envar(EnvKeyEdgeServerHost).Default(agent.DefaultEdgeServerAddr).IP() + fEdgeServerPort = kingpin.Flag("EdgeServerPort", "").Envar(EnvKeyEdgeServerPort).Default(agent.DefaultEdgeServerPort).Int() + fEdgeInactivityTimeout = kingpin.Flag("EdgeInactivityTimeout", "").Envar(EnvKeyEdgeInactivityTimeout).Default(agent.DefaultEdgeSleepInterval).String() + fEdgeInsecurePoll = kingpin.Flag("EdgeInsecurePoll", "").Envar(EnvKeyEdgeInsecurePoll).Bool() +) - return duration, nil +func (parser *EnvOptionParser) Options() (*agent.Options, error) { + kingpin.Parse() + return &agent.Options{ + AgentServerAddr: fAgentServerAddr.String(), + AgentServerPort: strconv.Itoa(*fAgentServerPort), + AgentSecurityShutdown: *fAgentSecurityShutdown, + ClusterAddress: *fClusterAddress, + HostManagementEnabled: true, // TODO: is this a constant? can we get rid of it? + SharedSecret: *fSharedSecret, + EdgeMode: *fEdgeMode, + EdgeKey: *fEdgeKey, + EdgeID: *fEdgeID, + EdgeServerAddr: fEdgeServerAddr.String(), // TODO: really, an agent can't be both edge and non-edge, so we don't need both AgentServerAddr and EdgeServerAddr ? + EdgeServerPort: strconv.Itoa(*fEdgeServerPort), + EdgeInactivityTimeout: *fEdgeInactivityTimeout, + EdgeInsecurePoll: *fEdgeInsecurePoll, + LogLevel: *fLogLevel, + }, nil } From fc226edd92b52eee366b95d7880b64f279a8920f Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 24 Jan 2022 17:44:20 +1000 Subject: [PATCH 02/30] clean out unused env var names Signed-off-by: Sven Dowideit --- os/options.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/options.go b/os/options.go index a34571dc..3534bc00 100644 --- a/os/options.go +++ b/os/options.go @@ -13,7 +13,7 @@ const ( EnvKeyClusterAddr = "AGENT_CLUSTER_ADDR" EnvKeyAgentSecret = "AGENT_SECRET" EnvKeyAgentSecurityShutdown = "AGENT_SECRET_TIMEOUT" - EnvKeyCapHostManagement = "CAP_HOST_MANAGEMENT" + //EnvKeyCapHostManagement = "CAP_HOST_MANAGEMENT" // deprecated and unused EnvKeyEdge = "EDGE" EnvKeyEdgeKey = "EDGE_KEY" EnvKeyEdgeID = "EDGE_ID" @@ -22,7 +22,7 @@ const ( EnvKeyEdgeInactivityTimeout = "EDGE_INACTIVITY_TIMEOUT" EnvKeyEdgeInsecurePoll = "EDGE_INSECURE_POLL" EnvKeyLogLevel = "LOG_LEVEL" - EnvKeyDockerBinaryPath = "DOCKER_BINARY_PATH" + //EnvKeyDockerBinaryPath = "DOCKER_BINARY_PATH" //unused ) type EnvOptionParser struct{} From 6b4596ab83fdec62c9aba7e88b1d1265b0402f0d Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 24 Jan 2022 18:09:25 +1000 Subject: [PATCH 03/30] use the ENV text from the README Signed-off-by: Sven Dowideit --- os/options.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/os/options.go b/os/options.go index 3534bc00..56c64041 100644 --- a/os/options.go +++ b/os/options.go @@ -32,21 +32,21 @@ func NewEnvOptionParser() *EnvOptionParser { } var ( - fAgentServerAddr = kingpin.Flag("AgentServerAddr", "").Envar(EnvKeyAgentHost).Default(agent.DefaultAgentAddr).IP() - fAgentServerPort = kingpin.Flag("AgentServerPort", "").Envar(EnvKeyAgentPort).Default(agent.DefaultAgentPort).Int() - fAgentSecurityShutdown = kingpin.Flag("AgentSecurityShutdown", "").Envar(EnvKeyAgentSecurityShutdown).Default(agent.DefaultAgentSecurityShutdown).Duration() - fClusterAddress = kingpin.Flag("ClusterAddress", "").Envar(EnvKeyClusterAddr).String() - fSharedSecret = kingpin.Flag("SharedSecret", "").Envar(EnvKeyAgentSecret).String() - fLogLevel = kingpin.Flag("LogLevel", "").Envar(EnvKeyLogLevel).Default(agent.DefaultLogLevel).Enum("INFO", "") + fAgentServerAddr = kingpin.Flag("AgentServerAddr", EnvKeyAgentHost+" address on which the agent API will be exposed").Envar(EnvKeyAgentHost).Default(agent.DefaultAgentAddr).IP() + fAgentServerPort = kingpin.Flag("AgentServerPort", EnvKeyAgentPort+" port on which the agent API will be exposed").Envar(EnvKeyAgentPort).Default(agent.DefaultAgentPort).Int() + fAgentSecurityShutdown = kingpin.Flag("AgentSecurityShutdown", EnvKeyAgentSecurityShutdown+" the duration after which the agent will be shutdown if not associated or secured by AGENT_SECRET. (defaults to 72h)").Envar(EnvKeyAgentSecurityShutdown).Default(agent.DefaultAgentSecurityShutdown).Duration() + fClusterAddress = kingpin.Flag("ClusterAddress", EnvKeyClusterAddr+" address (in the IP:PORT format) of an existing agent to join the agent cluster. When deploying the agent as a Docker Swarm service, we can leverage the internal Docker DNS to automatically join existing agents or form a cluster by using tasks.: as the address").Envar(EnvKeyClusterAddr).String() + fSharedSecret = kingpin.Flag("SharedSecret", EnvKeyAgentSecret+" shared secret used in the signature verification process").Envar(EnvKeyAgentSecret).String() + fLogLevel = kingpin.Flag("LogLevel", EnvKeyLogLevel+" defines the log output verbosity (default to INFO)").Envar(EnvKeyLogLevel).Default(agent.DefaultLogLevel).Enum("INFO", "") // Edge mode - fEdgeMode = kingpin.Flag("EdgeMode", "").Envar(EnvKeyEdge).Bool() - fEdgeKey = kingpin.Flag("EdgeKey", "").Envar(EnvKeyEdgeKey).String() - fEdgeID = kingpin.Flag("EdgeID", "").Envar(EnvKeyEdgeID).String() - fEdgeServerAddr = kingpin.Flag("EdgeServerAddr", "").Envar(EnvKeyEdgeServerHost).Default(agent.DefaultEdgeServerAddr).IP() - fEdgeServerPort = kingpin.Flag("EdgeServerPort", "").Envar(EnvKeyEdgeServerPort).Default(agent.DefaultEdgeServerPort).Int() - fEdgeInactivityTimeout = kingpin.Flag("EdgeInactivityTimeout", "").Envar(EnvKeyEdgeInactivityTimeout).Default(agent.DefaultEdgeSleepInterval).String() - fEdgeInsecurePoll = kingpin.Flag("EdgeInsecurePoll", "").Envar(EnvKeyEdgeInsecurePoll).Bool() + fEdgeMode = kingpin.Flag("EdgeMode", EnvKeyEdge+" enable Edge mode. Disabled by default, set to 1 or true to enable it").Envar(EnvKeyEdge).Bool() + fEdgeKey = kingpin.Flag("EdgeKey", EnvKeyEdgeKey+" specify an Edge key to use at startup").Envar(EnvKeyEdgeKey).String() + fEdgeID = kingpin.Flag("EdgeID", EnvKeyEdgeID+" a unique identifier associated to this agent cluster").Envar(EnvKeyEdgeID).String() + fEdgeServerAddr = kingpin.Flag("EdgeServerAddr", EnvKeyEdgeServerHost+" address on which the Edge UI will be exposed (default to 0.0.0.0)").Envar(EnvKeyEdgeServerHost).Default(agent.DefaultEdgeServerAddr).IP() + fEdgeServerPort = kingpin.Flag("EdgeServerPort", EnvKeyEdgeServerPort+" port on which the Edge UI will be exposed (default to 80)").Envar(EnvKeyEdgeServerPort).Default(agent.DefaultEdgeServerPort).Int() + fEdgeInactivityTimeout = kingpin.Flag("EdgeInactivityTimeout", EnvKeyEdgeInactivityTimeout+" timeout used by the agent to close the reverse tunnel after inactivity (default to 5m)").Envar(EnvKeyEdgeInactivityTimeout).Default(agent.DefaultEdgeSleepInterval).String() + fEdgeInsecurePoll = kingpin.Flag("EdgeInsecurePoll", EnvKeyEdgeInsecurePoll+" enable this option if you need the agent to poll a HTTPS Portainer instance with self-signed certificates. Disabled by default, set to 1 to enable it").Envar(EnvKeyEdgeInsecurePoll).Bool() ) func (parser *EnvOptionParser) Options() (*agent.Options, error) { From b1766dc33944a12875cb4ae4068baab3a568745d Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 12:02:02 +1000 Subject: [PATCH 04/30] HostManagementEnabled was hardcoded to true - I presume this was once a feature? Signed-off-by: Sven Dowideit --- agent.go | 2 +- http/handler/browse/browse_delete.go | 5 ----- http/handler/browse/browse_get.go | 5 ----- http/handler/browse/browse_list.go | 5 ----- http/handler/browse/browse_rename.go | 4 ---- os/options.go | 1 - 6 files changed, 1 insertion(+), 21 deletions(-) diff --git a/agent.go b/agent.go index d9c061e8..32b94dfc 100644 --- a/agent.go +++ b/agent.go @@ -60,7 +60,7 @@ type ( AgentServerPort string AgentSecurityShutdown time.Duration ClusterAddress string - HostManagementEnabled bool + //HostManagementEnabled bool SharedSecret string EdgeMode bool EdgeKey string diff --git a/http/handler/browse/browse_delete.go b/http/handler/browse/browse_delete.go index dfd5d526..3084af9a 100644 --- a/http/handler/browse/browse_delete.go +++ b/http/handler/browse/browse_delete.go @@ -1,7 +1,6 @@ package browse import ( - "errors" "net/http" "github.com/portainer/agent/filesystem" @@ -13,10 +12,6 @@ import ( // DELETE request on /browse/delete?volumeID=:id&path=:path func (handler *Handler) browseDelete(rw http.ResponseWriter, r *http.Request) *httperror.HandlerError { volumeID, _ := request.RetrieveQueryParameter(r, "volumeID", true) - if volumeID == "" && !handler.agentOptions.HostManagementEnabled { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Host management capability disabled", errors.New("This agent feature is not enabled")} - } - path, err := request.RetrieveQueryParameter(r, "path", false) if err != nil { return &httperror.HandlerError{http.StatusBadRequest, "Invalid query parameter: path", err} diff --git a/http/handler/browse/browse_get.go b/http/handler/browse/browse_get.go index 7c2c0745..dae5b19c 100644 --- a/http/handler/browse/browse_get.go +++ b/http/handler/browse/browse_get.go @@ -1,7 +1,6 @@ package browse import ( - "errors" "net/http" "github.com/portainer/agent/filesystem" @@ -12,10 +11,6 @@ import ( // GET request on /browse/get?volumeID=:id&path=:path func (handler *Handler) browseGet(rw http.ResponseWriter, r *http.Request) *httperror.HandlerError { volumeID, _ := request.RetrieveQueryParameter(r, "volumeID", true) - if volumeID == "" && !handler.agentOptions.HostManagementEnabled { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Host management capability disabled", errors.New("This agent feature is not enabled")} - } - path, err := request.RetrieveQueryParameter(r, "path", false) if err != nil { return &httperror.HandlerError{http.StatusBadRequest, "Invalid query parameter: path", err} diff --git a/http/handler/browse/browse_list.go b/http/handler/browse/browse_list.go index 60ac9169..a2a9022f 100644 --- a/http/handler/browse/browse_list.go +++ b/http/handler/browse/browse_list.go @@ -1,7 +1,6 @@ package browse import ( - "errors" "net/http" "github.com/portainer/agent/filesystem" @@ -13,10 +12,6 @@ import ( // GET request on /browse/ls?volumeID=:id&path=:path func (handler *Handler) browseList(rw http.ResponseWriter, r *http.Request) *httperror.HandlerError { volumeID, _ := request.RetrieveQueryParameter(r, "volumeID", true) - if volumeID == "" && !handler.agentOptions.HostManagementEnabled { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Host management capability disabled", errors.New("This agent feature is not enabled")} - } - path, err := request.RetrieveQueryParameter(r, "path", false) if err != nil { return &httperror.HandlerError{http.StatusBadRequest, "Invalid query parameter: path", err} diff --git a/http/handler/browse/browse_rename.go b/http/handler/browse/browse_rename.go index 2e223415..a11054fc 100644 --- a/http/handler/browse/browse_rename.go +++ b/http/handler/browse/browse_rename.go @@ -29,10 +29,6 @@ func (payload *browseRenamePayload) Validate(r *http.Request) error { // PUT request on /browse/rename?volumeID=:id func (handler *Handler) browseRename(rw http.ResponseWriter, r *http.Request) *httperror.HandlerError { volumeID, _ := request.RetrieveQueryParameter(r, "volumeID", true) - if volumeID == "" && !handler.agentOptions.HostManagementEnabled { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Host management capability disabled", errors.New("This agent feature is not enabled")} - } - var payload browseRenamePayload err := request.DecodeAndValidateJSONPayload(r, &payload) if err != nil { diff --git a/os/options.go b/os/options.go index 56c64041..e251e40d 100644 --- a/os/options.go +++ b/os/options.go @@ -56,7 +56,6 @@ func (parser *EnvOptionParser) Options() (*agent.Options, error) { AgentServerPort: strconv.Itoa(*fAgentServerPort), AgentSecurityShutdown: *fAgentSecurityShutdown, ClusterAddress: *fClusterAddress, - HostManagementEnabled: true, // TODO: is this a constant? can we get rid of it? SharedSecret: *fSharedSecret, EdgeMode: *fEdgeMode, EdgeKey: *fEdgeKey, From f1c3af2986133a49f1fd781dafcf83f10c694ec1 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 12:17:07 +1000 Subject: [PATCH 05/30] do we need to store a copy of a bool we have access to? Signed-off-by: Sven Dowideit --- agent.go | 1 - internal/edge/edge.go | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/agent.go b/agent.go index 32b94dfc..72188245 100644 --- a/agent.go +++ b/agent.go @@ -60,7 +60,6 @@ type ( AgentServerPort string AgentSecurityShutdown time.Duration ClusterAddress string - //HostManagementEnabled bool SharedSecret string EdgeMode bool EdgeKey string diff --git a/internal/edge/edge.go b/internal/edge/edge.go index 748d22a5..1873e598 100644 --- a/internal/edge/edge.go +++ b/internal/edge/edge.go @@ -16,7 +16,6 @@ type ( advertiseAddr string agentOptions *agent.Options clusterService agent.ClusterService - edgeMode bool dockerInfoService agent.DockerInfoService key *edgeKey logsManager *logsManager @@ -42,7 +41,6 @@ func NewManager(parameters *ManagerParameters) *Manager { dockerInfoService: parameters.DockerInfoService, agentOptions: parameters.Options, advertiseAddr: parameters.AdvertiseAddr, - edgeMode: parameters.Options.EdgeMode, containerPlatform: parameters.ContainerPlatform, } } @@ -95,7 +93,7 @@ func (manager *Manager) Start() error { // IsEdgeModeEnabled returns true if edge mode is enabled func (manager *Manager) IsEdgeModeEnabled() bool { - return manager.edgeMode + return manager.agentOptions.EdgeMode } // ResetActivityTimer resets the activity timer From 53a6bf15d28ab1f325fdbb5608c2e9d367b21092 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 13:20:52 +1000 Subject: [PATCH 06/30] add the LogLevels to the flag options Signed-off-by: Sven Dowideit --- os/options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/options.go b/os/options.go index e251e40d..fafa2ae6 100644 --- a/os/options.go +++ b/os/options.go @@ -37,7 +37,7 @@ var ( fAgentSecurityShutdown = kingpin.Flag("AgentSecurityShutdown", EnvKeyAgentSecurityShutdown+" the duration after which the agent will be shutdown if not associated or secured by AGENT_SECRET. (defaults to 72h)").Envar(EnvKeyAgentSecurityShutdown).Default(agent.DefaultAgentSecurityShutdown).Duration() fClusterAddress = kingpin.Flag("ClusterAddress", EnvKeyClusterAddr+" address (in the IP:PORT format) of an existing agent to join the agent cluster. When deploying the agent as a Docker Swarm service, we can leverage the internal Docker DNS to automatically join existing agents or form a cluster by using tasks.: as the address").Envar(EnvKeyClusterAddr).String() fSharedSecret = kingpin.Flag("SharedSecret", EnvKeyAgentSecret+" shared secret used in the signature verification process").Envar(EnvKeyAgentSecret).String() - fLogLevel = kingpin.Flag("LogLevel", EnvKeyLogLevel+" defines the log output verbosity (default to INFO)").Envar(EnvKeyLogLevel).Default(agent.DefaultLogLevel).Enum("INFO", "") + fLogLevel = kingpin.Flag("LogLevel", EnvKeyLogLevel+" defines the log output verbosity (default to INFO)").Envar(EnvKeyLogLevel).Default(agent.DefaultLogLevel).Enum("ERROR", "WARN", "INFO", "DEBUG") // Edge mode fEdgeMode = kingpin.Flag("EdgeMode", EnvKeyEdge+" enable Edge mode. Disabled by default, set to 1 or true to enable it").Envar(EnvKeyEdge).Bool() From 4a5756b804d7836e0a23310090e6a3784cd69c56 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 14:28:57 +1000 Subject: [PATCH 07/30] Don't try over-writing the existing /data/agent_edge_key - its 0444 Signed-off-by: Sven Dowideit --- internal/edge/key.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/edge/key.go b/internal/edge/key.go index 89823050..d35ebd8e 100644 --- a/internal/edge/key.go +++ b/internal/edge/key.go @@ -20,15 +20,26 @@ type edgeKey struct { // SetKey parses and associates an Edge key to the agent. // If the agent is running inside a Swarm cluster, it will also set the "set" flag to specify that a key is set on this agent in the cluster. +// Don't overwrite the file if it exists - this only appears to work, because our container runs as root func (manager *Manager) SetKey(key string) error { edgeKey, err := parseEdgeKey(key) if err != nil { return err } - err = filesystem.WriteFile(agent.DataDirectory, agent.EdgeKeyFile, []byte(key), 0444) - if err != nil { - return err + // TODO: yeah, we don't know if we got it from the fs, so lets try again :/ + keyRetrieval, _ := retrieveEdgeKeyFromFilesystem() + if keyRetrieval == "" { + // key not previously saved + err = filesystem.WriteFile(agent.DataDirectory, agent.EdgeKeyFile, []byte(key), 0444) + if err != nil { + return err + } + keyRetrieval = key + } + if keyRetrieval != key { + // TODO: ok, I'm not sure if it should die, or delete the file and re-write it. + log.Fatalf("EdgeKey modified from %s to %s", keyRetrieval, key) } manager.key = edgeKey From 1aff82ae0aec9bb341cd862731d8006bf619ee22 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 13:05:53 +1000 Subject: [PATCH 08/30] EdgeKey retreival code should be with the other EdgeKey code Signed-off-by: Sven Dowideit --- cmd/agent/main.go | 72 +------------------------------------------- internal/edge/key.go | 69 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 71 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index c21aa3e7..949dea0d 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -14,10 +14,8 @@ import ( "github.com/portainer/agent/crypto" "github.com/portainer/agent/docker" "github.com/portainer/agent/exec" - "github.com/portainer/agent/filesystem" "github.com/portainer/agent/ghw" "github.com/portainer/agent/http" - "github.com/portainer/agent/http/client" "github.com/portainer/agent/internal/edge" "github.com/portainer/agent/kubernetes" "github.com/portainer/agent/logutils" @@ -190,7 +188,7 @@ func main() { edgeManager := edge.NewManager(edgeManagerParameters) if options.EdgeMode { - edgeKey, err := retrieveEdgeKey(options.EdgeKey, clusterService) + edgeKey, err := edge.RetrieveEdgeKey(options.EdgeKey, clusterService) if err != nil { log.Printf("[ERROR] [main,edge] [message: Unable to retrieve Edge key] [error: %s]", err) } @@ -290,71 +288,3 @@ func serveEdgeUI(edgeManager *edge.Manager, serverAddr, serverPort string) { } }() } - -func retrieveEdgeKey(edgeKey string, clusterService agent.ClusterService) (string, error) { - - if edgeKey != "" { - log.Println("[INFO] [main,edge] [message: Edge key loaded from options]") - return edgeKey, nil - } - - var keyRetrievalError error - - edgeKey, keyRetrievalError = retrieveEdgeKeyFromFilesystem() - if keyRetrievalError != nil { - return "", keyRetrievalError - } - - if edgeKey == "" && clusterService != nil { - edgeKey, keyRetrievalError = retrieveEdgeKeyFromCluster(clusterService) - if keyRetrievalError != nil { - return "", keyRetrievalError - } - } - - return edgeKey, nil -} - -func retrieveEdgeKeyFromFilesystem() (string, error) { - var edgeKey string - - edgeKeyFilePath := fmt.Sprintf("%s/%s", agent.DataDirectory, agent.EdgeKeyFile) - - keyFileExists, err := filesystem.FileExists(edgeKeyFilePath) - if err != nil { - return "", err - } - - if keyFileExists { - filesystemKey, err := filesystem.ReadFromFile(edgeKeyFilePath) - if err != nil { - return "", err - } - - log.Println("[INFO] [main,edge] [message: Edge key loaded from the filesystem]") - edgeKey = string(filesystemKey) - } - - return edgeKey, nil -} - -func retrieveEdgeKeyFromCluster(clusterService agent.ClusterService) (string, error) { - var edgeKey string - - member := clusterService.GetMemberWithEdgeKeySet() - if member != nil { - httpCli := client.NewAPIClient() - - memberAddr := fmt.Sprintf("%s:%s", member.IPAddress, member.Port) - memberKey, err := httpCli.GetEdgeKey(memberAddr) - if err != nil { - log.Printf("[ERROR] [main,edge,http,cluster] [message: Unable to retrieve Edge key from cluster member] [error: %s]", err) - return "", err - } - - log.Println("[INFO] [main,edge] [message: Edge key loaded from cluster]") - edgeKey = memberKey - } - - return edgeKey, nil -} diff --git a/internal/edge/key.go b/internal/edge/key.go index d35ebd8e..a82d77aa 100644 --- a/internal/edge/key.go +++ b/internal/edge/key.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "errors" "fmt" + "log" "strings" "github.com/portainer/agent" @@ -133,3 +134,71 @@ func encodeKey(edgeKey *edgeKey) string { encodedKey := base64.RawStdEncoding.EncodeToString([]byte(keyInfo)) return encodedKey } + +func RetrieveEdgeKey(edgeKey string, clusterService agent.ClusterService) (string, error) { + + if edgeKey != "" { + log.Println("[INFO] [main,edge] [message: Edge key loaded from options]") + return edgeKey, nil + } + + var keyRetrievalError error + + edgeKey, keyRetrievalError = retrieveEdgeKeyFromFilesystem() + if keyRetrievalError != nil { + return "", keyRetrievalError + } + + if edgeKey == "" && clusterService != nil { + edgeKey, keyRetrievalError = retrieveEdgeKeyFromCluster(clusterService) + if keyRetrievalError != nil { + return "", keyRetrievalError + } + } + + return edgeKey, nil +} + +func retrieveEdgeKeyFromFilesystem() (string, error) { + var edgeKey string + + edgeKeyFilePath := fmt.Sprintf("%s/%s", agent.DataDirectory, agent.EdgeKeyFile) + + keyFileExists, err := filesystem.FileExists(edgeKeyFilePath) + if err != nil { + return "", err + } + + if keyFileExists { + filesystemKey, err := filesystem.ReadFromFile(edgeKeyFilePath) + if err != nil { + return "", err + } + + log.Println("[INFO] [main,edge] [message: Edge key loaded from the filesystem]") + edgeKey = string(filesystemKey) + } + + return edgeKey, nil +} + +func retrieveEdgeKeyFromCluster(clusterService agent.ClusterService) (string, error) { + var edgeKey string + + member := clusterService.GetMemberWithEdgeKeySet() + if member != nil { + httpCli := client.NewAPIClient() + + memberAddr := fmt.Sprintf("%s:%s", member.IPAddress, member.Port) + memberKey, err := httpCli.GetEdgeKey(memberAddr) + if err != nil { + log.Printf("[ERROR] [main,edge,http,cluster] [message: Unable to retrieve Edge key from cluster member] [error: %s]", err) + return "", err + } + + log.Println("[INFO] [main,edge] [message: Edge key loaded from cluster]") + edgeKey = memberKey + } + + return edgeKey, nil +} From d21c02a71663225f27356eac1c6acb61e15704ea Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 14:12:29 +1000 Subject: [PATCH 09/30] PROPOSAL: don't create an edgeManager if we're not in Edge mode Signed-off-by: Sven Dowideit --- cmd/agent/main.go | 24 +++++++++++++----------- http/handler/handler.go | 4 ++-- http/handler/key/key_create.go | 2 +- http/handler/key/key_inspect.go | 2 +- internal/edge/edge.go | 5 ----- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 949dea0d..aa0483f9 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -178,16 +178,17 @@ func main() { // !Security // Edge - edgeManagerParameters := &edge.ManagerParameters{ - Options: options, - AdvertiseAddr: advertiseAddr, - ClusterService: clusterService, - DockerInfoService: dockerInfoService, - ContainerPlatform: containerPlatform, - } - edgeManager := edge.NewManager(edgeManagerParameters) - + var edgeManager *edge.Manager if options.EdgeMode { + edgeManagerParameters := &edge.ManagerParameters{ + Options: options, + AdvertiseAddr: advertiseAddr, + ClusterService: clusterService, + DockerInfoService: dockerInfoService, + ContainerPlatform: containerPlatform, + } + edgeManager = edge.NewManager(edgeManagerParameters) + edgeKey, err := edge.RetrieveEdgeKey(options.EdgeKey, clusterService) if err != nil { log.Printf("[ERROR] [main,edge] [message: Unable to retrieve Edge key] [error: %s]", err) @@ -196,6 +197,7 @@ func main() { if edgeKey != "" { log.Println("[DEBUG] [main,edge] [message: Edge key found in environment. Associating Edge key]") + // TODO: this fails when RetreiveEdgeKey came from an existing file, as mode 0444 can't be written to - unless you're root... err := edgeManager.SetKey(edgeKey) if err != nil { log.Fatalf("[ERROR] [main,edge] [message: Unable to associate Edge key] [error: %s]", err) @@ -231,7 +233,7 @@ func main() { ContainerPlatform: containerPlatform, } - if edgeManager.IsEdgeModeEnabled() { + if options.EdgeMode { config.Addr = advertiseAddr } @@ -252,7 +254,7 @@ func main() { func startAPIServer(config *http.APIServerConfig) error { server := http.NewAPIServer(config) - if config.EdgeManager.IsEdgeModeEnabled() { + if config.EdgeManager != nil { return server.StartUnsecured() } diff --git a/http/handler/handler.go b/http/handler/handler.go index 22ff7c5e..4b2eaa5e 100644 --- a/http/handler/handler.go +++ b/http/handler/handler.go @@ -91,12 +91,12 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, request *http.Request) { return } - if !h.securedProtocol && !h.edgeManager.IsKeySet() { + if !h.securedProtocol && !(h.edgeManager != nil && h.edgeManager.IsKeySet()) { httperror.WriteError(rw, http.StatusForbidden, "Unable to use the unsecured agent API without Edge key", errors.New("edge key not set")) return } - if h.edgeManager.IsEdgeModeEnabled() { + if h.edgeManager != nil { h.edgeManager.ResetActivityTimer() } diff --git a/http/handler/key/key_create.go b/http/handler/key/key_create.go index 372a73b9..17a00b8f 100644 --- a/http/handler/key/key_create.go +++ b/http/handler/key/key_create.go @@ -23,7 +23,7 @@ func (payload *keyCreatePayload) Validate(r *http.Request) error { } func (handler *Handler) keyCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - if !handler.edgeManager.IsEdgeModeEnabled() { + if handler.edgeManager == nil { return &httperror.HandlerError{http.StatusServiceUnavailable, "Edge key management is disabled on non Edge agent", errors.New("Edge key management is disabled")} } diff --git a/http/handler/key/key_inspect.go b/http/handler/key/key_inspect.go index 0b9bd25f..2987e948 100644 --- a/http/handler/key/key_inspect.go +++ b/http/handler/key/key_inspect.go @@ -13,7 +13,7 @@ type keyInspectResponse struct { } func (handler *Handler) keyInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - if !handler.edgeManager.IsEdgeModeEnabled() { + if handler.edgeManager == nil { return &httperror.HandlerError{http.StatusServiceUnavailable, "Edge key management is disabled on non Edge agent", errors.New("Edge key management is disabled")} } diff --git a/internal/edge/edge.go b/internal/edge/edge.go index 1873e598..fc544d11 100644 --- a/internal/edge/edge.go +++ b/internal/edge/edge.go @@ -91,11 +91,6 @@ func (manager *Manager) Start() error { return nil } -// IsEdgeModeEnabled returns true if edge mode is enabled -func (manager *Manager) IsEdgeModeEnabled() bool { - return manager.agentOptions.EdgeMode -} - // ResetActivityTimer resets the activity timer func (manager *Manager) ResetActivityTimer() { manager.pollService.resetActivityTimer() From 84c35e767d03d3fe80c50af55bc37477b5d83fb3 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 16:43:12 +1000 Subject: [PATCH 10/30] add todo Signed-off-by: Sven Dowideit --- http/handler/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/handler/handler.go b/http/handler/handler.go index 4b2eaa5e..ec46728e 100644 --- a/http/handler/handler.go +++ b/http/handler/handler.go @@ -41,7 +41,7 @@ type Handler struct { hostHandler *host.Handler pingHandler *ping.Handler securedProtocol bool - edgeManager *edge.Manager + edgeManager *edge.Manager // TODO: I suspect we should not store this here containerPlatform agent.ContainerPlatform } From 953c7205887f1e683ef122b07356f39c009d060e Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 17:01:06 +1000 Subject: [PATCH 11/30] JUST git mv internal/edge/ to edge/ Signed-off-by: Sven Dowideit --- cmd/agent/main.go | 2 +- {internal/edge => edge}/edge.go | 0 {internal/edge => edge}/key.go | 0 {internal/edge => edge}/logs.go | 0 {internal/edge => edge}/poll.go | 0 {internal/edge => edge}/stack.go | 0 http/edge.go | 2 +- http/handler/handler.go | 2 +- http/handler/key/handler.go | 2 +- http/server.go | 2 +- 10 files changed, 5 insertions(+), 5 deletions(-) rename {internal/edge => edge}/edge.go (100%) rename {internal/edge => edge}/key.go (100%) rename {internal/edge => edge}/logs.go (100%) rename {internal/edge => edge}/poll.go (100%) rename {internal/edge => edge}/stack.go (100%) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index aa0483f9..ecddc9e1 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -13,10 +13,10 @@ import ( "github.com/portainer/agent" "github.com/portainer/agent/crypto" "github.com/portainer/agent/docker" + "github.com/portainer/agent/edge" "github.com/portainer/agent/exec" "github.com/portainer/agent/ghw" "github.com/portainer/agent/http" - "github.com/portainer/agent/internal/edge" "github.com/portainer/agent/kubernetes" "github.com/portainer/agent/logutils" "github.com/portainer/agent/net" diff --git a/internal/edge/edge.go b/edge/edge.go similarity index 100% rename from internal/edge/edge.go rename to edge/edge.go diff --git a/internal/edge/key.go b/edge/key.go similarity index 100% rename from internal/edge/key.go rename to edge/key.go diff --git a/internal/edge/logs.go b/edge/logs.go similarity index 100% rename from internal/edge/logs.go rename to edge/logs.go diff --git a/internal/edge/poll.go b/edge/poll.go similarity index 100% rename from internal/edge/poll.go rename to edge/poll.go diff --git a/internal/edge/stack.go b/edge/stack.go similarity index 100% rename from internal/edge/stack.go rename to edge/stack.go diff --git a/http/edge.go b/http/edge.go index 59200742..312b56d4 100644 --- a/http/edge.go +++ b/http/edge.go @@ -6,7 +6,7 @@ import ( "net/http" "time" - "github.com/portainer/agent/internal/edge" + "github.com/portainer/agent/edge" "github.com/gorilla/mux" ) diff --git a/http/handler/handler.go b/http/handler/handler.go index ec46728e..7074e579 100644 --- a/http/handler/handler.go +++ b/http/handler/handler.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/portainer/agent" + "github.com/portainer/agent/edge" "github.com/portainer/agent/exec" httpagenthandler "github.com/portainer/agent/http/handler/agent" "github.com/portainer/agent/http/handler/browse" @@ -21,7 +22,6 @@ import ( "github.com/portainer/agent/http/handler/websocket" "github.com/portainer/agent/http/proxy" "github.com/portainer/agent/http/security" - "github.com/portainer/agent/internal/edge" kubecli "github.com/portainer/agent/kubernetes" httperror "github.com/portainer/libhttp/error" ) diff --git a/http/handler/key/handler.go b/http/handler/key/handler.go index 45b1d525..e41d5bb4 100644 --- a/http/handler/key/handler.go +++ b/http/handler/key/handler.go @@ -4,8 +4,8 @@ import ( "net/http" "github.com/gorilla/mux" + "github.com/portainer/agent/edge" "github.com/portainer/agent/http/security" - "github.com/portainer/agent/internal/edge" httperror "github.com/portainer/libhttp/error" ) diff --git a/http/server.go b/http/server.go index be29c80e..a371b0ec 100644 --- a/http/server.go +++ b/http/server.go @@ -8,9 +8,9 @@ import ( "time" "github.com/portainer/agent" + "github.com/portainer/agent/edge" "github.com/portainer/agent/exec" "github.com/portainer/agent/http/handler" - "github.com/portainer/agent/internal/edge" "github.com/portainer/agent/kubernetes" ) From 5701949b029a841a73dcf4fa2c633a7444ba1720 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 17:05:11 +1000 Subject: [PATCH 12/30] git mv the scheduler code from filesystem/ to edge/scheduler/ Signed-off-by: Sven Dowideit --- edge/poll.go | 4 ++-- {filesystem => edge/scheduler}/scheduler.go | 10 ++++++---- {filesystem => edge/scheduler}/scheduler_windows.go | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) rename {filesystem => edge/scheduler}/scheduler.go (88%) rename {filesystem => edge/scheduler}/scheduler_windows.go (86%) diff --git a/edge/poll.go b/edge/poll.go index 14944835..adb85d40 100644 --- a/edge/poll.go +++ b/edge/poll.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/portainer/agent/edge/scheduler" "log" "net/http" "strconv" @@ -13,7 +14,6 @@ import ( "github.com/portainer/agent" "github.com/portainer/agent/chisel" - "github.com/portainer/agent/filesystem" "github.com/portainer/libcrypto" ) @@ -74,7 +74,7 @@ func newPollService(edgeStackManager *StackManager, logsManager *logsManager, co insecurePoll: config.InsecurePoll, inactivityTimeout: inactivityTimeout, tunnelClient: chisel.NewClient(), - scheduleManager: filesystem.NewCronManager(), + scheduleManager: scheduler.NewCronManager(), refreshSignal: nil, edgeStackManager: edgeStackManager, portainerURL: config.PortainerURL, diff --git a/filesystem/scheduler.go b/edge/scheduler/scheduler.go similarity index 88% rename from filesystem/scheduler.go rename to edge/scheduler/scheduler.go index 3613842f..f65df352 100644 --- a/filesystem/scheduler.go +++ b/edge/scheduler/scheduler.go @@ -1,10 +1,12 @@ +//go:build !windows // +build !windows -package filesystem +package scheduler import ( "encoding/base64" "fmt" + "github.com/portainer/agent/filesystem" "log" "strings" @@ -42,7 +44,7 @@ func (manager *CronManager) Schedule(schedules []agent.Schedule) error { if manager.cronFileExists { log.Println("[DEBUG] [filesystem,cron] [message: no schedules available, removing cron file]") manager.cronFileExists = false - return RemoveFile(fmt.Sprintf("%s%s/%s", agent.HostRoot, cronDirectory, cronFile)) + return filesystem.RemoveFile(fmt.Sprintf("%s%s/%s", agent.HostRoot, cronDirectory, cronFile)) } return nil } @@ -81,7 +83,7 @@ func createCronEntry(schedule *agent.Schedule) (string, error) { return "", err } - err = WriteFile(fmt.Sprintf("%s%s", agent.HostRoot, agent.ScheduleScriptDirectory), fmt.Sprintf("schedule_%d", schedule.ID), []byte(decodedScript), 0744) + err = filesystem.WriteFile(fmt.Sprintf("%s%s", agent.HostRoot, agent.ScheduleScriptDirectory), fmt.Sprintf("schedule_%d", schedule.ID), []byte(decodedScript), 0744) if err != nil { return "", err } @@ -118,7 +120,7 @@ func (manager *CronManager) flushEntries() error { cronEntries = append(cronEntries, "") cronFileContent := strings.Join(cronEntries, "\n") - err := WriteFile(fmt.Sprintf("%s%s", agent.HostRoot, cronDirectory), cronFile, []byte(cronFileContent), 0644) + err := filesystem.WriteFile(fmt.Sprintf("%s%s", agent.HostRoot, cronDirectory), cronFile, []byte(cronFileContent), 0644) if err != nil { return err } diff --git a/filesystem/scheduler_windows.go b/edge/scheduler/scheduler_windows.go similarity index 86% rename from filesystem/scheduler_windows.go rename to edge/scheduler/scheduler_windows.go index 350bba18..01c88976 100644 --- a/filesystem/scheduler_windows.go +++ b/edge/scheduler/scheduler_windows.go @@ -1,6 +1,7 @@ +//go:build windows // +build windows -package filesystem +package scheduler import "github.com/portainer/agent" From be12a1c9bc405c1d1d3140e418e839eacc36b14c Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 17:20:18 +1000 Subject: [PATCH 13/30] git mv edge/logs.go edge/scheduler/logs.go - jobID in logs == schedule.ID Signed-off-by: Sven Dowideit --- edge/edge.go | 7 ++++--- edge/poll.go | 6 +++--- edge/{ => scheduler}/logs.go | 16 ++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) rename edge/{ => scheduler}/logs.go (88%) diff --git a/edge/edge.go b/edge/edge.go index fc544d11..90585b6c 100644 --- a/edge/edge.go +++ b/edge/edge.go @@ -3,6 +3,7 @@ package edge import ( "errors" "fmt" + "github.com/portainer/agent/edge/scheduler" "log" "time" @@ -18,7 +19,7 @@ type ( clusterService agent.ClusterService dockerInfoService agent.DockerInfoService key *edgeKey - logsManager *logsManager + logsManager *scheduler.LogsManager pollService *PollService pollServiceConfig *pollServiceConfig stackManager *StackManager @@ -74,8 +75,8 @@ func (manager *Manager) Start() error { } manager.stackManager = stackManager - manager.logsManager = newLogsManager(manager.key.PortainerInstanceURL, manager.key.EndpointID, manager.agentOptions.EdgeID, pollServiceConfig.InsecurePoll) - manager.logsManager.start() + manager.logsManager = scheduler.NewLogsManager(manager.key.PortainerInstanceURL, manager.key.EndpointID, manager.agentOptions.EdgeID, pollServiceConfig.InsecurePoll) + manager.logsManager.Start() pollService, err := newPollService(manager.stackManager, manager.logsManager, pollServiceConfig) if err != nil { diff --git a/edge/poll.go b/edge/poll.go index adb85d40..4b8c2a6a 100644 --- a/edge/poll.go +++ b/edge/poll.go @@ -38,7 +38,7 @@ type PollService struct { endpointID string tunnelServerAddr string tunnelServerFingerprint string - logsManager *logsManager + logsManager *scheduler.LogsManager containerPlatform agent.ContainerPlatform } @@ -56,7 +56,7 @@ type pollServiceConfig struct { } // newPollService returns a pointer to a new instance of PollService -func newPollService(edgeStackManager *StackManager, logsManager *logsManager, config *pollServiceConfig) (*PollService, error) { +func newPollService(edgeStackManager *StackManager, logsManager *scheduler.LogsManager, config *pollServiceConfig) (*PollService, error) { pollFrequency, err := time.ParseDuration(config.PollFrequency) if err != nil { return nil, err @@ -293,7 +293,7 @@ func (service *PollService) poll() error { } } - service.logsManager.handleReceivedLogsRequests(logsToCollect) + service.logsManager.HandleReceivedLogsRequests(logsToCollect) if responseData.CheckinInterval != service.pollIntervalInSeconds { log.Printf("[DEBUG] [internal,edge,poll] [old_interval: %f] [new_interval: %f] [message: updating poll interval]", service.pollIntervalInSeconds, responseData.CheckinInterval) diff --git a/edge/logs.go b/edge/scheduler/logs.go similarity index 88% rename from edge/logs.go rename to edge/scheduler/logs.go index d0c2220a..31b8f432 100644 --- a/edge/logs.go +++ b/edge/scheduler/logs.go @@ -1,4 +1,4 @@ -package edge +package scheduler import ( "fmt" @@ -10,7 +10,7 @@ import ( "github.com/portainer/agent/http/client" ) -type logsManager struct { +type LogsManager struct { httpClient *client.PortainerClient stopSignal chan struct{} jobs map[int]logStatus @@ -25,17 +25,17 @@ const ( logFailed ) -func newLogsManager(portainerURL, endpointID, edgeID string, insecurePoll bool) *logsManager { +func NewLogsManager(portainerURL, endpointID, edgeID string, insecurePoll bool) *LogsManager { cli := client.NewPortainerClient(portainerURL, endpointID, edgeID, insecurePoll) - return &logsManager{ + return &LogsManager{ httpClient: cli, stopSignal: nil, jobs: map[int]logStatus{}, } } -func (manager *logsManager) start() error { +func (manager *LogsManager) Start() error { if manager.stopSignal != nil { return nil } @@ -100,7 +100,7 @@ func (manager *logsManager) start() error { return nil } -func (manager *logsManager) stop() { +func (manager *LogsManager) stop() { if manager.stopSignal != nil { log.Printf("[DEBUG] [internal,edge,logs] [message: logs manager stopped]") close(manager.stopSignal) @@ -108,7 +108,7 @@ func (manager *logsManager) stop() { } } -func (manager *logsManager) handleReceivedLogsRequests(jobs []int) { +func (manager *LogsManager) HandleReceivedLogsRequests(jobs []int) { for _, jobID := range jobs { if _, ok := manager.jobs[jobID]; !ok { log.Printf("[DEBUG] [internal,edge,logs] [job_identifier: %d] [message: added job to queue]", jobID) @@ -117,7 +117,7 @@ func (manager *logsManager) handleReceivedLogsRequests(jobs []int) { } } -func (manager *logsManager) next() int { +func (manager *LogsManager) next() int { for jobID, status := range manager.jobs { if status == logPending { return jobID From 450bdfaaae8d684bcf7a2a1b9c106b3d33ab142f Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 25 Jan 2022 17:33:59 +1000 Subject: [PATCH 14/30] git mv edge/stack.go edge/stack/stack.go Signed-off-by: Sven Dowideit --- edge/edge.go | 19 ++++++++++--------- edge/poll.go | 7 ++++--- edge/{ => stack}/stack.go | 30 +++++++++++++++--------------- 3 files changed, 29 insertions(+), 27 deletions(-) rename edge/{ => stack}/stack.go (91%) diff --git a/edge/edge.go b/edge/edge.go index 90585b6c..29817824 100644 --- a/edge/edge.go +++ b/edge/edge.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/portainer/agent/edge/scheduler" + "github.com/portainer/agent/edge/stack" "log" "time" @@ -22,7 +23,7 @@ type ( logsManager *scheduler.LogsManager pollService *PollService pollServiceConfig *pollServiceConfig - stackManager *StackManager + stackManager *stack.StackManager } // ManagerParameters represents an object used to create a Manager @@ -69,7 +70,7 @@ func (manager *Manager) Start() error { log.Printf("[DEBUG] [internal,edge] [api_addr: %s] [edge_id: %s] [poll_frequency: %s] [inactivity_timeout: %s] [insecure_poll: %t]", pollServiceConfig.APIServerAddr, pollServiceConfig.EdgeID, pollServiceConfig.PollFrequency, pollServiceConfig.InactivityTimeout, pollServiceConfig.InsecurePoll) - stackManager, err := newStackManager(manager.key.PortainerInstanceURL, manager.key.EndpointID, manager.agentOptions.EdgeID, pollServiceConfig.InsecurePoll) + stackManager, err := stack.NewStackManager(manager.key.PortainerInstanceURL, manager.key.EndpointID, manager.agentOptions.EdgeID, pollServiceConfig.InsecurePoll) if err != nil { return err } @@ -138,13 +139,13 @@ func (manager *Manager) startEdgeBackgroundProcessOnKubernetes(runtimeCheckFrequ return } - err = manager.stackManager.setEngineStatus(engineTypeKubernetes) + err = manager.stackManager.SetEngineStatus(stack.EngineTypeKubernetes) if err != nil { log.Printf("[ERROR] [internal,edge,runtime] [message: unable to set engine status] [error: %s]", err) return } - err = manager.stackManager.start() + err = manager.stackManager.Start() if err != nil { log.Printf("[ERROR] [internal,edge,runtime] [message: unable to start stack manager] [error: %s]", err) return @@ -185,9 +186,9 @@ func (manager *Manager) checkDockerRuntimeConfig() error { log.Printf("[DEBUG] [internal,edge,runtime,docker] [message: Docker runtime configuration check] [engine_status: %d] [leader_node: %t]", runtimeConfiguration.DockerConfiguration.EngineStatus, agentRunsOnLeaderNode) if !agentRunsOnSwarm || agentRunsOnLeaderNode { - engineStatus := engineTypeDockerStandalone + engineStatus := stack.EngineTypeDockerStandalone if agentRunsOnSwarm { - engineStatus = engineTypeDockerSwarm + engineStatus = stack.EngineTypeDockerSwarm } err = manager.pollService.start() @@ -195,12 +196,12 @@ func (manager *Manager) checkDockerRuntimeConfig() error { return err } - err = manager.stackManager.setEngineStatus(engineStatus) + err = manager.stackManager.SetEngineStatus(engineStatus) if err != nil { return err } - err = manager.stackManager.start() + err = manager.stackManager.Start() if err != nil { return err } @@ -211,7 +212,7 @@ func (manager *Manager) checkDockerRuntimeConfig() error { return err } - err = manager.stackManager.stop() + err = manager.stackManager.Stop() if err != nil { return err } diff --git a/edge/poll.go b/edge/poll.go index 4b8c2a6a..65d3dc1e 100644 --- a/edge/poll.go +++ b/edge/poll.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "github.com/portainer/agent/edge/scheduler" + "github.com/portainer/agent/edge/stack" "log" "net/http" "strconv" @@ -33,7 +34,7 @@ type PollService struct { scheduleManager agent.Scheduler lastActivity time.Time refreshSignal chan struct{} - edgeStackManager *StackManager + edgeStackManager *stack.StackManager portainerURL string endpointID string tunnelServerAddr string @@ -56,7 +57,7 @@ type pollServiceConfig struct { } // newPollService returns a pointer to a new instance of PollService -func newPollService(edgeStackManager *StackManager, logsManager *scheduler.LogsManager, config *pollServiceConfig) (*PollService, error) { +func newPollService(edgeStackManager *stack.StackManager, logsManager *scheduler.LogsManager, config *pollServiceConfig) (*PollService, error) { pollFrequency, err := time.ParseDuration(config.PollFrequency) if err != nil { return nil, err @@ -308,7 +309,7 @@ func (service *PollService) poll() error { stacks[stack.ID] = stack.Version } - err := service.edgeStackManager.updateStacksStatus(stacks) + err := service.edgeStackManager.UpdateStacksStatus(stacks) if err != nil { log.Printf("[ERROR] [internal,edge,stack] [message: an error occurred during stack management] [error: %s]", err) return err diff --git a/edge/stack.go b/edge/stack/stack.go similarity index 91% rename from edge/stack.go rename to edge/stack/stack.go index 89e258d8..e87dcddd 100644 --- a/edge/stack.go +++ b/edge/stack/stack.go @@ -1,4 +1,4 @@ -package edge +package stack import ( "context" @@ -56,9 +56,9 @@ type engineType int const ( _ engineType = iota - engineTypeDockerStandalone - engineTypeDockerSwarm - engineTypeKubernetes + EngineTypeDockerStandalone + EngineTypeDockerSwarm + EngineTypeKubernetes ) // StackManager represents a service for managing Edge stacks @@ -73,8 +73,8 @@ type StackManager struct { httpClient *client.PortainerClient } -// newStackManager returns a pointer to a new instance of StackManager -func newStackManager(portainerURL, endpointID, edgeID string, insecurePoll bool) (*StackManager, error) { +// NewStackManager returns a pointer to a new instance of StackManager +func NewStackManager(portainerURL, endpointID, edgeID string, insecurePoll bool) (*StackManager, error) { cli := client.NewPortainerClient(portainerURL, endpointID, edgeID, insecurePoll) stackManager := &StackManager{ @@ -86,7 +86,7 @@ func newStackManager(portainerURL, endpointID, edgeID string, insecurePoll bool) return stackManager, nil } -func (manager *StackManager) updateStacksStatus(stacks map[int]int) error { +func (manager *StackManager) UpdateStacksStatus(stacks map[int]int) error { if !manager.isEnabled { return nil } @@ -122,7 +122,7 @@ func (manager *StackManager) updateStacksStatus(stacks map[int]int) error { folder := fmt.Sprintf("%s/%d", agent.EdgeStackFilesPath, stackID) fileName := "docker-compose.yml" - if manager.engineType == engineTypeKubernetes { + if manager.engineType == EngineTypeKubernetes { fileName = fmt.Sprintf("%s.yml", stack.Name) } @@ -155,7 +155,7 @@ func (manager *StackManager) updateStacksStatus(stacks map[int]int) error { return nil } -func (manager *StackManager) stop() error { +func (manager *StackManager) Stop() error { if manager.stopSignal != nil { close(manager.stopSignal) manager.stopSignal = nil @@ -165,7 +165,7 @@ func (manager *StackManager) stop() error { return nil } -func (manager *StackManager) start() error { +func (manager *StackManager) Start() error { if manager.stopSignal != nil { return nil } @@ -218,14 +218,14 @@ func (manager *StackManager) next() *edgeStack { return nil } -func (manager *StackManager) setEngineStatus(engineStatus engineType) error { +func (manager *StackManager) SetEngineStatus(engineStatus engineType) error { if engineStatus == manager.engineType { return nil } manager.engineType = engineStatus - err := manager.stop() + err := manager.Stop() if err != nil { return err } @@ -283,11 +283,11 @@ func (manager *StackManager) deleteStack(ctx context.Context, stack *edgeStack, func buildDeployerService(engineStatus engineType) (agent.Deployer, error) { switch engineStatus { - case engineTypeDockerStandalone: + case EngineTypeDockerStandalone: return exec.NewDockerComposeStackService(agent.DockerBinaryPath) - case engineTypeDockerSwarm: + case EngineTypeDockerSwarm: return exec.NewDockerSwarmStackService(agent.DockerBinaryPath) - case engineTypeKubernetes: + case EngineTypeKubernetes: return exec.NewKubernetesDeployer(agent.DockerBinaryPath), nil } From 09f3b8b8c88320436186135965664828af71ccc7 Mon Sep 17 00:00:00 2001 From: deviantony Date: Sun, 13 Feb 2022 20:07:41 +0000 Subject: [PATCH 15/30] feat(edge): rollback key retrieval changes --- internal/edge/key.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/internal/edge/key.go b/internal/edge/key.go index d35ebd8e..76ec2dc6 100644 --- a/internal/edge/key.go +++ b/internal/edge/key.go @@ -27,19 +27,9 @@ func (manager *Manager) SetKey(key string) error { return err } - // TODO: yeah, we don't know if we got it from the fs, so lets try again :/ - keyRetrieval, _ := retrieveEdgeKeyFromFilesystem() - if keyRetrieval == "" { - // key not previously saved - err = filesystem.WriteFile(agent.DataDirectory, agent.EdgeKeyFile, []byte(key), 0444) - if err != nil { - return err - } - keyRetrieval = key - } - if keyRetrieval != key { - // TODO: ok, I'm not sure if it should die, or delete the file and re-write it. - log.Fatalf("EdgeKey modified from %s to %s", keyRetrieval, key) + err = filesystem.WriteFile(agent.DataDirectory, agent.EdgeKeyFile, []byte(key), 0444) + if err != nil { + return err } manager.key = edgeKey From adb725f6159b2d49bf9a56a25fb8fce348c6fbe1 Mon Sep 17 00:00:00 2001 From: deviantony Date: Sun, 13 Feb 2022 20:10:13 +0000 Subject: [PATCH 16/30] refactor(edge): remove comment --- internal/edge/key.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/edge/key.go b/internal/edge/key.go index 76ec2dc6..89823050 100644 --- a/internal/edge/key.go +++ b/internal/edge/key.go @@ -20,7 +20,6 @@ type edgeKey struct { // SetKey parses and associates an Edge key to the agent. // If the agent is running inside a Swarm cluster, it will also set the "set" flag to specify that a key is set on this agent in the cluster. -// Don't overwrite the file if it exists - this only appears to work, because our container runs as root func (manager *Manager) SetKey(key string) error { edgeKey, err := parseEdgeKey(key) if err != nil { From b7d87a7146de3f6a80da77db81d6a834f3bb095f Mon Sep 17 00:00:00 2001 From: deviantony Date: Sun, 13 Feb 2022 20:12:03 +0000 Subject: [PATCH 17/30] feat(main): rollback changes to ip detection --- cmd/agent/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index c21aa3e7..1327abd6 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -81,8 +81,7 @@ func main() { log.Printf("[WARN] [main,podman] [message: Unable to retrieve local agent IP address, using '%s' instead] [error: %s]", options.AgentServerAddr, err) advertiseAddr = options.AgentServerAddr } else { - log.Printf("[ERROR] [main,docker] [message: Unable to retrieve local agent IP address] [error: %s]", err) - advertiseAddr = options.AgentServerAddr + log.Fatalf("[ERROR] [main,docker] [message: Unable to retrieve local agent IP address] [error: %s]", err) } } From 733ce077f75c8c124085a0b9ba2371f5166d162d Mon Sep 17 00:00:00 2001 From: deviantony Date: Sun, 13 Feb 2022 20:14:14 +0000 Subject: [PATCH 18/30] refactor(options): remove unused options --- os/options.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/os/options.go b/os/options.go index cfbd23b3..d1ec995e 100644 --- a/os/options.go +++ b/os/options.go @@ -13,7 +13,6 @@ const ( EnvKeyClusterAddr = "AGENT_CLUSTER_ADDR" EnvKeyAgentSecret = "AGENT_SECRET" EnvKeyAgentSecurityShutdown = "AGENT_SECRET_TIMEOUT" - //EnvKeyCapHostManagement = "CAP_HOST_MANAGEMENT" // deprecated and unused EnvKeyEdge = "EDGE" EnvKeyEdgeKey = "EDGE_KEY" EnvKeyEdgeID = "EDGE_ID" @@ -23,7 +22,6 @@ const ( EnvKeyEdgeInsecurePoll = "EDGE_INSECURE_POLL" EnvKeyEdgeTunnel = "EDGE_TUNNEL" EnvKeyLogLevel = "LOG_LEVEL" - //EnvKeyDockerBinaryPath = "DOCKER_BINARY_PATH" //unused ) type EnvOptionParser struct{} From 6884e18727f49245fa810d1762093e322f5cc9d7 Mon Sep 17 00:00:00 2001 From: deviantony Date: Sun, 13 Feb 2022 20:14:53 +0000 Subject: [PATCH 19/30] refactor(options): remove comment --- os/options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/options.go b/os/options.go index d1ec995e..35a2f8c9 100644 --- a/os/options.go +++ b/os/options.go @@ -60,7 +60,7 @@ func (parser *EnvOptionParser) Options() (*agent.Options, error) { EdgeMode: *fEdgeMode, EdgeKey: *fEdgeKey, EdgeID: *fEdgeID, - EdgeServerAddr: fEdgeServerAddr.String(), // TODO: really, an agent can't be both edge and non-edge, so we don't need both AgentServerAddr and EdgeServerAddr ? + EdgeServerAddr: fEdgeServerAddr.String(), EdgeServerPort: strconv.Itoa(*fEdgeServerPort), EdgeInactivityTimeout: *fEdgeInactivityTimeout, EdgeInsecurePoll: *fEdgeInsecurePoll, From 3feef7cf33ccc1332889fbfb76c83ca91de9610e Mon Sep 17 00:00:00 2001 From: deviantony Date: Sun, 13 Feb 2022 20:42:21 +0000 Subject: [PATCH 20/30] refactor(http): update parameters --- http/client/portainer_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/client/portainer_client.go b/http/client/portainer_client.go index 726fff78..3a3c684a 100644 --- a/http/client/portainer_client.go +++ b/http/client/portainer_client.go @@ -23,7 +23,7 @@ type PortainerClient struct { } // NewPortainerClient returns a pointer to a new PortainerClient instance -func NewPortainerClient(serverAddress, endpointID, edgeID string, insecurePoll bool, tunnel bool) *PortainerClient { +func NewPortainerClient(serverAddress, endpointID, edgeID string, insecurePoll, tunnel bool) *PortainerClient { httpCli := &http.Client{ Timeout: 10 * time.Second, } From 3f6307fc2c819af3278c1660188037240bc828d7 Mon Sep 17 00:00:00 2001 From: deviantony Date: Wed, 16 Feb 2022 19:55:29 +0000 Subject: [PATCH 21/30] refactor(edge): refactor of the edge package --- cmd/agent/main.go | 1 - edge/edge.go | 8 ++++---- edge/key.go | 51 +++++++++++++++++++---------------------------- 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 9eae0b1e..9f02a267 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -196,7 +196,6 @@ func main() { if edgeKey != "" { log.Println("[DEBUG] [main,edge] [message: Edge key found in environment. Associating Edge key]") - // TODO: this fails when RetreiveEdgeKey came from an existing file, as mode 0444 can't be written to - unless you're root... err := edgeManager.SetKey(edgeKey) if err != nil { log.Fatalf("[ERROR] [main,edge] [message: Unable to associate Edge key] [error: %s]", err) diff --git a/edge/edge.go b/edge/edge.go index c278404e..0495ca43 100644 --- a/edge/edge.go +++ b/edge/edge.go @@ -3,11 +3,12 @@ package edge import ( "errors" "fmt" - "github.com/portainer/agent/edge/scheduler" - "github.com/portainer/agent/edge/stack" "log" "time" + "github.com/portainer/agent/edge/scheduler" + "github.com/portainer/agent/edge/stack" + "github.com/portainer/agent" ) @@ -22,7 +23,6 @@ type ( key *edgeKey logsManager *scheduler.LogsManager pollService *PollService - pollServiceConfig *pollServiceConfig stackManager *stack.StackManager } @@ -50,7 +50,7 @@ func NewManager(parameters *ManagerParameters) *Manager { // Start starts the manager func (manager *Manager) Start() error { if !manager.IsKeySet() { - return errors.New("Unable to start Edge manager without key") + return errors.New("unable to start Edge manager without key") } apiServerAddr := fmt.Sprintf("%s:%s", manager.advertiseAddr, manager.agentOptions.AgentServerPort) diff --git a/edge/key.go b/edge/key.go index 5d6e42d8..7fb8c7e4 100644 --- a/edge/key.go +++ b/edge/key.go @@ -20,7 +20,7 @@ type edgeKey struct { } // SetKey parses and associates an Edge key to the agent. -// If the agent is running inside a Swarm cluster, it will also set the "set" flag to specify that a key is set on this agent in the cluster. +// If the agent is running inside a cluster, it will also set the "set" flag to specify that a key is set on this agent in the cluster. func (manager *Manager) SetKey(key string) error { edgeKey, err := parseEdgeKey(key) if err != nil { @@ -59,10 +59,7 @@ func (manager *Manager) GetKey() string { // IsKeySet returns true if an Edge key is associated to the agent func (manager *Manager) IsKeySet() bool { - if manager.key == nil { - return false - } - return true + return manager.key != nil } // PropagateKeyInCluster propagates the Edge key associated to the agent to all the other agents inside the cluster @@ -95,7 +92,6 @@ func (manager *Manager) PropagateKeyInCluster() error { // parseEdgeKey decodes a base64 encoded key and extract the decoded information from the following // format: ||| -// are expected in the user:password format func parseEdgeKey(key string) (*edgeKey, error) { decodedKey, err := base64.RawStdEncoding.DecodeString(key) if err != nil { @@ -125,7 +121,6 @@ func encodeKey(edgeKey *edgeKey) string { } func RetrieveEdgeKey(edgeKey string, clusterService agent.ClusterService) (string, error) { - if edgeKey != "" { log.Println("[INFO] [main,edge] [message: Edge key loaded from options]") return edgeKey, nil @@ -149,8 +144,6 @@ func RetrieveEdgeKey(edgeKey string, clusterService agent.ClusterService) (strin } func retrieveEdgeKeyFromFilesystem() (string, error) { - var edgeKey string - edgeKeyFilePath := fmt.Sprintf("%s/%s", agent.DataDirectory, agent.EdgeKeyFile) keyFileExists, err := filesystem.FileExists(edgeKeyFilePath) @@ -158,36 +151,34 @@ func retrieveEdgeKeyFromFilesystem() (string, error) { return "", err } - if keyFileExists { - filesystemKey, err := filesystem.ReadFromFile(edgeKeyFilePath) - if err != nil { - return "", err - } + if !keyFileExists { + return "", nil + } - log.Println("[INFO] [main,edge] [message: Edge key loaded from the filesystem]") - edgeKey = string(filesystemKey) + filesystemKey, err := filesystem.ReadFromFile(edgeKeyFilePath) + if err != nil { + return "", err } - return edgeKey, nil + log.Println("[INFO] [main,edge] [message: Edge key loaded from the filesystem]") + return string(filesystemKey), nil } func retrieveEdgeKeyFromCluster(clusterService agent.ClusterService) (string, error) { - var edgeKey string - member := clusterService.GetMemberWithEdgeKeySet() - if member != nil { - httpCli := client.NewAPIClient() + if member == nil { + return "", nil + } - memberAddr := fmt.Sprintf("%s:%s", member.IPAddress, member.Port) - memberKey, err := httpCli.GetEdgeKey(memberAddr) - if err != nil { - log.Printf("[ERROR] [main,edge,http,cluster] [message: Unable to retrieve Edge key from cluster member] [error: %s]", err) - return "", err - } + httpCli := client.NewAPIClient() - log.Println("[INFO] [main,edge] [message: Edge key loaded from cluster]") - edgeKey = memberKey + memberAddr := fmt.Sprintf("%s:%s", member.IPAddress, member.Port) + memberKey, err := httpCli.GetEdgeKey(memberAddr) + if err != nil { + log.Printf("[ERROR] [main,edge,http,cluster] [message: Unable to retrieve Edge key from cluster member] [error: %s]", err) + return "", err } - return edgeKey, nil + log.Println("[INFO] [main,edge] [message: Edge key loaded from cluster]") + return memberKey, nil } From 6f57deeee4990303f0543799c0466f2942e2892b Mon Sep 17 00:00:00 2001 From: deviantony Date: Wed, 16 Feb 2022 19:56:24 +0000 Subject: [PATCH 22/30] refactor(edge): refactor of the edge package --- edge/poll.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/edge/poll.go b/edge/poll.go index 53b8d9dd..c55f15ac 100644 --- a/edge/poll.go +++ b/edge/poll.go @@ -6,8 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/portainer/agent/edge/scheduler" - "github.com/portainer/agent/edge/stack" "log" "net/http" "strconv" @@ -15,6 +13,8 @@ import ( "github.com/portainer/agent" "github.com/portainer/agent/chisel" + "github.com/portainer/agent/edge/scheduler" + "github.com/portainer/agent/edge/stack" "github.com/portainer/libcrypto" ) @@ -276,9 +276,9 @@ func (service *PollService) poll() error { } if service.tunnel == true { - if responseData.Status == "REQUIRED" && !service.tunnelClient.IsTunnelOpen(){ + if responseData.Status == "REQUIRED" && !service.tunnelClient.IsTunnelOpen() { log.Println("[DEBUG] [internal,edge,poll] [message: Required status detected, creating reverse tunnel]") - + err := service.createTunnel(responseData.Credentials, responseData.Port) if err != nil { log.Printf("[ERROR] [internal,edge,poll] [message: Unable to create tunnel] [error: %s]", err) From cf8b3d592423882b849fbcbe0b5dc045ea8ce5e1 Mon Sep 17 00:00:00 2001 From: deviantony Date: Wed, 16 Feb 2022 20:51:43 +0000 Subject: [PATCH 23/30] refactor(edge): import refactor --- edge/scheduler/scheduler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edge/scheduler/scheduler.go b/edge/scheduler/scheduler.go index f65df352..01a101ba 100644 --- a/edge/scheduler/scheduler.go +++ b/edge/scheduler/scheduler.go @@ -6,11 +6,11 @@ package scheduler import ( "encoding/base64" "fmt" - "github.com/portainer/agent/filesystem" "log" "strings" "github.com/portainer/agent" + "github.com/portainer/agent/filesystem" ) const ( From 2efeba12e93e6b22785e8cb7c43246d9392c43ae Mon Sep 17 00:00:00 2001 From: deviantony Date: Wed, 16 Feb 2022 20:59:29 +0000 Subject: [PATCH 24/30] refactor(edge): refactor of the edge module --- edge/poll.go | 6 +----- edge/stack/stack.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/edge/poll.go b/edge/poll.go index c55f15ac..1a7b1a10 100644 --- a/edge/poll.go +++ b/edge/poll.go @@ -90,10 +90,6 @@ func newPollService(edgeStackManager *stack.StackManager, logsManager *scheduler }, nil } -func (service *PollService) closeTunnel() error { - return service.tunnelClient.CloseTunnel() -} - func (service *PollService) resetActivityTimer() { if service.tunnelClient.IsTunnelOpen() { service.lastActivity = time.Now() @@ -275,7 +271,7 @@ func (service *PollService) poll() error { } } - if service.tunnel == true { + if service.tunnel { if responseData.Status == "REQUIRED" && !service.tunnelClient.IsTunnelOpen() { log.Println("[DEBUG] [internal,edge,poll] [message: Required status detected, creating reverse tunnel]") diff --git a/edge/stack/stack.go b/edge/stack/stack.go index c3d38789..7f3bc603 100644 --- a/edge/stack/stack.go +++ b/edge/stack/stack.go @@ -55,6 +55,8 @@ const ( type engineType int const ( + // TODO: consider defining this in agent.go or re-use/enhance some of the existing constants + // that are declared in agent.go _ engineType = iota EngineTypeDockerStandalone EngineTypeDockerSwarm @@ -63,14 +65,12 @@ const ( // StackManager represents a service for managing Edge stacks type StackManager struct { - engineType engineType - stacks map[edgeStackID]*edgeStack - stopSignal chan struct{} - deployer agent.Deployer - portainerURL string - endpointID string - isEnabled bool - httpClient *client.PortainerClient + engineType engineType + stacks map[edgeStackID]*edgeStack + stopSignal chan struct{} + deployer agent.Deployer + isEnabled bool + httpClient *client.PortainerClient } // NewStackManager returns a pointer to a new instance of StackManager From a074820c579eea72162b2cee0f2679b64a438ab3 Mon Sep 17 00:00:00 2001 From: deviantony Date: Wed, 16 Feb 2022 21:21:40 +0000 Subject: [PATCH 25/30] refactor(edge): refactor imports --- edge/edge.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/edge/edge.go b/edge/edge.go index 0495ca43..f0eab270 100644 --- a/edge/edge.go +++ b/edge/edge.go @@ -6,10 +6,9 @@ import ( "log" "time" + "github.com/portainer/agent" "github.com/portainer/agent/edge/scheduler" "github.com/portainer/agent/edge/stack" - - "github.com/portainer/agent" ) type ( From a0a1be9d74ef14cbe3202b46c8a67870f225d794 Mon Sep 17 00:00:00 2001 From: deviantony Date: Wed, 16 Feb 2022 21:22:01 +0000 Subject: [PATCH 26/30] refactor(http): do not store pointer to EdgeManager in Handler --- http/handler/handler.go | 15 --------------- http/server.go | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/http/handler/handler.go b/http/handler/handler.go index 7074e579..75d5b519 100644 --- a/http/handler/handler.go +++ b/http/handler/handler.go @@ -1,7 +1,6 @@ package handler import ( - "errors" "net/http" "regexp" "strconv" @@ -23,7 +22,6 @@ import ( "github.com/portainer/agent/http/proxy" "github.com/portainer/agent/http/security" kubecli "github.com/portainer/agent/kubernetes" - httperror "github.com/portainer/libhttp/error" ) // Handler is the main handler of the application. @@ -40,8 +38,6 @@ type Handler struct { webSocketHandler *websocket.Handler hostHandler *host.Handler pingHandler *ping.Handler - securedProtocol bool - edgeManager *edge.Manager // TODO: I suspect we should not store this here containerPlatform agent.ContainerPlatform } @@ -79,8 +75,6 @@ func NewHandler(config *Config) *Handler { webSocketHandler: websocket.NewHandler(config.ClusterService, config.RuntimeConfiguration, notaryService, config.KubeClient), hostHandler: host.NewHandler(config.SystemService, agentProxy, notaryService), pingHandler: ping.NewHandler(), - securedProtocol: config.Secured, - edgeManager: config.EdgeManager, containerPlatform: config.ContainerPlatform, } } @@ -91,15 +85,6 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, request *http.Request) { return } - if !h.securedProtocol && !(h.edgeManager != nil && h.edgeManager.IsKeySet()) { - httperror.WriteError(rw, http.StatusForbidden, "Unable to use the unsecured agent API without Edge key", errors.New("edge key not set")) - return - } - - if h.edgeManager != nil { - h.edgeManager.ResetActivityTimer() - } - request.URL.Path = dockerAPIVersionRegexp.ReplaceAllString(request.URL.Path, "") rw.Header().Set(agent.HTTPResponseAgentHeaderName, agent.Version) rw.Header().Set(agent.HTTPResponseAgentApiVersion, agent.APIVersion) diff --git a/http/server.go b/http/server.go index a371b0ec..73e93dc1 100644 --- a/http/server.go +++ b/http/server.go @@ -3,6 +3,7 @@ package http import ( "context" "crypto/tls" + "errors" "log" "net/http" "time" @@ -12,6 +13,7 @@ import ( "github.com/portainer/agent/exec" "github.com/portainer/agent/http/handler" "github.com/portainer/agent/kubernetes" + httperror "github.com/portainer/libhttp/error" ) // APIServer is the web server exposing the API of an agent. @@ -62,6 +64,30 @@ func NewAPIServer(config *APIServerConfig) *APIServer { } } +func (server *APIServer) serveSecuredEdgeAPI(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if server.edgeManager != nil { + server.edgeManager.ResetActivityTimer() + } + next.ServeHTTP(w, r) + }) +} + +func (server *APIServer) serveUnsecuredEdgeAPI(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if server.edgeManager != nil && !server.edgeManager.IsKeySet() { + httperror.WriteError(w, http.StatusForbidden, "Unable to use the unsecured agent API without Edge key", errors.New("edge key not set")) + return + } + + if server.edgeManager != nil { + server.edgeManager.ResetActivityTimer() + } + + next.ServeHTTP(w, r) + }) +} + // Start starts a new web server by listening on the specified listenAddr. func (server *APIServer) StartUnsecured() error { config := &handler.Config{ @@ -71,9 +97,9 @@ func (server *APIServer) StartUnsecured() error { RuntimeConfiguration: server.agentTags, AgentOptions: server.agentOptions, EdgeManager: server.edgeManager, - Secured: false, KubeClient: server.kubeClient, KubernetesDeployer: server.kubernetesDeployer, + Secured: false, ContainerPlatform: server.containerPlatform, } @@ -84,7 +110,7 @@ func (server *APIServer) StartUnsecured() error { httpServer := &http.Server{ Addr: listenAddr, - Handler: h, + Handler: server.serveUnsecuredEdgeAPI(h), ReadTimeout: 120 * time.Second, WriteTimeout: 30 * time.Minute, } @@ -101,9 +127,9 @@ func (server *APIServer) StartSecured() error { RuntimeConfiguration: server.agentTags, AgentOptions: server.agentOptions, EdgeManager: server.edgeManager, - Secured: true, KubeClient: server.kubeClient, KubernetesDeployer: server.kubernetesDeployer, + Secured: true, ContainerPlatform: server.containerPlatform, } @@ -129,7 +155,7 @@ func (server *APIServer) StartSecured() error { httpServer := &http.Server{ Addr: listenAddr, - Handler: h, + Handler: server.serveSecuredEdgeAPI(h), ReadTimeout: 120 * time.Second, TLSConfig: tlsConfig, WriteTimeout: 30 * time.Minute, From 9026f316fb99fb3864f32480a039964551c68e71 Mon Sep 17 00:00:00 2001 From: deviantony Date: Thu, 17 Feb 2022 03:54:06 +0000 Subject: [PATCH 27/30] refactor(edge): refactor edge tunnel capability mgmt --- edge/edge.go | 8 ++++---- edge/poll.go | 34 ++++++++++++++++++--------------- edge/scheduler/logs.go | 4 ++-- edge/stack/stack.go | 4 ++-- http/client/portainer_client.go | 2 +- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/edge/edge.go b/edge/edge.go index f0eab270..7ecbc77b 100644 --- a/edge/edge.go +++ b/edge/edge.go @@ -60,7 +60,7 @@ func (manager *Manager) Start() error { PollFrequency: agent.DefaultEdgePollInterval, InactivityTimeout: manager.agentOptions.EdgeInactivityTimeout, InsecurePoll: manager.agentOptions.EdgeInsecurePoll, - Tunnel: manager.agentOptions.EdgeTunnel, + TunneCapability: manager.agentOptions.EdgeTunnel, PortainerURL: manager.key.PortainerInstanceURL, EndpointID: manager.key.EndpointID, TunnelServerAddr: manager.key.TunnelServerAddr, @@ -68,15 +68,15 @@ func (manager *Manager) Start() error { ContainerPlatform: manager.containerPlatform, } - log.Printf("[DEBUG] [internal,edge] [api_addr: %s] [edge_id: %s] [poll_frequency: %s] [inactivity_timeout: %s] [insecure_poll: %t] [tunnel: %t]", pollServiceConfig.APIServerAddr, pollServiceConfig.EdgeID, pollServiceConfig.PollFrequency, pollServiceConfig.InactivityTimeout, pollServiceConfig.InsecurePoll, pollServiceConfig.Tunnel) + log.Printf("[DEBUG] [internal,edge] [api_addr: %s] [edge_id: %s] [poll_frequency: %s] [inactivity_timeout: %s] [insecure_poll: %t] [tunnel_capability: %t]", pollServiceConfig.APIServerAddr, pollServiceConfig.EdgeID, pollServiceConfig.PollFrequency, pollServiceConfig.InactivityTimeout, pollServiceConfig.InsecurePoll, manager.agentOptions.EdgeTunnel) - stackManager, err := stack.NewStackManager(manager.key.PortainerInstanceURL, manager.key.EndpointID, manager.agentOptions.EdgeID, pollServiceConfig.InsecurePoll, pollServiceConfig.Tunnel) + stackManager, err := stack.NewStackManager(manager.key.PortainerInstanceURL, manager.key.EndpointID, manager.agentOptions.EdgeID, pollServiceConfig.InsecurePoll) if err != nil { return err } manager.stackManager = stackManager - manager.logsManager = scheduler.NewLogsManager(manager.key.PortainerInstanceURL, manager.key.EndpointID, manager.agentOptions.EdgeID, pollServiceConfig.InsecurePoll, pollServiceConfig.Tunnel) + manager.logsManager = scheduler.NewLogsManager(manager.key.PortainerInstanceURL, manager.key.EndpointID, manager.agentOptions.EdgeID, pollServiceConfig.InsecurePoll) manager.logsManager.Start() pollService, err := newPollService(manager.stackManager, manager.logsManager, pollServiceConfig) diff --git a/edge/poll.go b/edge/poll.go index 1a7b1a10..060dcf72 100644 --- a/edge/poll.go +++ b/edge/poll.go @@ -27,7 +27,6 @@ type PollService struct { apiServerAddr string pollIntervalInSeconds float64 insecurePoll bool - tunnel bool inactivityTimeout time.Duration edgeID string httpClient *http.Client @@ -50,7 +49,7 @@ type pollServiceConfig struct { InactivityTimeout string PollFrequency string InsecurePoll bool - Tunnel bool + TunneCapability bool PortainerURL string EndpointID string TunnelServerAddr string @@ -59,6 +58,7 @@ type pollServiceConfig struct { } // newPollService returns a pointer to a new instance of PollService +// If TunneCapability is disabled, it will only poll for Edge stacks and schedule without managing reverse tunnels. func newPollService(edgeStackManager *stack.StackManager, logsManager *scheduler.LogsManager, config *pollServiceConfig) (*PollService, error) { pollFrequency, err := time.ParseDuration(config.PollFrequency) if err != nil { @@ -70,14 +70,12 @@ func newPollService(edgeStackManager *stack.StackManager, logsManager *scheduler return nil, err } - return &PollService{ + pollService := &PollService{ apiServerAddr: config.APIServerAddr, edgeID: config.EdgeID, pollIntervalInSeconds: pollFrequency.Seconds(), insecurePoll: config.InsecurePoll, - tunnel: config.Tunnel, inactivityTimeout: inactivityTimeout, - tunnelClient: chisel.NewClient(), scheduleManager: scheduler.NewCronManager(), refreshSignal: nil, edgeStackManager: edgeStackManager, @@ -87,11 +85,17 @@ func newPollService(edgeStackManager *stack.StackManager, logsManager *scheduler tunnelServerFingerprint: config.TunnelServerFingerprint, logsManager: logsManager, containerPlatform: config.ContainerPlatform, - }, nil + } + + if config.TunneCapability { + pollService.tunnelClient = chisel.NewClient() + } + + return pollService, nil } func (service *PollService) resetActivityTimer() { - if service.tunnelClient.IsTunnelOpen() { + if service.tunnelClient != nil && service.tunnelClient.IsTunnelOpen() { service.lastActivity = time.Now() } } @@ -169,7 +173,7 @@ func (service *PollService) startActivityMonitoringLoop() { elapsed := time.Since(service.lastActivity) log.Printf("[DEBUG] [internal,edge,monitoring] [tunnel_last_activity_seconds: %f] [message: tunnel activity monitoring]", elapsed.Seconds()) - if service.tunnelClient.IsTunnelOpen() && elapsed.Seconds() > service.inactivityTimeout.Seconds() { + if service.tunnelClient != nil && service.tunnelClient.IsTunnelOpen() && elapsed.Seconds() > service.inactivityTimeout.Seconds() { log.Printf("[INFO] [internal,edge,monitoring] [tunnel_last_activity_seconds: %f] [message: shutting down tunnel after inactivity period]", elapsed.Seconds()) @@ -262,16 +266,16 @@ func (service *PollService) poll() error { log.Printf("[DEBUG] [internal,edge,poll] [status: %s] [port: %d] [schedule_count: %d] [checkin_interval_seconds: %f]", responseData.Status, responseData.Port, len(responseData.Schedules), responseData.CheckinInterval) - if responseData.Status == "IDLE" && service.tunnelClient.IsTunnelOpen() { - log.Printf("[DEBUG] [internal,edge,poll] [status: %s] [message: Idle status detected, shutting down tunnel]", responseData.Status) + if service.tunnelClient != nil { + if responseData.Status == "IDLE" && service.tunnelClient.IsTunnelOpen() { + log.Printf("[DEBUG] [internal,edge,poll] [status: %s] [message: Idle status detected, shutting down tunnel]", responseData.Status) - err := service.tunnelClient.CloseTunnel() - if err != nil { - log.Printf("[ERROR] [internal,edge,poll] [message: Unable to shutdown tunnel] [error: %s]", err) + err := service.tunnelClient.CloseTunnel() + if err != nil { + log.Printf("[ERROR] [internal,edge,poll] [message: Unable to shutdown tunnel] [error: %s]", err) + } } - } - if service.tunnel { if responseData.Status == "REQUIRED" && !service.tunnelClient.IsTunnelOpen() { log.Println("[DEBUG] [internal,edge,poll] [message: Required status detected, creating reverse tunnel]") diff --git a/edge/scheduler/logs.go b/edge/scheduler/logs.go index 4f041d5a..31b8f432 100644 --- a/edge/scheduler/logs.go +++ b/edge/scheduler/logs.go @@ -25,8 +25,8 @@ const ( logFailed ) -func NewLogsManager(portainerURL, endpointID, edgeID string, insecurePoll, tunnel bool) *LogsManager { - cli := client.NewPortainerClient(portainerURL, endpointID, edgeID, insecurePoll, tunnel) +func NewLogsManager(portainerURL, endpointID, edgeID string, insecurePoll bool) *LogsManager { + cli := client.NewPortainerClient(portainerURL, endpointID, edgeID, insecurePoll) return &LogsManager{ httpClient: cli, diff --git a/edge/stack/stack.go b/edge/stack/stack.go index 7f3bc603..698d3313 100644 --- a/edge/stack/stack.go +++ b/edge/stack/stack.go @@ -74,8 +74,8 @@ type StackManager struct { } // NewStackManager returns a pointer to a new instance of StackManager -func NewStackManager(portainerURL, endpointID, edgeID string, insecurePoll, tunnel bool) (*StackManager, error) { - cli := client.NewPortainerClient(portainerURL, endpointID, edgeID, insecurePoll, tunnel) +func NewStackManager(portainerURL, endpointID, edgeID string, insecurePoll bool) (*StackManager, error) { + cli := client.NewPortainerClient(portainerURL, endpointID, edgeID, insecurePoll) stackManager := &StackManager{ stacks: map[edgeStackID]*edgeStack{}, diff --git a/http/client/portainer_client.go b/http/client/portainer_client.go index 3a3c684a..658ee782 100644 --- a/http/client/portainer_client.go +++ b/http/client/portainer_client.go @@ -23,7 +23,7 @@ type PortainerClient struct { } // NewPortainerClient returns a pointer to a new PortainerClient instance -func NewPortainerClient(serverAddress, endpointID, edgeID string, insecurePoll, tunnel bool) *PortainerClient { +func NewPortainerClient(serverAddress, endpointID, edgeID string, insecurePoll bool) *PortainerClient { httpCli := &http.Client{ Timeout: 10 * time.Second, } From 67dad55b22b1855c91fee0903c01d779fd58fdf3 Mon Sep 17 00:00:00 2001 From: deviantony Date: Thu, 17 Feb 2022 18:00:52 +0000 Subject: [PATCH 28/30] refactor(edge): rename parameter --- edge/edge.go | 2 +- edge/poll.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/edge/edge.go b/edge/edge.go index 7ecbc77b..b52dc4af 100644 --- a/edge/edge.go +++ b/edge/edge.go @@ -60,7 +60,7 @@ func (manager *Manager) Start() error { PollFrequency: agent.DefaultEdgePollInterval, InactivityTimeout: manager.agentOptions.EdgeInactivityTimeout, InsecurePoll: manager.agentOptions.EdgeInsecurePoll, - TunneCapability: manager.agentOptions.EdgeTunnel, + TunnelCapability: manager.agentOptions.EdgeTunnel, PortainerURL: manager.key.PortainerInstanceURL, EndpointID: manager.key.EndpointID, TunnelServerAddr: manager.key.TunnelServerAddr, diff --git a/edge/poll.go b/edge/poll.go index 060dcf72..356e8eea 100644 --- a/edge/poll.go +++ b/edge/poll.go @@ -49,7 +49,7 @@ type pollServiceConfig struct { InactivityTimeout string PollFrequency string InsecurePoll bool - TunneCapability bool + TunnelCapability bool PortainerURL string EndpointID string TunnelServerAddr string @@ -87,7 +87,7 @@ func newPollService(edgeStackManager *stack.StackManager, logsManager *scheduler containerPlatform: config.ContainerPlatform, } - if config.TunneCapability { + if config.TunnelCapability { pollService.tunnelClient = chisel.NewClient() } From 7727e3f78f4ada4e77c3b1f6f827a97a93d365db Mon Sep 17 00:00:00 2001 From: deviantony Date: Thu, 17 Feb 2022 18:08:10 +0000 Subject: [PATCH 29/30] refactor(main): update Edge condition check --- cmd/agent/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 9f02a267..d1949ee4 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -235,7 +235,7 @@ func main() { config.Addr = advertiseAddr } - err = startAPIServer(config) + err = startAPIServer(config, options.EdgeMode) if err != nil && !errors.Is(err, gohttp.ErrServerClosed) { log.Fatalf("[ERROR] [main,http] [message: Unable to start Agent API server] [error: %s]", err) } @@ -249,10 +249,10 @@ func main() { fmt.Printf("[DEBUG] [main] [message: shutting down] [signal: %s]", s) } -func startAPIServer(config *http.APIServerConfig) error { +func startAPIServer(config *http.APIServerConfig, edgeMode bool) error { server := http.NewAPIServer(config) - if config.EdgeManager != nil { + if edgeMode { return server.StartUnsecured() } From eb860ac2ca466537bb62c636902137cdb0bb1788 Mon Sep 17 00:00:00 2001 From: deviantony Date: Thu, 17 Feb 2022 20:30:30 +0000 Subject: [PATCH 30/30] refactor(http): review Edge mode server's handler enhancement --- cmd/agent/main.go | 4 ++-- http/server.go | 37 +++++++++++++++++-------------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index d1949ee4..c67aeff2 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -253,10 +253,10 @@ func startAPIServer(config *http.APIServerConfig, edgeMode bool) error { server := http.NewAPIServer(config) if edgeMode { - return server.StartUnsecured() + return server.StartUnsecured(edgeMode) } - return server.StartSecured() + return server.StartSecured(edgeMode) } func parseOptions() (*agent.Options, error) { diff --git a/http/server.go b/http/server.go index 73e93dc1..2066e6a8 100644 --- a/http/server.go +++ b/http/server.go @@ -64,32 +64,21 @@ func NewAPIServer(config *APIServerConfig) *APIServer { } } -func (server *APIServer) serveSecuredEdgeAPI(next http.Handler) http.Handler { +func (server *APIServer) enhanceAPIForEdgeMode(next http.Handler, isSecure bool) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if server.edgeManager != nil { - server.edgeManager.ResetActivityTimer() - } - next.ServeHTTP(w, r) - }) -} - -func (server *APIServer) serveUnsecuredEdgeAPI(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if server.edgeManager != nil && !server.edgeManager.IsKeySet() { + if !isSecure && !server.edgeManager.IsKeySet() { httperror.WriteError(w, http.StatusForbidden, "Unable to use the unsecured agent API without Edge key", errors.New("edge key not set")) return } - if server.edgeManager != nil { - server.edgeManager.ResetActivityTimer() - } + server.edgeManager.ResetActivityTimer() next.ServeHTTP(w, r) }) } // Start starts a new web server by listening on the specified listenAddr. -func (server *APIServer) StartUnsecured() error { +func (server *APIServer) StartUnsecured(edgeMode bool) error { config := &handler.Config{ SystemService: server.systemService, ClusterService: server.clusterService, @@ -103,14 +92,18 @@ func (server *APIServer) StartUnsecured() error { ContainerPlatform: server.containerPlatform, } - h := handler.NewHandler(config) + var h http.Handler = handler.NewHandler(config) listenAddr := server.addr + ":" + server.port + if edgeMode { + h = server.enhanceAPIForEdgeMode(h, false) + } + log.Printf("[INFO] [http] [server_addr: %s] [server_port: %s] [secured: %t] [api_version: %s] [message: Starting Agent API server]", server.addr, server.port, config.Secured, agent.Version) httpServer := &http.Server{ Addr: listenAddr, - Handler: server.serveUnsecuredEdgeAPI(h), + Handler: h, ReadTimeout: 120 * time.Second, WriteTimeout: 30 * time.Minute, } @@ -119,7 +112,7 @@ func (server *APIServer) StartUnsecured() error { } // Start starts a new web server by listening on the specified listenAddr. -func (server *APIServer) StartSecured() error { +func (server *APIServer) StartSecured(edgeMode bool) error { config := &handler.Config{ SystemService: server.systemService, ClusterService: server.clusterService, @@ -133,9 +126,13 @@ func (server *APIServer) StartSecured() error { ContainerPlatform: server.containerPlatform, } - h := handler.NewHandler(config) + var h http.Handler = handler.NewHandler(config) listenAddr := server.addr + ":" + server.port + if edgeMode { + h = server.enhanceAPIForEdgeMode(h, true) + } + log.Printf("[INFO] [http] [server_addr: %s] [server_port: %s] [secured: %t] [api_version: %s] [message: Starting Agent API server]", server.addr, server.port, config.Secured, agent.Version) tlsConfig := &tls.Config{ @@ -155,7 +152,7 @@ func (server *APIServer) StartSecured() error { httpServer := &http.Server{ Addr: listenAddr, - Handler: server.serveSecuredEdgeAPI(h), + Handler: h, ReadTimeout: 120 * time.Second, TLSConfig: tlsConfig, WriteTimeout: 30 * time.Minute,