Skip to content

Commit

Permalink
MEN-5216: Local HTTP proxy for Mender server API calls
Browse files Browse the repository at this point in the history
Changelog: Mender starts a local HTTP server that will proxy incoming
requests to `/api/devices` to the currently authenticated Mender server.
The existing D-Bus API endpoints GetJwtToken and JwtTokenStateChange
will now return the local address together with the JWT token. Supports
also websocket upgrade when calling
`/api/devices/v1/deviceconnect/connect` endpoint

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>
  • Loading branch information
lluiscampos committed Dec 3, 2021
1 parent 3bb7b09 commit 84204a3
Show file tree
Hide file tree
Showing 38 changed files with 5,145 additions and 43 deletions.
1 change: 1 addition & 0 deletions LIC_FILES_CHKSUM.sha256
Expand Up @@ -8,6 +8,7 @@ cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 vendor/github.
# BSD 2 Clause license.
8d427fd87bc9579ea368fde3d49f9ca22eac857f91a9dec7e3004bdfab7dee86 vendor/github.com/pkg/errors/LICENSE
e4646a82a976369d7ae8f6ed5c11d35dc0af18433a8ccc24c85b459ad8b95128 vendor/github.com/godbus/dbus/LICENSE
2be1b548b0387ca8948e1bb9434e709126904d15f622cc2d0d8e7f186e4d122d vendor/github.com/gorilla/websocket/LICENSE
#
# BSD 3 Clause license.
16f848582e4b276a7392cd34496b7a33d6f65c0e190c163ff3a056a7c61219ce vendor/github.com/klauspost/compress/LICENSE
Expand Down
55 changes: 46 additions & 9 deletions app/auth.go
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"

"github.com/mendersoftware/mender/app/proxy"
"github.com/mendersoftware/mender/client"
"github.com/mendersoftware/mender/conf"
"github.com/mendersoftware/mender/datastore"
Expand Down Expand Up @@ -144,6 +145,8 @@ type menderAuthManagerService struct {
idSrc device.IdentityDataGetter
serverURL string
tenantToken client.AuthToken

localProxy *proxy.ProxyController
}

// AuthManagerConfig holds the configuration of the auth manager
Expand All @@ -162,13 +165,15 @@ func NewAuthManager(conf AuthManagerConfig) *MenderAuthManager {
return nil
}

var api *client.ApiClient
httpConfig := client.Config{}
if conf.Config != nil {
var err error
api, err = client.New(conf.Config.GetHttpConfig())
if err != nil {
return nil
}
httpConfig = conf.Config.GetHttpConfig()

}

api, err := client.New(httpConfig)
if err != nil {
return nil
}

// get the first server URL available in the config file
Expand All @@ -192,6 +197,16 @@ func NewAuthManager(conf AuthManagerConfig) *MenderAuthManager {
log.Errorf("Error handling the caching of the tenant token: %s", err.Error())
}

wsDialer, err := client.NewWebsocketDialer(httpConfig)
if err != nil {
return nil
}

proxy, err := proxy.NewProxyController(api, wsDialer, "", "")
if err != nil {
log.Errorf("Error creating local proxy: %s", err.Error())
}

mgr := &MenderAuthManager{
&menderAuthManagerService{
inChan: make(chan AuthManagerRequest, authManagerInMessageChanSize),
Expand All @@ -207,6 +222,7 @@ func NewAuthManager(conf AuthManagerConfig) *MenderAuthManager {
idSrc: conf.IdentitySource,
tenantToken: tenantToken,
serverURL: serverURL,
localProxy: proxy,
},
}

Expand Down Expand Up @@ -310,7 +326,7 @@ func (m *menderAuthManagerService) registerDBusCallbacks() (unregisterFunc func(
case message := <-respChan:
tokenAndServerURL := dbus.TokenAndServerURL{
Token: string(message.AuthToken),
ServerURL: m.serverURL,
ServerURL: m.localProxy.GetServerUrl(),
}
return tokenAndServerURL, message.Error
case <-time.After(5 * time.Second):
Expand Down Expand Up @@ -370,7 +386,7 @@ func (m *menderAuthManagerService) run() {
// When we are being stopped, make sure they know that this happened.
defer func() {
// Checking for panic here is just to avoid deadlocking if we
// get an unexpected panic: Let it propogate instead of blocking
// get an unexpected panic: Let it propagate instead of blocking
// on the channel. If the program is correct, this should never
// be non-nil.
if recover() == nil {
Expand Down Expand Up @@ -487,6 +503,8 @@ func (m *MenderAuthManager) Stop() {
<-m.menderAuthManagerService.quitResp
m.menderAuthManagerService.hasStarted = false

m.localProxy.Stop()

runtime.SetFinalizer(m, nil)
}

Expand Down Expand Up @@ -516,7 +534,7 @@ func (m *menderAuthManagerService) broadcast(message AuthManagerResponse) {
if m.dbus != nil {
tokenAndServerURL := dbus.TokenAndServerURL{
Token: string(message.AuthToken),
ServerURL: m.serverURL,
ServerURL: m.localProxy.GetServerUrl(),
}
_ = m.dbus.EmitSignal(m.dbusConn, "", AuthManagerDBusPath,
AuthManagerDBusInterfaceName, AuthManagerDBusSignalJwtTokenStateChange,
Expand All @@ -527,7 +545,26 @@ func (m *menderAuthManagerService) broadcast(message AuthManagerResponse) {
// broadcastAuthTokenStateChange broadcasts the notification to all the subscribers
func (m *menderAuthManagerService) broadcastAuthTokenStateChange() {
authToken, err := m.authToken()

// stop proxy regardless
m.localProxy.Stop()
if err == nil {
// reconfigure proxy
err = m.localProxy.Reconfigure(m.serverURL, string(authToken))
if err != nil {
log.Errorf(
"Could not reconfigure proxy with URL %q and token '%s...'"+
" Other applications running on the device won't be able"+
" to reach the Mender server. Error: %s",
m.serverURL,
string(authToken)[:7],
err.Error(),
)
} else {
m.localProxy.Start()

}

m.broadcast(AuthManagerResponse{
Event: EventAuthTokenStateChange,
AuthToken: authToken,
Expand Down
12 changes: 7 additions & 5 deletions app/mender_test.go
Expand Up @@ -952,7 +952,7 @@ func TestReauthorization(t *testing.T) {
assert.Error(t, err)
}

// TestFailbackServers tests the optional failover feature for which
// TestFailoverServers tests the optional failover feature for which
// a client can swap server if current server stops serving.
//
// Add multiple servers into conf.MenderConfig, and let the first one "fail".
Expand All @@ -979,6 +979,11 @@ func TestFailoverServers(t *testing.T) {
srv2 := cltest.NewClientTestServer()
defer srv1.Close()
defer srv2.Close()
// Give srv1 the wrong artifact- and device name to trigger 400 Bad Request
srv1.Update.Current = &client.CurrentUpdate{
Artifact: "mender-image-foo",
DeviceType: "dev-bar",
}
// Give srv2 knowledge about client artifact- and device name
srv2.Update.Current = &client.CurrentUpdate{
Artifact: "mender-image",
Expand Down Expand Up @@ -1132,10 +1137,7 @@ func TestMutualTLSClientConnection(t *testing.T) {

for name, test := range tests {
t.Run(name, func(t *testing.T) {
srv := cltest.NewClientTestServer(
cltest.Options{
TLSConfig: &tc,
})
srv := cltest.NewClientTestServer(&tc)
defer srv.Close()

test.conf.ServerURL = srv.URL
Expand Down

0 comments on commit 84204a3

Please sign in to comment.