Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Token Of Respect: Implementing Microservice Identity And Access Management In Go

A Token Of Respect: Implementing Microservice Identity And Access Management In Go

How do you balance robust access management features with the distributed nature of a microservices ecosystem? This talk will walk you through how we built our IAM system at Manifold, explore the tradeoffs we made, and look at the features of Go that helped in our implementation.

James Bowes

May 31, 2019
Tweet

More Decks by James Bowes

Other Decks in Technology

Transcript

  1. @jrbowes // Subject of role-based access control, eg a user

    type Subject interface { Name() string }
  2. @jrbowes // Subject of role-based access control, eg a user

    type Subject interface { Name() string Roles() []Role } // Role provides a list of permissions type Role struct { Label string Permissions []Permission }
  3. @jrbowes // Subject of role-based access control, eg a user

    type Subject interface { Name() string Roles() []Role } // Role provides a list of permissions type Role struct { Label string Permissions []Permission } // Permission can be many things. // type Permission struct { }
  4. @jrbowes // Subject of role-based access control, eg a user

    type Subject interface { Name() string Roles() []Role } // Role provides a list of permissions type Role struct { Label string Permissions []Permission } // Permission can be many things. For our use we need a verb and a // target to perform that verb on. type Permission struct { Verb Verb TargetID ID }
  5. @jrbowes // Verb is a action that is encapsulated in

    a permission, and is // requested of the RBAC system to check a if a subject is permitted // to perform an action. type Verb uint64
  6. @jrbowes // Verb is a action that is encapsulated in

    a permission, and is // requested of the RBAC system to check a if a subject is permitted // to perform an action. type Verb uint64 // All possible permission verbs // // // // const ( TeamCreate Verb TeamRead TeamUpdate TeamInvite TeamRemove TeamDelete // ... )
  7. @jrbowes // Verb is a action that is encapsulated in

    a permission, and is // requested of the RBAC system to check a if a subject is permitted // to perform an action. type Verb uint64 // All possible permission verbs // // // // const ( TeamCreate Verb = 1 << iota TeamRead TeamUpdate TeamInvite TeamRemove TeamDelete // ... )
  8. @jrbowes // Verb is a action that is encapsulated in

    a permission, and is // requested of the RBAC system to check a if a subject is permitted // to perform an action. type Verb uint64 // All possible permission verbs // // // // const ( TeamCreate Verb = 1 << iota // 00...000001 TeamRead // 00...000010 TeamUpdate // 00...000100 TeamInvite // 00...001000 TeamRemove // 00...010000 TeamDelete // 00...100000 // ... )
  9. @jrbowes // Verb is a action that is encapsulated in

    a permission, and is // requested of the RBAC system to check a if a subject is permitted // to perform an action. type Verb uint64 // 64 possible permissions // All possible permission verbs // // IMPORTANT: maintain the order in this list, or else the bits // change, and a principal's permissions could be interpreted // differently between our services. const ( TeamCreate Verb = 1 << iota // 00...000001 TeamRead // 00...000010 TeamUpdate // 00...000100 TeamInvite // 00...001000 TeamRemove // 00...010000 TeamDelete // 00...100000 // ... )
  10. @jrbowes // MarshalText marshals a Verb to a string, for

    use in JSON. Verbs are // stored in raw base64 url (no trailing ==), with leading zero bytes // removed. This allows us to both store sparse Verbs efficiently, and // have forwards compatibility as additional verbs are added, growing // the bit length. func (v Verb) MarshalText() ([]byte, error) { b := make([]byte, 8) binary.BigEndian.PutUint64(b, uint64(v)) for len(b) > 0 && b[0] == 0x00 { b = b[1:] } o := make([]byte, base64.RawURLEncoding.EncodedLen(len(b))) base64.RawURLEncoding.Encode(o, b) return o, nil }
  11. @jrbowes // UnmarshalText unmarshals a string into a Verb, for

    use from JSON. func (v *Verb) UnmarshalText(t []byte) error { i := make([]byte, base64.RawURLEncoding.DecodedLen(len(t))) base64.RawURLEncoding.Decode(i, t) // forwards compat if len(i) > 8 { i = i[len(i)-8:] } b := make([]byte, 8) copy(b[8-len(i):], i) *v = Verb(binary.BigEndian.Uint64(b)) return nil }
  12. @jrbowes func (s *JWTSubject) Can(v Verb, target ID) bool {

    return v&s.perms[ID] > 0 } v&s.perms[ID] return v&s.perms[ID] > 0
  13. @jrbowes // anonymousSubject is the type representing anonymous // (not

    authenticated) users. It is never verified, and has // no permissions. type anonymousSubject struct{} func (anonymousSubject) Verified() bool { return false } func (anonymousSubject) Can(rbac.Verb, ...ID) bool { return false } // ... // make sure the interface is satisfied at compile time var _ Subject = anonymousSubject // anonymousSubject is the type representing anonymous // (not authenticated) users. It is never verified, and has // no permissions. type anonymousSubject struct{} func (anonymousSubject) Verified() bool { return false } func (anonymousSubject) Can(rbac.Verb, ...ID) bool { return false }