mirror of https://github.com/grpc/grpc-go.git
examples: add example to illustrate the use of file watcher interceptor (#7226)
authz: add example to illustrate the use of file watcher interceptor
This commit is contained in:
parent
03da31acc6
commit
6e59dd1d7f
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "authz",
|
||||
"allow_rules": [
|
||||
{
|
||||
"name": "allow_UnaryEcho",
|
||||
"request": {
|
||||
"paths": ["/grpc.examples.echo.Echo/UnaryEcho"],
|
||||
"headers": [
|
||||
{
|
||||
"key": "UNARY_ECHO:RW",
|
||||
"values": ["true"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "allow_BidirectionalStreamingEcho",
|
||||
"request": {
|
||||
"paths": ["/grpc.examples.echo.Echo/BidirectionalStreamingEcho"],
|
||||
"headers": [
|
||||
{
|
||||
"key": "STREAM_ECHO:RW",
|
||||
"values": ["true"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"deny_rules": []
|
||||
}
|
||||
|
|
@ -1,26 +1,29 @@
|
|||
# RBAC authorization
|
||||
|
||||
This example uses the `StaticInterceptor` from the `google.golang.org/grpc/authz`
|
||||
package. It uses a header based RBAC policy to match each gRPC method to a
|
||||
required role. For simplicity, the context is injected with mock metadata which
|
||||
includes the required roles, but this should be fetched from an appropriate
|
||||
service based on the authenticated context.
|
||||
This example uses the `StaticInterceptor` and `FileWatcherInterceptor` from the
|
||||
`google.golang.org/grpc/authz` package. It uses a header based RBAC policy to
|
||||
match each gRPC method to a required role. For simplicity, the context is
|
||||
injected with mock metadata which includes the required roles, but this should
|
||||
be fetched from an appropriate service based on the authenticated context.
|
||||
|
||||
## Try it
|
||||
|
||||
Server requires the following roles on an authenticated user to authorize usage
|
||||
of these methods:
|
||||
Server is expected to require the following roles on an authenticated user to
|
||||
authorize usage of these methods:
|
||||
|
||||
- `UnaryEcho` requires the role `UNARY_ECHO:W`
|
||||
- `BidirectionalStreamingEcho` requires the role `STREAM_ECHO:RW`
|
||||
|
||||
Upon receiving a request, the server first checks that a token was supplied,
|
||||
decodes it and checks that a secret is correctly set (hardcoded to `super-secret`
|
||||
for simplicity, this should use a proper ID provider in production).
|
||||
decodes it and checks that a secret is correctly set (hardcoded to
|
||||
`super-secret` for simplicity, this should use a proper ID provider in
|
||||
production).
|
||||
|
||||
If the above is successful, it uses the username in the token to set appropriate
|
||||
roles (hardcoded to the 2 required roles above if the username matches `super-user`
|
||||
for simplicity, these roles should be supplied externally as well).
|
||||
roles (hardcoded to the 2 required roles above if the username matches
|
||||
`super-user` for simplicity, these roles should be supplied externally as well).
|
||||
|
||||
### Authorization with static policy
|
||||
|
||||
Start the server with:
|
||||
|
||||
|
|
@ -38,3 +41,35 @@ Start the client with:
|
|||
```
|
||||
go run client/main.go
|
||||
```
|
||||
|
||||
### Authorization by watching a policy file
|
||||
|
||||
The server accepts an optional `--authz-option filewatcher` flag to set up
|
||||
authorization policy by reading a [policy
|
||||
file](/examples/data/rbac/policy.json), and to look for update on the policy
|
||||
file every 100 millisecond. Having `GRPC_GO_LOG_SEVERITY_LEVEL` environment
|
||||
variable set to `info` will log out the reload activity of the policy every time
|
||||
a file update is detected.
|
||||
|
||||
Start the server with:
|
||||
|
||||
```
|
||||
GRPC_GO_LOG_SEVERITY_LEVEL=info go run server/main.go --authz-option filewatcher
|
||||
```
|
||||
|
||||
Start the client with:
|
||||
|
||||
```
|
||||
go run client/main.go
|
||||
```
|
||||
|
||||
The client will first hit `codes.PermissionDenied` error when invoking
|
||||
`UnaryEcho` although a legitimate username (`super-user`) is associated with the
|
||||
RPC. This is because the policy file has an intentional glitch (falsely asks for
|
||||
role `UNARY_ECHO:RW`).
|
||||
|
||||
While the server is still running, edit and save the policy file to replace
|
||||
`UNARY_ECHO:RW` with the correct role `UNARY_ECHO:W` (policy reload activity
|
||||
should now be found in server logs). This time when the client is started again
|
||||
with the command above, it will be able to get responses just as in the
|
||||
static-policy example.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/authz"
|
||||
|
|
@ -76,10 +77,13 @@ const (
|
|||
"deny_rules": []
|
||||
}
|
||||
`
|
||||
authzOptStatic = "static"
|
||||
authzOptFileWatcher = "filewatcher"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.Int("port", 50051, "the port to serve on")
|
||||
port = flag.Int("port", 50051, "the port to serve on")
|
||||
authzOpt = flag.String("authz-option", authzOptStatic, "the authz option (static or filewatcher)")
|
||||
|
||||
errMissingMetadata = status.Errorf(codes.InvalidArgument, "missing metadata")
|
||||
)
|
||||
|
|
@ -186,6 +190,10 @@ func authStreamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServe
|
|||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *authzOpt != authzOptStatic && *authzOpt != authzOptFileWatcher {
|
||||
log.Fatalf("Invalid authz option: %s", *authzOpt)
|
||||
}
|
||||
|
||||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
|
||||
if err != nil {
|
||||
log.Fatalf("Listening on local port %q: %v", *port, err)
|
||||
|
|
@ -197,14 +205,28 @@ func main() {
|
|||
log.Fatalf("Loading credentials: %v", err)
|
||||
}
|
||||
|
||||
// Create an authorization interceptor using a static policy.
|
||||
staticInteceptor, err := authz.NewStatic(authzPolicy)
|
||||
if err != nil {
|
||||
log.Fatalf("Creating a static authz interceptor: %v", err)
|
||||
// Create authorization interceptors according to the authz-option command-line flag.
|
||||
var unaryAuthzInterceptor grpc.UnaryServerInterceptor
|
||||
var streamAuthzInterceptor grpc.StreamServerInterceptor
|
||||
if *authzOpt == authzOptStatic {
|
||||
// Create an authorization interceptor using a static policy.
|
||||
staticInterceptor, err := authz.NewStatic(authzPolicy)
|
||||
if err != nil {
|
||||
log.Fatalf("Creating a static authz interceptor: %v", err)
|
||||
}
|
||||
unaryAuthzInterceptor, streamAuthzInterceptor = staticInterceptor.UnaryInterceptor, staticInterceptor.StreamInterceptor
|
||||
} else if *authzOpt == authzOptFileWatcher {
|
||||
// Create an authorization interceptor by watching a policy file.
|
||||
fileWatcherInterceptor, err := authz.NewFileWatcher(data.Path("rbac/policy.json"), 100*time.Millisecond)
|
||||
if err != nil {
|
||||
log.Fatalf("Creating a file watcher authz interceptor: %v", err)
|
||||
}
|
||||
unaryAuthzInterceptor, streamAuthzInterceptor = fileWatcherInterceptor.UnaryInterceptor, fileWatcherInterceptor.StreamInterceptor
|
||||
}
|
||||
unaryInts := grpc.ChainUnaryInterceptor(authUnaryInterceptor, staticInteceptor.UnaryInterceptor)
|
||||
streamInts := grpc.ChainStreamInterceptor(authStreamInterceptor, staticInteceptor.StreamInterceptor)
|
||||
s := grpc.NewServer(grpc.Creds(creds), unaryInts, streamInts)
|
||||
|
||||
unaryInterceptors := grpc.ChainUnaryInterceptor(authUnaryInterceptor, unaryAuthzInterceptor)
|
||||
streamInterceptors := grpc.ChainStreamInterceptor(authStreamInterceptor, streamAuthzInterceptor)
|
||||
s := grpc.NewServer(grpc.Creds(creds), unaryInterceptors, streamInterceptors)
|
||||
|
||||
// Register EchoServer on the server.
|
||||
pb.RegisterEchoServer(s, &server{})
|
||||
|
|
|
|||
Loading…
Reference in New Issue