internal/sessions: allow manual session scope

This commit is contained in:
Bobby DeSimone 2019-07-05 12:32:35 -07:00
parent 79f96eab52
commit 437dee0315
No known key found for this signature in database
GPG key ID: AEE4CF12FE86D07E
4 changed files with 37 additions and 31 deletions

View file

@ -18,6 +18,7 @@
- Fixed Azure group lookups [GH-190] - Fixed Azure group lookups [GH-190]
- If a session is too large (over 4096 bytes) Pomerium will no longer fail silently. [GH-211] - If a session is too large (over 4096 bytes) Pomerium will no longer fail silently. [GH-211]
- Internal URLs like dashboard now start auth process to login a user if no session is found [GH-205]. - Internal URLs like dashboard now start auth process to login a user if no session is found [GH-205].
- When set,`CookieDomain` lets a user set the scope of the user session. CSRF cookies will still always be scoped at the individual route level. [GH-181]
## v0.0.5 ## v0.0.5

View file

@ -66,6 +66,7 @@ func New(opts config.Options) (*Authenticate, error) {
cookieStore, err := sessions.NewCookieStore( cookieStore, err := sessions.NewCookieStore(
&sessions.CookieStoreOptions{ &sessions.CookieStoreOptions{
Name: opts.CookieName, Name: opts.CookieName,
CookieDomain: opts.CookieDomain,
CookieSecure: opts.CookieSecure, CookieSecure: opts.CookieSecure,
CookieHTTPOnly: opts.CookieHTTPOnly, CookieHTTPOnly: opts.CookieHTTPOnly,
CookieExpire: opts.CookieExpire, CookieExpire: opts.CookieExpire,

View file

@ -68,20 +68,19 @@ func NewCookieStore(opts *CookieStoreOptions) (*CookieStore, error) {
} }
func (s *CookieStore) makeCookie(req *http.Request, name string, value string, expiration time.Duration, now time.Time) *http.Cookie { func (s *CookieStore) makeCookie(req *http.Request, name string, value string, expiration time.Duration, now time.Time) *http.Cookie {
// if csrf, scope cookie to the route or service specific domain
domain := req.Host domain := req.Host
if h, _, err := net.SplitHostPort(domain); err == nil {
domain = h
}
if s.CookieDomain != "" {
domain = s.CookieDomain
}
// Non-CSRF sessions can shared, and set domain-wide if name == s.csrfName() {
if !strings.Contains(name, "csrf") { domain = req.Host
} else if s.CookieDomain != "" {
domain = s.CookieDomain
} else {
domain = splitDomain(domain) domain = splitDomain(domain)
} }
if h, _, err := net.SplitHostPort(domain); err == nil {
domain = h
}
c := &http.Cookie{ c := &http.Cookie{
Name: name, Name: name,
Value: value, Value: value,

View file

@ -8,6 +8,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/google/go-cmp/cmp"
"github.com/pomerium/pomerium/internal/cryptutil" "github.com/pomerium/pomerium/internal/cryptutil"
) )
@ -110,35 +111,39 @@ func TestCookieStore_makeCookie(t *testing.T) {
name string name string
domain string domain string
cookieName string cookieDomain string
value string cookieName string
expiration time.Duration value string
want *http.Cookie expiration time.Duration
wantCSRF *http.Cookie want *http.Cookie
wantCSRF *http.Cookie
}{ }{
{"good", "http://httpbin.corp.pomerium.io", "_pomerium", "value", 0, &http.Cookie{Name: "_pomerium", Value: "value", Path: "/", Domain: "corp.pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}}, {"good", "http://httpbin.corp.pomerium.io", "", "_pomerium", "value", 0, &http.Cookie{Name: "_pomerium", Value: "value", Path: "/", Domain: "corp.pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}},
{"domains with https", "https://httpbin.corp.pomerium.io", "_pomerium", "value", 0, &http.Cookie{Name: "_pomerium", Value: "value", Path: "/", Domain: "corp.pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}}, {"domains with https", "https://httpbin.corp.pomerium.io", "", "_pomerium", "value", 0, &http.Cookie{Name: "_pomerium", Value: "value", Path: "/", Domain: "corp.pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}},
{"domain with port", "http://httpbin.corp.pomerium.io:443", "_pomerium", "value", 0, &http.Cookie{Name: "_pomerium", Value: "value", Path: "/", Domain: "corp.pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}}, {"domain with port", "http://httpbin.corp.pomerium.io:443", "", "_pomerium", "value", 0, &http.Cookie{Name: "_pomerium", Value: "value", Path: "/", Domain: "corp.pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}},
{"expiration set", "http://httpbin.corp.pomerium.io:443", "_pomerium", "value", 10 * time.Second, &http.Cookie{Expires: now.Add(10 * time.Second), Name: "_pomerium", Value: "value", Path: "/", Domain: "corp.pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Expires: now.Add(10 * time.Second), Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}}, {"expiration set", "http://httpbin.corp.pomerium.io:443", "", "_pomerium", "value", 10 * time.Second, &http.Cookie{Expires: now.Add(10 * time.Second), Name: "_pomerium", Value: "value", Path: "/", Domain: "corp.pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Expires: now.Add(10 * time.Second), Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}},
{"good", "http://httpbin.corp.pomerium.io", "pomerium.io", "_pomerium", "value", 0, &http.Cookie{Name: "_pomerium", Value: "value", Path: "/", Domain: "pomerium.io", Secure: true, HttpOnly: true}, &http.Cookie{Name: "_pomerium_csrf", Value: "value", Path: "/", Domain: "httpbin.corp.pomerium.io", Secure: true, HttpOnly: true}},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
r := httptest.NewRequest("GET", tt.domain, nil) r := httptest.NewRequest("GET", tt.domain, nil)
o := &CookieStoreOptions{ s, err := NewCookieStore(
Name: "_pomerium", &CookieStoreOptions{
CookieSecure: true, Name: "_pomerium",
CookieHTTPOnly: true, CookieSecure: true,
CookieDomain: "httpbin.corp.pomerium.io", CookieHTTPOnly: true,
CookieExpire: 10 * time.Second, CookieDomain: tt.cookieDomain,
CookieCipher: cipher} CookieExpire: 10 * time.Second,
CookieCipher: cipher})
s, _ := NewCookieStore(o) if err != nil {
if got := s.makeCookie(r, tt.cookieName, tt.value, tt.expiration, now); !reflect.DeepEqual(got, tt.want) { t.Fatal(err)
t.Errorf("CookieStore.makeCookie() = \n%#v, \nwant\n%#v", got, tt.want)
} }
if got := s.makeSessionCookie(r, tt.value, tt.expiration, now); !reflect.DeepEqual(got, tt.want) { if diff := cmp.Diff(s.makeCookie(r, tt.cookieName, tt.value, tt.expiration, now), tt.want); diff != "" {
t.Errorf("CookieStore.makeCookie() = \n%#v, \nwant\n%#v", got, tt.want) t.Errorf("CookieStore.makeCookie() = \n%s", diff)
}
if diff := cmp.Diff(s.makeSessionCookie(r, tt.value, tt.expiration, now), tt.want); diff != "" {
t.Errorf("CookieStore.makeSessionCookie() = \n%s", diff)
} }
got := s.makeCSRFCookie(r, tt.value, tt.expiration, now) got := s.makeCSRFCookie(r, tt.value, tt.expiration, now)
tt.wantCSRF.Name = "_pomerium_csrf" tt.wantCSRF.Name = "_pomerium_csrf"