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

PMM-12686 Basic/Token auth between server and client. #2852

Merged
merged 34 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5d197bb
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
JiriCtvrtka Feb 26, 2024
db74044
PMM-12686 Authorization between server and client.
JiriCtvrtka Feb 27, 2024
d72fbcc
PMM-12686 Tests.
JiriCtvrtka Feb 28, 2024
ffdbd78
PMM-12686 Lint.
JiriCtvrtka Feb 28, 2024
9d27ce2
PMM-12686 Comment fix.
JiriCtvrtka Feb 28, 2024
aee4991
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
JiriCtvrtka Feb 28, 2024
6fb770b
PMM-12686 New required permissions for Connect endpoint.
JiriCtvrtka Feb 28, 2024
b6995bc
PMM-12686 Apply suggestion.
JiriCtvrtka Feb 29, 2024
b03ca85
PMM-12686 Add unit test for authenticate method.
JiriCtvrtka Feb 29, 2024
6ea9603
PMM-12686 Format.
JiriCtvrtka Feb 29, 2024
bb80511
PMM-12686 Part changes after review.
JiriCtvrtka Mar 4, 2024
c30c41b
PMM-12686 Unit tests for auth server.
JiriCtvrtka Mar 4, 2024
b327e85
PMM-12686 Revert unnecessary changes anymore.
JiriCtvrtka Mar 4, 2024
56c695d
PMM-12686 Dynamic names in auth test.
JiriCtvrtka Mar 4, 2024
9afbe2b
PMM-12686 Skip check for pmm-server agent.
JiriCtvrtka Mar 4, 2024
a2dc551
PMM-12686 Better local auth check.
JiriCtvrtka Mar 5, 2024
c277a12
PMM-12686 Local auth check.
JiriCtvrtka Mar 5, 2024
6dc3675
PMM-12686 Refactor.
JiriCtvrtka Mar 5, 2024
4fa045a
PMM-12686 Refactor.
JiriCtvrtka Mar 5, 2024
20b47f9
PMM-12686 Refactor.
JiriCtvrtka Mar 5, 2024
a7e7b6a
PMM-12686 Fix.
JiriCtvrtka Mar 5, 2024
271232e
Update managed/services/grafana/auth_server.go
JiriCtvrtka Mar 5, 2024
d98b509
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
ademidoff Mar 6, 2024
d24a263
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
ademidoff Mar 7, 2024
bb0e0e2
PMM-12686 Revert of some changes.
JiriCtvrtka Mar 11, 2024
f64f6be
PMM-12686 Another reverted changes.
JiriCtvrtka Mar 11, 2024
2d34556
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
JiriCtvrtka Mar 11, 2024
cf3ebbc
PMM-12686 Years in licence.
JiriCtvrtka Mar 12, 2024
6abd1b9
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
JiriCtvrtka Mar 12, 2024
991e090
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
JiriCtvrtka Mar 12, 2024
4c540c4
PMM-12686 Missed parallel in one test case.
JiriCtvrtka Mar 12, 2024
2aaa387
Merge branch 'PMM-12686-PMM-server-client-auth' of github.com:percona…
JiriCtvrtka Mar 12, 2024
9347186
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
JiriCtvrtka Mar 18, 2024
e4981ac
Merge branch 'PMM-12251-service-accounts' into PMM-12686-PMM-server-c…
JiriCtvrtka Mar 20, 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
46 changes: 34 additions & 12 deletions managed/services/grafana/auth_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ import (
"github.com/percona/pmm/managed/models"
)

const (
connectionEndpoint = "/agent.Agent/Connect"
)

// rules maps original URL prefix to minimal required role.
var rules = map[string]role{
// TODO https://jira.percona.com/browse/PMM-4420
"/agent.Agent/Connect": none,
connectionEndpoint: admin,

"/inventory.": admin,
"/management.": admin,
Expand Down Expand Up @@ -459,6 +462,17 @@ func nextPrefix(path string) string {
return path[:i+1]
}

func isLocalAgentConnection(req *http.Request) bool {
ip := strings.Split(req.RemoteAddr, ":")[0]
pmmAgent := req.Header.Get("Pmm-Agent-Id")
path := req.Header.Get("X-Original-Uri")
if ip == "127.0.0.1" && pmmAgent == "pmm-server" && path == connectionEndpoint {
return true
}

return false
}

// authenticate checks if user has access to a specific path.
// It returns user information retrieved during authentication.
// Paths which require no Grafana role return zero value for
Expand Down Expand Up @@ -498,22 +512,30 @@ func (s *AuthServer) authenticate(ctx context.Context, req *http.Request, l *log
return nil, nil
}

// Get authenticated user from Grafana
authUser, authErr := s.getAuthUser(ctx, req, l)
if authErr != nil {
return nil, authErr
var user *authUser
if isLocalAgentConnection(req) {
user = &authUser{
role: rules[connectionEndpoint],
userID: 0,
}
} else {
var authErr *authError
// Get authenticated user from Grafana
user, authErr = s.getAuthUser(ctx, req, l)
if authErr != nil {
return nil, authErr
}
}
l = l.WithField("role", user.role.String())

l = l.WithField("role", authUser.role.String())

if authUser.role == grafanaAdmin {
if user.role == grafanaAdmin {
l.Debugf("Grafana admin, allowing access.")
return authUser, nil
return user, nil
}

if minRole <= authUser.role {
if minRole <= user.role {
l.Debugf("Minimal required role is %q, granting access.", minRole)
return authUser, nil
return user, nil
}

l.Warnf("Minimal required role is %q.", minRole)
Expand Down
73 changes: 71 additions & 2 deletions managed/services/grafana/auth_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"gopkg.in/reform.v1"
"gopkg.in/reform.v1/dialects/postgresql"

Expand Down Expand Up @@ -161,7 +162,6 @@ func TestAuthServerMustSetup(t *testing.T) {

func TestAuthServerAuthenticate(t *testing.T) {
t.Parallel()
// logrus.SetLevel(logrus.TraceLevel)

checker := &mockAwsInstanceChecker{}
checker.Test(t)
Expand Down Expand Up @@ -198,7 +198,7 @@ func TestAuthServerAuthenticate(t *testing.T) {
})

for uri, minRole := range map[string]role{
"/agent.Agent/Connect": none,
"/agent.Agent/Connect": admin,

"/inventory.Nodes/ListNodes": admin,
"/management.Actions/StartMySQLShowTableStatusAction": viewer,
Expand Down Expand Up @@ -270,6 +270,75 @@ func TestAuthServerAuthenticate(t *testing.T) {
}
}

func TestServerClientConnection(t *testing.T) {
t.Parallel()

checker := &mockAwsInstanceChecker{}
checker.Test(t)
t.Cleanup(func() { checker.AssertExpectations(t) })

ctx := context.Background()
c := NewClient("127.0.0.1:3000")
s := NewAuthServer(c, checker, nil)

t.Run("Basic auth - success", func(t *testing.T) {
t.Parallel()

req, err := http.NewRequestWithContext(ctx, http.MethodGet, connectionEndpoint, nil)
require.NoError(t, err)
req.SetBasicAuth("admin", "admin")

_, authError := s.authenticate(ctx, req, logrus.WithField("test", t.Name()))
assert.Nil(t, authError)
})

t.Run("Basic auth - fail", func(t *testing.T) {
t.Parallel()

req, err := http.NewRequestWithContext(ctx, http.MethodGet, connectionEndpoint, nil)
require.NoError(t, err)
req.SetBasicAuth("admin", "wrong")

_, authError := s.authenticate(ctx, req, logrus.WithField("test", t.Name()))
assert.Equal(t, codes.Unauthenticated, authError.code)
})

t.Run("Token auth - success", func(t *testing.T) {
t.Parallel()

nodeName := fmt.Sprintf("N1-%d", time.Now().UnixNano())
headersMD := metadata.New(map[string]string{
"Authorization": "Basic YWRtaW46YWRtaW4=",
})
ctx := metadata.NewIncomingContext(context.Background(), headersMD)
_, serviceToken, err := c.CreateServiceAccount(ctx, nodeName, true)
require.NoError(t, err)
defer func() {
warning, err := c.DeleteServiceAccount(ctx, nodeName, true)
require.NoError(t, err)
require.Empty(t, warning)
}()

req, err := http.NewRequestWithContext(ctx, http.MethodGet, connectionEndpoint, nil)
require.NoError(t, err)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", serviceToken))

_, authError := s.authenticate(ctx, req, logrus.WithField("test", t.Name()))
assert.Nil(t, authError)
})

t.Run("Token auth - fail", func(t *testing.T) {
t.Parallel()

req, err := http.NewRequestWithContext(ctx, http.MethodGet, connectionEndpoint, nil)
require.NoError(t, err)
req.Header.Set("Authorization", "Bearer wrong")

_, authError := s.authenticate(ctx, req, logrus.WithField("test", t.Name()))
assert.Equal(t, codes.Unauthenticated, authError.code)
})
}

func TestAuthServerAddVMGatewayToken(t *testing.T) {
ctx := logger.Set(context.Background(), t.Name())
uuid.SetRand(&tests.IDReader{})
Expand Down
Loading