package api import ( "fmt" "net" "github.com/Sirupsen/logrus" "github.com/asaskevich/govalidator" "github.com/cad/ovpm" "github.com/cad/ovpm/permset" gcontext "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) // AuthUnaryInterceptor is a interceptor function. // // See https://godoc.org/google.golang.org/grpc#UnaryServerInterceptor. func AuthUnaryInterceptor(ctx gcontext.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { var enableAuthCheck bool md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, fmt.Errorf("Expected 2 metadata items in context; got %v", md) } // We enable auth check if we find a non-loopback // or invalid IP in the headers coming from the grpc-gateway. for _, userAgentIP := range md["x-forwarded-for"] { // Check if the remote user IP addr is a proper IP addr. if !govalidator.IsIP(userAgentIP) { enableAuthCheck = true logrus.Debugf("grpc request user agent ip can not be fetched from x-forwarded-for metadata, enabling auth check module '%s'", userAgentIP) break } // Check if the remote user IP addr is a loopback IP addr. if ip := net.ParseIP(userAgentIP); !ip.IsLoopback() { enableAuthCheck = true logrus.Debugf("grpc request user agent ips include non-loopback ip, enabling auth check module '%s'", userAgentIP) break } // TODO(cad): We assume gRPC endpoints are for cli only therefore // we are listening only on looback IP. // // But if we decide use gRPC endpoints publicly, we need to add // extra checks against gRPC remote peer IP to test if the request // is coming from a remote peer IP or also from a loopback ip. } if !enableAuthCheck { logrus.Debugf("rpc: auth-check not enabled: %s", md["x-forwarded-for"]) ctx = NewUsernameContext(ctx, "root") permissions := permset.New(ovpm.AdminPerms()...) ctx = permset.NewContext(ctx, permissions) } if enableAuthCheck { switch info.FullMethod { // AuthService methods case "/pb.AuthService/Status": return authRequired(ctx, req, handler) // UserService methods case "/pb.UserService/List": return authRequired(ctx, req, handler) case "/pb.UserService/Create": return authRequired(ctx, req, handler) case "/pb.UserService/Update": return authRequired(ctx, req, handler) case "/pb.UserService/Delete": return authRequired(ctx, req, handler) case "/pb.UserService/Renew": return authRequired(ctx, req, handler) case "/pb.UserService/GenConfig": return authRequired(ctx, req, handler) // VPNService methods case "/pb.VPNService/Status": return authRequired(ctx, req, handler) case "/pb.VPNService/Init": return authRequired(ctx, req, handler) case "/pb.VPNService/Update": return authRequired(ctx, req, handler) case "/pb.VPNService/Restart": return authRequired(ctx, req, handler) // NetworkService methods case "/pb.NetworkService/Create": return authRequired(ctx, req, handler) case "/pb.NetworkService/List": return authRequired(ctx, req, handler) case "/pb.NetworkService/Delete": return authRequired(ctx, req, handler) case "/pb.NetworkService/GetAllTypes": return authRequired(ctx, req, handler) case "/pb.NetworkService/GetAssociatedUsers": return authRequired(ctx, req, handler) case "/pb.NetworkService/Associate": return authRequired(ctx, req, handler) case "/pb.NetworkService/Dissociate": return authRequired(ctx, req, handler) default: logrus.Debugf("rpc: auth not required for endpoint: '%s'", info.FullMethod) } } return handler(ctx, req) }