Skip to content

Commit

Permalink
gRPC Go local credential implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
yihuazhang committed Apr 10, 2020
1 parent a9555d0 commit f6f52c9
Show file tree
Hide file tree
Showing 2 changed files with 249 additions and 0 deletions.
105 changes: 105 additions & 0 deletions credentials/local/local.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

// Package local includes implementation of local credentials.
// The local credential should be used in either a local TCP or UDS connection.

package local

import (
"context"
"fmt"
"net"
"strings"

"google.golang.org/grpc/credentials"
)

// LocalInfo contains the auth information for a local connection.
// It implements the AuthInfo interface.
type LocalInfo struct {
credentials.CommonAuthInfo
}

// AuthType returns the type of LocalInfo as a string.
func (t LocalInfo) AuthType() string {
return "local"
}

// localTC is the credentials required to eatablish a local connection.
type localTC struct {
info *credentials.ProtocolInfo
}

func (c *localTC) Info() credentials.ProtocolInfo {
return *c.info
}

// getSecurityLevel returns the security level for a local connection.
// It returns an error if a connection is not local.
func getSecurityLevel(network, addr string) (credentials.SecurityLevel, error) {
// Local TCP connection
if strings.HasPrefix(addr, "127.") || strings.HasPrefix(addr, "localhost:") || strings.HasPrefix(addr, "[::1]:") {
return credentials.NoSecurity, nil
// UDS connection
} else if network == "unix" {
return credentials.PrivacyAndIntegrity, nil
// Not a local connection and should fail
} else {
return 0, fmt.Errorf("credentials: local credentials should be used in a local connection.")
}
}

func (c *localTC) ClientHandshake(ctx context.Context, authority string, conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
secLevel, err := getSecurityLevel(conn.RemoteAddr().Network(), conn.RemoteAddr().String())
if err == nil {
return conn, LocalInfo{credentials.CommonAuthInfo{secLevel}}, nil
} else {
return nil, nil, err
}
}

func (c *localTC) ServerHandshake(conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
secLevel, err := getSecurityLevel(conn.RemoteAddr().Network(), conn.RemoteAddr().String())
if err == nil {
return conn, LocalInfo{credentials.CommonAuthInfo{secLevel}}, nil
} else {
return nil, nil, err
}
}

// NewLocal returns a local credential implementing TransportCredentials interface.
func NewLocal() credentials.TransportCredentials {
return &localTC{
info: &credentials.ProtocolInfo{
SecurityProtocol: "local",
},
}
}

func (c *localTC) Clone() credentials.TransportCredentials {
info := *c.info
return &localTC{
info: &info,
}
}

func (c *localTC) OverrideServerName(serverNameOverride string) error {
c.info.ServerName = serverNameOverride
return nil
}
144 changes: 144 additions & 0 deletions credentials/local/local_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
*
* Copyright 2016 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package local

import (
"context"
"net"
"testing"

"google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal/grpctest"
)

type s struct {
grpctest.Tester
}

func Test(t *testing.T) {
grpctest.RunSubTests(t, s{})
}

func (s) TestGetSecurityLevel(t *testing.T) {
testCases := []struct {
testNetwork string
testAddr string
want credentials.SecurityLevel
}{
{
testNetwork: "tcp",
testAddr: "127.0.0.1:10000",
want: credentials.NoSecurity,
},
{
testNetwork: "tcp",
testAddr: "[::1]:10000",
want: credentials.NoSecurity,
},
{
testNetwork: "tcp",
testAddr: "localhost:10000",
want: credentials.NoSecurity,
},
{
testNetwork: "unix",
testAddr: "/tmp/grpc_fullstack_test",
want: credentials.PrivacyAndIntegrity,
},
{
testNetwork: "tcp",
testAddr: "192.168.0.1:10000",
want: 0,
},
}
for _, tc := range testCases {
secLevel, _ := getSecurityLevel(tc.testNetwork, tc.testAddr)
if tc.want != secLevel {
t.Fatalf("GetSeurityLevel(%s, %s) returned %s but want %s", tc.testNetwork, tc.testAddr, tc.want.String(), secLevel.String())
}
}
}

type serverHandshake func(net.Conn) (credentials.AuthInfo, error)

// Server local handshake implementation.
func serverLocalHandshake(conn net.Conn) (credentials.AuthInfo, error) {
cred := NewLocal()
_, authInfo, err := cred.ServerHandshake(conn)
if err != nil {
return nil, err
}
return authInfo, nil
}

// Client local handshake implementation.
func clientLocalHandshake(conn net.Conn, lisAddr string) (credentials.AuthInfo, error) {
cred := NewLocal()
_, authInfo, err := cred.ClientHandshake(context.Background(), lisAddr, conn)
if err != nil {
return nil, err
}
return authInfo, nil
}

// Client connects to a server with local credentials.
func clientHandle(t *testing.T, hs func(net.Conn, string) (credentials.AuthInfo, error), lisAddr string) credentials.AuthInfo {
conn, _ := net.Dial("tcp", lisAddr)
defer conn.Close()
clientAuthInfo, err := hs(conn, lisAddr)
if err != nil {
t.Fatalf("Error on client while handshake. Error: %v", err)
}
return clientAuthInfo
}

// Server accepts a client's connection with local credentials.
func serverHandle(t *testing.T, hs serverHandshake, done chan credentials.AuthInfo, lis net.Listener) {
serverRawConn, err := lis.Accept()
serverAuthInfo, err := hs(serverRawConn)
if err != nil {
t.Errorf("Server failed while handshake. Error: %v", err)
serverRawConn.Close()
close(done)
return
}
done <- serverAuthInfo
}

func (s) TestServerAndClientHandshake(t *testing.T) {
done := make(chan credentials.AuthInfo, 1)
lis, _ := net.Listen("tcp", "localhost:0")
go serverHandle(t, serverLocalHandshake, done, lis)
defer lis.Close()
clientAuthInfo := clientHandle(t, clientLocalHandshake, lis.Addr().String())
serverAuthInfo, ok := <-done
if !ok {
t.Fatalf("Error at server-side")
}
clientLocal, _ := clientAuthInfo.(LocalInfo)
serverLocal, _ := serverAuthInfo.(LocalInfo)
clientSecLevel := clientLocal.CommonAuthInfo.SecurityLevel
serverSecLevel := serverLocal.CommonAuthInfo.SecurityLevel
if clientSecLevel != credentials.NoSecurity {
t.Fatalf("client's AuthInfo contains %s but should have %s.", clientSecLevel.String(), credentials.NoSecurity.String())
}
if serverSecLevel != credentials.NoSecurity {
t.Fatalf("server's AuthInfo contains %s but should have %s.", serverSecLevel.String(), credentials.NoSecurity.String())
}
}

0 comments on commit f6f52c9

Please sign in to comment.