cluster/admitomatic: Regexp-based admission rules
Change-Id: Ic2b1d6a952dc194c0ee2fa1673ceb91c43799308
Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1723
Reviewed-by: q3k <q3k@hackerspace.pl>
diff --git a/cluster/admitomatic/ingress.go b/cluster/admitomatic/ingress.go
index b7bdf91..390a160 100644
--- a/cluster/admitomatic/ingress.go
+++ b/cluster/admitomatic/ingress.go
@@ -4,6 +4,7 @@
"encoding/json"
"fmt"
"strings"
+ "regexp"
"github.com/golang/glog"
admission "k8s.io/api/admission/v1beta1"
@@ -37,6 +38,9 @@
// allowed is a map from namespace to list of domain matchers.
allowed map[string][]*domain
+ // allowedRegexp is a list of domain regexps and their allowed namespaces
+ allowedRegexp []*regexpFilter
+
// anythingGoesNamespaces are namespaces that are opted out of security
// checks.
anythingGoesNamespaces []string
@@ -53,6 +57,11 @@
wildcard bool
}
+type regexpFilter struct {
+ namespace string
+ dns *regexp.Regexp
+}
+
// match returns whether this matcher matches a given domain.
func (d *domain) match(dns string) bool {
if !d.wildcard {
@@ -63,7 +72,7 @@
// allow adds a given (namespace, dns) pair to the filter. The dns variable is
// a string that is either a simple domain name, or a wildcard like
-// *.foo.example.com. An error is returned if the dns stirng could not be
+// *.foo.example.com. An error is returned if the dns string could not be
// parsed.
func (i *ingressFilter) allow(ns, dns string) error {
// If the filter is brand new, initialize it.
@@ -97,6 +106,22 @@
return nil
}
+func (i *ingressFilter) allowRegexp(ns string, dns string) error {
+ // Parse dns as a regexp
+ dnsPattern := "^" + dns + "$"
+ re, err := regexp.Compile(dnsPattern)
+ if err != nil {
+ return err
+ }
+
+ i.allowedRegexp = append(i.allowedRegexp, ®expFilter{
+ namespace: ns,
+ dns: re,
+ })
+
+ return nil
+}
+
// domainAllowed returns whether a given domain is allowed to be backed by an
// ingress within a given namespace.
func (i *ingressFilter) domainAllowed(ns, domain string) bool {
@@ -125,6 +150,24 @@
if domainFound {
return false
}
+
+ // Check regexp matching
+ for _, filter := range i.allowedRegexp {
+ re := filter.dns
+ allowedNs := filter.namespace
+
+ submatches := re.FindStringSubmatchIndex(domain)
+ if submatches == nil {
+ continue
+ }
+
+ // Domain matched, expand allowed namespace template
+ expectedNs := []byte{}
+ expectedNs = re.ExpandString(expectedNs, allowedNs, domain, submatches)
+ didMatch := string(expectedNs) == ns
+ return didMatch
+ }
+
// No direct match found, and this domain is not restricted. Allow.
return true
}