Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Gunakan RBAC untuk melindungi perkhidmatan gRPC anda tepat pada definisi proto

Gunakan RBAC untuk melindungi perkhidmatan gRPC anda tepat pada definisi proto

Mary-Kate Olsen
Mary-Kate Olsenasal
2024-10-16 06:08:31973semak imbas

Use RBAC to protect your gRPC service right on proto definition

Kami mempunyai perkhidmatan gRPC yang mudah:

message GetUserRequest {
  string id = 1;
}

message GetUserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

service UsersService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
}

gRPC hebat dalam banyak cara, prestasinya hebat, ekosistemnya tidak boleh dibandingkan.

Tetapi imho, atas semua itu adalah kontrak bertaip yang disediakannya.

Saya seorang jurutera bahagian belakang, saya dan rakan mudah alih dan web saya boleh duduk, berbincang, membuat perjanjian, kemudian kami menjana kod klien stub dalam flutter dan es untuk pelaksanaan olok-olok, kumpul semula selepas 3 hari.

Hari yang baik dan berprestasi!

Tetapi tunggu, kami terlepas sesuatu!

Apakah sebenarnya peranan pengguna yang boleh memanggil API ini?

service UsersService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
}

Kaedah GetUser hampir mempunyai segala-galanya, tetapi masih tidak mencukupi untuk menerangkan keperluan kebenaran.

  • Dapatkan: ini ialah kata kerja, tindakan
  • Pengguna: ini adalah sumber

Dan kami hanya kehilangan bahagian Peranan untuk menerangkan peraturan RBAC.

Terlalu buruk, kita semakin hampir, hanya jika kita boleh melakukan sesuatu, sesuatu jenis selamat, sesuatu yang generik... ?

Hasrat anda akan menjadi kenyataan, dengan bantuan deskriptor proto

Mula-mula, lanjutkan google.protobuf.MethodOptions untuk menerangkan dasar anda

enum Role {
  ROLE_UNSPECIFIED = 0;
  ROLE_CUSTOMER = 1;
  ROLE_ADMIN = 2;
}

message RoleBasedAccessControl {
  repeated Role allowed_roles = 1;
  bool allow_unauthenticated = 2;
}

extend google.protobuf.MethodOptions {
  optional RoleBasedAccessControl access_control = 90000; // I don't know about this 90000, seem as long as it's unique
}

Kemudian, gunakan MethodOptions baharu dalam definisi Kaedah anda (fikirkan laluan import)

service UsersService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse) {
    option (components.rbac.v1.access_control) = {
      allowed_roles: [
        ROLE_CUSTOMER,
        ROLE_ADMIN
      ]
      allow_unauthenticated: false
    };
  }

  rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse) {
    option (components.rbac.v1.access_control) = {
      allowed_roles: [ROLE_ADMIN]
      allow_unauthenticated: false
    };
  }
}

Kemudian garpu dan ubah suai perintah penjana proto grpc-go

Gurau saja, malam sudah larut dan saya perlu berada di pejabat sebelum pukul 08:00 ?

Penyelesaian: tulis pemintas dan gunakannya dengan pelayan anda

Muatkan deskriptor kaedah:

func RBACUnaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
    // the info give us: /components.users.v1.UsersService/GetUser
    // we need to convert it to components.users.v1.UsersService.GetUser
    methodName := strings.Replace(strings.TrimPrefix(info.FullMethod, "/"), "/", ".", -1)
    desc, err := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(methodName))
    if err != nil {
        return nil, status.Errorf(codes.Internal, "method not found descriptor")
    }

    method, ok := desc.(protoreflect.MethodDescriptor)
    if !ok {
        return nil, status.Errorf(codes.Internal, "some hoe this is not a method")
    }

Cari pilihan kawalan_akses kami:

    var policy *rbacv1.RoleBasedAccessControl
    method.Options().ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
        if fd.FullName() != rbacv1.E_AccessControl.TypeDescriptor().FullName() {
            // continue finding the AccessControl field
            return true
        }

        b, err := proto.Marshal(v.Message().Interface())
        if err != nil {
            // TODO: better handle this as an Internal error
            // but for now, just return PermissionDenied
            return false
        }

        policy = &rbacv1.RoleBasedAccessControl{}
        if err := proto.Unmarshal(b, policy); err != nil {
            // same as above, better handle this as an Internal error
            return false
        }

        // btw I think this policy can be cached

        return false
    })

    if policy == nil {
        // secure by default, DENY_ALL if no control policy is found
        return nil, status.Errorf(codes.PermissionDenied, "permission denied")
    }

Contoh:

Tambah atau tambahkan pemintas yang baru dibuat pada hujung belakang anda

func newUsersServer() *grpc.Server {
    svc := grpc.NewServer(grpc.UnaryInterceptor(interceptors.RBACUnaryInterceptor))
    usersv1.RegisterUsersServiceServer(svc, &usersServer{})
    return svc
}

Kemudian ujinya:

    peasantCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("role", "ROLE_CUSTOMER"))
    _, err = client.GetUser(peasantCtx, &usersv1.GetUserRequest{})
    fmt.Println(status.Code(err))

    _, err = client.DeleteUser(peasantCtx, &usersv1.DeleteUserRequest{})
    fmt.Println(status.Code(err))

    knightlyAdminCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("role", "ROLE_ADMIN"))
    _, err = client.GetUser(knightlyAdminCtx, &usersv1.GetUserRequest{})
    fmt.Println(status.Code(err))

    _, err = client.DeleteUser(knightlyAdminCtx, &usersv1.DeleteUserRequest{})
    fmt.Println(status.Code(err))
    // Output:
    // OK
    // PermissionDenied
    // OK
    // OK

Akhir sekali, contoh kod dengan Contoh yang boleh diuji diterbitkan di sini https://github.com/nvcnvn/grpc-methods-descriptor-example

Atas ialah kandungan terperinci Gunakan RBAC untuk melindungi perkhidmatan gRPC anda tepat pada definisi proto. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:Belajar GO : - GelungArtikel seterusnya:Belajar GO : - Gelung