Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add explicit trust establishment #383

Merged
merged 29 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8e0920d
go: Use latest dependencies
roosterfish Aug 26, 2024
3f9ca3a
service: Add EFF wordlist
roosterfish Aug 26, 2024
f04787b
api/session: Add session API endpoints
roosterfish Aug 26, 2024
3a98ec7
service: Add session concept
roosterfish Aug 26, 2024
21abafb
client: Add the WebsocketGateway
roosterfish Aug 26, 2024
8445749
api/services: Use the authHandlerMTLS func
roosterfish Aug 26, 2024
a100d4d
api/auth: Allow temporary trust store and HMAC authentication
roosterfish Aug 26, 2024
9030f3f
client: Replace secret with HMAC
roosterfish Aug 26, 2024
8174d83
client: Add new StartSession and JoinIntent client functions
roosterfish Aug 26, 2024
237829d
mdns: Lookup only a single system and increase the version number
roosterfish Aug 26, 2024
7c413f2
service: Remove secret
roosterfish Aug 26, 2024
8c79d86
service: Add session concept and remove service level mDNS
roosterfish Aug 26, 2024
61751b6
cmd/microcloud: Add join and preseed subcommands
roosterfish Aug 26, 2024
0f54bba
cmd/microcloud: Add ask utilities and replace secret in client calls
roosterfish Aug 26, 2024
a2ef02f
cmd/microcloud: Use session concept for init
roosterfish Aug 26, 2024
cd814e2
cmd/microcloud: Use session concept for add
roosterfish Aug 26, 2024
147355f
cmd/microcloud: Add join subcommand
roosterfish Aug 26, 2024
e29abc8
cmd/microcloud: Move preseed code into its own subcommand
roosterfish Aug 26, 2024
276dd98
cmd/microcloud/preseed: Update test
roosterfish Aug 26, 2024
c8dafac
cmd/microcloud: Add initiating and joining session handlers for the CLI
roosterfish Aug 26, 2024
61a25f4
cmd/microcloudd: Remove secret and mDNS from daemon
roosterfish Aug 26, 2024
6e6a1a4
doc: Apply trust establishment changes to the init, add and preseed docs
roosterfish Sep 9, 2024
d347a7d
test/includes: Add capture_and_join
roosterfish Sep 13, 2024
459ff56
test/suites: Rework preseed to work with explicit trust establishment
roosterfish Sep 13, 2024
91e2452
test/suites: Rework add to work with explicit trust establishment
roosterfish Sep 30, 2024
c522ff0
test/suites: Rework recover to work with explicit trust establishment
roosterfish Sep 30, 2024
f9322a3
test/suites: Rework basic to work with explicit trust establishment
roosterfish Oct 2, 2024
aa54fb6
cmd/microcloud: Always join the MicroCloud microcluster first
roosterfish Oct 2, 2024
46fd760
test/suites/basic: Use openvswitch bridge driver for IPv6 tests
roosterfish Oct 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ jobs:
run: |
sudo add-apt-repository ppa:dqlite/dev -y --no-update
sudo apt-get update
sudo apt-get install --no-install-recommends -y libdqlite-dev pkg-config
sudo apt-get install --no-install-recommends -y libdqlite-dev pkg-config openvswitch-switch

- name: Build
run: |
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
GOMIN=1.22.5
GOMIN=1.22.6

.PHONY: default
default: build
Expand Down
2 changes: 1 addition & 1 deletion api/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var ServicesCmd = func(sh *service.Handler) rest.Endpoint {
Name: "services",
Path: "services",

Put: rest.EndpointAction{Handler: authHandler(sh, servicesPut), AllowUntrusted: true, ProxyTarget: true},
Put: rest.EndpointAction{Handler: authHandlerMTLS(sh, servicesPut), ProxyTarget: true},
}
}

Expand Down
61 changes: 50 additions & 11 deletions api/services_auth.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package api

import (
"errors"
"fmt"
"net/http"

"github.com/canonical/lxd/lxd/response"
"github.com/canonical/lxd/lxd/util"
"github.com/canonical/lxd/shared/logger"
"github.com/canonical/lxd/shared/trust"
"github.com/canonical/microcluster/v2/state"

"github.com/canonical/microcloud/microcloud/service"
Expand All @@ -15,8 +17,8 @@ import (
// endpointHandler is just a convenience for writing clean return types.
type endpointHandler func(state.State, *http.Request) response.Response

// authHandler ensures a request has been authenticated with the mDNS broadcast secret.
func authHandler(sh *service.Handler, f endpointHandler) endpointHandler {
// authHandlerMTLS ensures a request has been authenticated using mTLS.
func authHandlerMTLS(sh *service.Handler, f endpointHandler) endpointHandler {
return func(s state.State, r *http.Request) response.Response {
if r.RemoteAddr == "@" {
logger.Debug("Allowing unauthenticated request through unix socket")
Expand All @@ -25,27 +27,64 @@ func authHandler(sh *service.Handler, f endpointHandler) endpointHandler {
}

// Use certificate based authentication between cluster members.
if r.TLS != nil && r.Host == s.Address().URL.Host {
if r.TLS != nil {
trustedCerts := s.Remotes().CertificatesNative()
for _, cert := range r.TLS.PeerCertificates {
// First evaluate the permanent turst store.
trusted, _ := util.CheckMutualTLS(*cert, trustedCerts)
if trusted {
return f(s, r)
}

// Second evaluate the temporary trust store.
// This is the fallback during the forming of the cluster.
trusted, _ = util.CheckMutualTLS(*cert, sh.TemporaryTrustStore())
if trusted {
return f(s, r)
}
}
}

secret := r.Header.Get("X-MicroCloud-Auth")
if secret == "" {
return response.BadRequest(fmt.Errorf("No auth secret in response"))
}
return response.Forbidden(fmt.Errorf("Failed to authenticate using mTLS"))
}
}

// authHandlerHMAC ensures a request has been authenticated using the HMAC in the Authorization header.
func authHandlerHMAC(sh *service.Handler, f endpointHandler) endpointHandler {
return func(s state.State, r *http.Request) response.Response {
sessionFunc := func(session *service.Session) error {
h, err := trust.NewHMACArgon2([]byte(session.Passphrase()), nil, trust.NewDefaultHMACConf(HMACMicroCloud10))
if err != nil {
return err
}

err = trust.HMACEqual(h, r)
if err != nil {
attemptErr := session.RegisterFailedAttempt()
if attemptErr != nil {
errorCause := errors.New("Stopping session after too many failed attempts")

// Immediately stop the session to not allow further join attempts.
stopErr := session.Stop(errorCause)
if stopErr != nil {
return fmt.Errorf("Cannot stop session after too many failed attempts: %w", stopErr)
}

// Log the error and return it to the caller
logger.Warn(errorCause.Error())
return errorCause
}

return err
}

if sh.AuthSecret == "" {
return response.BadRequest(fmt.Errorf("No generated auth secret"))
return nil
}

if sh.AuthSecret != secret {
return response.SmartError(fmt.Errorf("Request secret does not match, ignoring request"))
// Run a r/w transaction against the session as we might stop it due to too many failed attempts.
err := sh.SessionTransaction(false, sessionFunc)
if err != nil {
return response.SmartError(err)
}

return f(s, r)
Expand Down
2 changes: 1 addition & 1 deletion api/services_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var ServicesClusterCmd = func(sh *service.Handler) rest.Endpoint {
Name: "services/cluster/{name}",
Path: "services/cluster/{name}",

Delete: rest.EndpointAction{Handler: authHandler(sh, removeClusterMember), AllowUntrusted: true},
Delete: rest.EndpointAction{Handler: authHandlerMTLS(sh, removeClusterMember)},
}
}

Expand Down
10 changes: 5 additions & 5 deletions api/services_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ func proxy(sh *service.Handler, name, path string, handler endpointHandler) rest
Name: name,
Path: path,

Get: rest.EndpointAction{Handler: authHandler(sh, handler), AllowUntrusted: true, ProxyTarget: true},
Put: rest.EndpointAction{Handler: authHandler(sh, handler), AllowUntrusted: true, ProxyTarget: true},
Post: rest.EndpointAction{Handler: authHandler(sh, handler), AllowUntrusted: true, ProxyTarget: true},
Patch: rest.EndpointAction{Handler: authHandler(sh, handler), AllowUntrusted: true, ProxyTarget: true},
Delete: rest.EndpointAction{Handler: authHandler(sh, handler), AllowUntrusted: true, ProxyTarget: true},
Get: rest.EndpointAction{Handler: authHandlerMTLS(sh, handler), ProxyTarget: true},
Put: rest.EndpointAction{Handler: authHandlerMTLS(sh, handler), ProxyTarget: true},
Post: rest.EndpointAction{Handler: authHandlerMTLS(sh, handler), ProxyTarget: true},
Patch: rest.EndpointAction{Handler: authHandlerMTLS(sh, handler), ProxyTarget: true},
Delete: rest.EndpointAction{Handler: authHandlerMTLS(sh, handler), ProxyTarget: true},
}
}

Expand Down
2 changes: 1 addition & 1 deletion api/services_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var ServiceTokensCmd = func(sh *service.Handler) rest.Endpoint {
Name: "services/{serviceType}/tokens",
Path: "services/{serviceType}/tokens",

Post: rest.EndpointAction{Handler: authHandler(sh, serviceTokensPost), AllowUntrusted: true, ProxyTarget: true},
Post: rest.EndpointAction{Handler: authHandlerMTLS(sh, serviceTokensPost), ProxyTarget: true},
}
}

Expand Down
Loading
Loading