Skip to content

Commit 70f2f5d

Browse files
newellz2kradalby
authored andcommitted
Added an OIDC AllowGroups option for authorization.
1 parent 4453728 commit 70f2f5d

4 files changed

Lines changed: 44 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 0.18.x (2022-xx-xx)
44

5+
- Added an OIDC AllowGroups Configuration options and authorization check [#1041](https://github.com/juanfont/headscale/pull/1041)
56
- Reworked routing and added support for subnet router failover [#1024](https://github.com/juanfont/headscale/pull/1024)
67

78
### Changes

config-example.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ unix_socket_permission: "0770"
273273
#
274274
# allowed_domains:
275275
# - example.com
276+
# Groups from keycloak have a leading '/'
277+
# allowed_groups:
278+
# - /headscale
276279
# allowed_users:
277280
# - alice@example.com
278281
#

config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ type OIDCConfig struct {
9696
ExtraParams map[string]string
9797
AllowedDomains []string
9898
AllowedUsers []string
99+
AllowedGroups []string
99100
StripEmaildomain bool
100101
}
101102

@@ -568,6 +569,7 @@ func GetHeadscaleConfig() (*Config, error) {
568569
ExtraParams: viper.GetStringMapString("oidc.extra_params"),
569570
AllowedDomains: viper.GetStringSlice("oidc.allowed_domains"),
570571
AllowedUsers: viper.GetStringSlice("oidc.allowed_users"),
572+
AllowedGroups: viper.GetStringSlice("oidc.allowed_groups"),
571573
StripEmaildomain: viper.GetBool("oidc.strip_email_domain"),
572574
},
573575

oidc.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const (
2525
errEmptyOIDCCallbackParams = Error("empty OIDC callback params")
2626
errNoOIDCIDToken = Error("could not extract ID Token for OIDC callback")
2727
errOIDCAllowedDomains = Error("authenticated principal does not match any allowed domain")
28+
errOIDCAllowedGroups = Error("authenticated principal is not in any allowed group")
2829
errOIDCAllowedUsers = Error("authenticated principal does not match any allowed user")
2930
errOIDCInvalidMachineState = Error("requested machine state key expired before authorisation completed")
3031
errOIDCNodeKeyMissing = Error("could not get node key from cache")
@@ -209,6 +210,10 @@ func (h *Headscale) OIDCCallback(
209210
return
210211
}
211212

213+
if err := validateOIDCAllowedGroups(writer, h.cfg.OIDC.AllowedGroups, claims); err != nil {
214+
return
215+
}
216+
212217
if err := validateOIDCAllowedUsers(writer, h.cfg.OIDC.AllowedUsers, claims); err != nil {
213218
return
214219
}
@@ -404,6 +409,39 @@ func validateOIDCAllowedDomains(
404409
return nil
405410
}
406411

412+
// validateOIDCAllowedGroups checks if AllowedGroups is provided,
413+
// and that the user has one group in the list.
414+
// claims.Groups can be populated by adding a client scope named
415+
// 'groups' that contains group membership.
416+
func validateOIDCAllowedGroups(
417+
writer http.ResponseWriter,
418+
allowedGroups []string,
419+
claims *IDTokenClaims,
420+
) error {
421+
if len(allowedGroups) > 0 {
422+
for _, group := range allowedGroups {
423+
if IsStringInSlice(claims.Groups, group) {
424+
return nil
425+
}
426+
}
427+
428+
log.Error().Msg("authenticated principal not in any allowed groups")
429+
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
430+
writer.WriteHeader(http.StatusBadRequest)
431+
_, err := writer.Write([]byte("unauthorized principal (allowed groups)"))
432+
if err != nil {
433+
log.Error().
434+
Caller().
435+
Err(err).
436+
Msg("Failed to write response")
437+
}
438+
439+
return errOIDCAllowedGroups
440+
}
441+
442+
return nil
443+
}
444+
407445
// validateOIDCAllowedUsers checks that if AllowedUsers is provided,
408446
// that the authenticated principal is part of that list.
409447
func validateOIDCAllowedUsers(

0 commit comments

Comments
 (0)