feat: dynamic issuer (#3481)

* feat: dynamic issuer

* dynamic domain handling

* key rotation durations

* feat: dynamic issuer

* make webauthn displayname dynamic
This commit is contained in:
Livio Amstutz
2022-04-25 10:01:17 +02:00
committed by GitHub
parent 3d5891eb11
commit 75ec73ca4a
41 changed files with 403 additions and 348 deletions

View File

@@ -2,6 +2,7 @@ package http
import (
"net/http"
"strings"
"github.com/gorilla/securecookie"
@@ -20,7 +21,6 @@ type CookieHandler struct {
sameSite http.SameSite
path string
maxAge int
domain string
}
func NewCookieHandler(opts ...CookieHandlerOpt) *CookieHandler {
@@ -76,12 +76,6 @@ func WithMaxAge(maxAge int) CookieHandlerOpt {
}
}
func WithDomain(domain string) CookieHandlerOpt {
return func(c *CookieHandler) {
c.domain = domain
}
}
func SetCookiePrefix(name, domain, path string, secureOnly bool) string {
if !secureOnly {
return name
@@ -101,7 +95,7 @@ func (c *CookieHandler) GetCookieValue(r *http.Request, name string) (string, er
}
func (c *CookieHandler) GetEncryptedCookieValue(r *http.Request, name string, value interface{}) error {
cookie, err := r.Cookie(SetCookiePrefix(name, c.domain, c.path, c.secureOnly))
cookie, err := r.Cookie(SetCookiePrefix(name, r.Host, c.path, c.secureOnly))
if err != nil {
return err
}
@@ -111,11 +105,11 @@ func (c *CookieHandler) GetEncryptedCookieValue(r *http.Request, name string, va
return c.securecookie.Decode(name, cookie.Value, value)
}
func (c *CookieHandler) SetCookie(w http.ResponseWriter, name string, value string) {
c.httpSet(w, name, value, c.maxAge)
func (c *CookieHandler) SetCookie(w http.ResponseWriter, name, domain, value string) {
c.httpSet(w, name, domain, value, c.maxAge)
}
func (c *CookieHandler) SetEncryptedCookie(w http.ResponseWriter, name string, value interface{}) error {
func (c *CookieHandler) SetEncryptedCookie(w http.ResponseWriter, name, domain string, value interface{}) error {
if c.securecookie == nil {
return errors.ThrowInternal(nil, "HTTP-s2HUtx", "securecookie not configured")
}
@@ -123,19 +117,19 @@ func (c *CookieHandler) SetEncryptedCookie(w http.ResponseWriter, name string, v
if err != nil {
return err
}
c.httpSet(w, name, encoded, c.maxAge)
c.httpSet(w, name, domain, encoded, c.maxAge)
return nil
}
func (c *CookieHandler) DeleteCookie(w http.ResponseWriter, name string) {
c.httpSet(w, name, "", -1)
func (c *CookieHandler) DeleteCookie(w http.ResponseWriter, r *http.Request, name string) {
c.httpSet(w, name, r.Host, "", -1)
}
func (c *CookieHandler) httpSet(w http.ResponseWriter, name, value string, maxage int) {
func (c *CookieHandler) httpSet(w http.ResponseWriter, name, domain, value string, maxage int) {
http.SetCookie(w, &http.Cookie{
Name: SetCookiePrefix(name, c.domain, c.path, c.secureOnly),
Name: SetCookiePrefix(name, domain, c.path, c.secureOnly),
Value: value,
Domain: c.domain,
Domain: strings.Split(domain, ":")[0],
Path: c.path,
MaxAge: maxage,
HttpOnly: c.httpOnly,

View File

@@ -34,7 +34,7 @@ var (
}
)
func (csp *CSP) Value(nonce string) string {
func (csp *CSP) Value(nonce string, host string) string {
valuesMap := csp.asMap()
values := make([]string, 0, len(valuesMap))
@@ -43,7 +43,7 @@ func (csp *CSP) Value(nonce string) string {
continue
}
values = append(values, fmt.Sprintf("%v %v", k, v.String(nonce)))
values = append(values, fmt.Sprintf("%v %v", k, v.String(nonce, host)))
}
return strings.Join(values, ";")
@@ -99,24 +99,33 @@ func (srcOpts CSPSourceOptions) AddHost(h ...string) CSPSourceOptions {
return append(srcOpts, h...)
}
func (srcOpts CSPSourceOptions) AddOwnHost() CSPSourceOptions {
return append(srcOpts, placeHolderHost)
}
func (srcOpts CSPSourceOptions) AddScheme(s ...string) CSPSourceOptions {
return srcOpts.add(s, "%v:")
}
func (srcOpts CSPSourceOptions) AddNonce() CSPSourceOptions {
return append(srcOpts, "'nonce-%v'")
return append(srcOpts, fmt.Sprintf("'nonce-%s'", placeHolderNonce))
}
const (
placeHolderNonce = "{{nonce}}"
placeHolderHost = "{{host}}"
)
func (srcOpts CSPSourceOptions) AddHash(alg, b64v string) CSPSourceOptions {
return append(srcOpts, fmt.Sprintf("'%v-%v'", alg, b64v))
}
func (srcOpts CSPSourceOptions) String(nonce string) string {
func (srcOpts CSPSourceOptions) String(nonce, host string) string {
value := strings.Join(srcOpts, " ")
if !strings.Contains(value, "%v") {
if !strings.Contains(value, placeHolderNonce) && !strings.Contains(value, placeHolderHost) {
return value
}
return fmt.Sprintf(value, nonce)
return strings.ReplaceAll(strings.ReplaceAll(value, placeHolderHost, host), placeHolderNonce, nonce)
}
func (srcOpts CSPSourceOptions) add(values []string, format string) CSPSourceOptions {

View File

@@ -63,7 +63,7 @@ func (h *headers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r = saveContext(r, nonceKey, nonce)
}
headers := w.Header()
headers.Set(http_utils.ContentSecurityPolicy, h.csp.Value(nonce))
headers.Set(http_utils.ContentSecurityPolicy, h.csp.Value(nonce, r.Host))
headers.Set(http_utils.XXSSProtection, "1; mode=block")
headers.Set(http_utils.StrictTransportSecurity, "max-age=31536000; includeSubDomains")
headers.Set(http_utils.XFrameOptions, "DENY")

View File

@@ -21,10 +21,6 @@ func UserAgentIDFromCtx(ctx context.Context) (string, bool) {
return userAgentID, ok
}
func InstanceIDFromCtx(ctx context.Context) string {
return "" //TODO: implement
}
type UserAgent struct {
ID string
}
@@ -41,10 +37,9 @@ type UserAgentCookieConfig struct {
MaxAge time.Duration
}
func NewUserAgentHandler(config *UserAgentCookieConfig, cookieKey []byte, domain string, idGenerator id.Generator, externalSecure bool) (func(http.Handler) http.Handler, error) {
func NewUserAgentHandler(config *UserAgentCookieConfig, cookieKey []byte, idGenerator id.Generator, externalSecure bool) (func(http.Handler) http.Handler, error) {
opts := []http_utils.CookieHandlerOpt{
http_utils.WithEncryption(cookieKey, cookieKey),
http_utils.WithDomain(domain),
http_utils.WithMaxAge(int(config.MaxAge.Seconds())),
}
if !externalSecure {
@@ -68,7 +63,7 @@ func (ua *userAgentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err == nil {
ctx := context.WithValue(r.Context(), userAgentKey, agent.ID)
r = r.WithContext(ctx)
ua.setUserAgent(w, agent)
ua.setUserAgent(w, r.Host, agent)
}
ua.nextHandler.ServeHTTP(w, r)
}
@@ -90,8 +85,8 @@ func (ua *userAgentHandler) getUserAgent(r *http.Request) (*UserAgent, error) {
return userAgent, nil
}
func (ua *userAgentHandler) setUserAgent(w http.ResponseWriter, agent *UserAgent) error {
err := ua.cookieHandler.SetEncryptedCookie(w, ua.cookieName, agent)
func (ua *userAgentHandler) setUserAgent(w http.ResponseWriter, host string, agent *UserAgent) error {
err := ua.cookieHandler.SetEncryptedCookie(w, ua.cookieName, host, agent)
if err != nil {
return errors.ThrowPermissionDenied(err, "HTTP-AqgqdA", "cannot set user agent cookie")
}

View File

@@ -32,9 +32,13 @@ func IsOrigin(rawOrigin string) bool {
}
func BuildHTTP(hostname string, externalPort uint16, secure bool) string {
return BuildOrigin(fmt.Sprintf("%s:%d", hostname, externalPort), secure)
}
func BuildOrigin(host string, secure bool) string {
schema := "https"
if !secure {
schema = "http"
}
return fmt.Sprintf("%s://%s:%d", schema, hostname, externalPort)
return fmt.Sprintf("%s://%s", schema, host)
}