Gunakan RBAC untuk melindungi perkhidmatan gRPC anda tepat pada definisi proto

Mary-Kate Olsen
Lepaskan: 2024-10-16 06:08:31
asal
967 orang telah melayarinya

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);
}
Salin selepas log masuk

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);
}
Salin selepas log masuk

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
}
Salin selepas log masuk

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
    };
  }
}
Salin selepas log masuk

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")
    }
Salin selepas log masuk

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")
    }
Salin selepas log masuk

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
}
Salin selepas log masuk

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
Salin selepas log masuk

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!

sumber:dev.to
Kenyataan Laman Web ini
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 terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!