Files
teleport/lib/utils/replace.go
T
Sasha Klizhentas 045490de25 External traits in node labels and regexp role map
This commit adds two extensions to template variables
in roles and adds support for regular expressions
and group captures in role mapping of trusted clusters.

1. Roles node_labels can expand variables from traits:

allow:
  node_labels:
    '{{external.key}}': '{{external.val}}'
deny:
  node_labels:
    '{{external.key}}': '{{external.val}}'

If traits variable is not found, label key pair in allow or
deny rule will be set to empty key or value, so if 'external.val'
trait is missing, the resulting role will not match
allow or deny rule:

allow:
  node_labels:
    '': 'val'
deny:
  node_labels:
    '': 'val'

Same thing will happen for missing value:

allow:
  node_labels:
    'key': ''
deny:
  node_labels:
    'key': ''

2. Trusted cluster role mapping can now
support advanced expressions:

a. Glob values will math any string, including
empty one

   role_map:
   - remote: 'cluster-*'
     local: [clusteradmin]

a. Regular expression syntax is supported:

Syntax: https://github.com/google/re2/wiki/Syntax

Brackets can be used as a capture group and referred
to with expand variable:

   role_map:
   - remote: '^clusteradmin-(.*)$'
     local: [unprivileged-$1]

Will map incoming role 'clusteradmin-account-1' to 'guest-account-1'.

3. Same regular expression syntax is supported for SAML and OIDC
mappings:

a. Glob matches of values instead of static matches:

  claims_to_roles:
      - {claim: "roles", value: "gravitational/*", roles: ["clusteradmin"]}

b. Regexp matches with subgroup expands:

  attributes_to_roles:
      - {name: "roles", value: "^gravitational/(.*)$", roles: ["cluster-$1"]}
2018-07-02 16:13:12 -07:00

51 lines
1.8 KiB
Go

package utils
import (
"regexp"
"strings"
"github.com/gravitational/trace"
)
// ContainsExpansion returns true if value contains
// expansion syntax, e.g. $1 or ${10}
func ContainsExpansion(val string) bool {
return reExpansion.FindAllStringIndex(val, -1) != nil
}
// GlobToRegexp replaces glob-style standalone wildcard values
// with real .* regexp-friendly values, does not modify regexp-compatible values,
// quotes non-wildcard values
func GlobToRegexp(in string) string {
return replaceWildcard.ReplaceAllString(regexp.QuoteMeta(in), "(.*)")
}
// ReplaceRegexp replaces value in string, accepts regular expression and simplified
// wildcard syntax, it has several important differeneces with standard lib
// regexp replacer:
// * Wildcard globs '*' are treated as regular expression .* expression
// * Expression is treated as regular expression if it starts with ^ and ends with $
// * Full match is expected, partial replacements ignored
// * If there is no match, returns not found error
func ReplaceRegexp(expression string, replaceWith string, input string) (string, error) {
if !strings.HasPrefix(expression, "^") || !strings.HasSuffix(expression, "$") {
// replace glob-style wildcards with regexp wildcards
// for plain strings, and quote all characters that could
// be interpreted in regular expression
expression = "^" + GlobToRegexp(expression) + "$"
}
expr, err := regexp.Compile(expression)
if err != nil {
return "", trace.BadParameter(err.Error())
}
// if there is no match, return NotFound error
index := expr.FindAllStringIndex(input, -1)
if len(index) == 0 {
return "", trace.NotFound("no match found")
}
return expr.ReplaceAllString(input, replaceWith), nil
}
var replaceWildcard = regexp.MustCompile(`(\\\*)`)
var reExpansion = regexp.MustCompile(`\$[^\$]+`)