proxy: fix invalid session after logout in forward auth mode (#1062)

Currently, authorize service does handle unauthenticated request in
forward auth mode, and return status 401.

But proxy has not handled the response yet, and always returns 403 for
both unauthenticated and unauthorized request. That breaks session
handling in forward auth mode. That said, if user was signed out, or for
any reason, authorize service return 401 status, proxy does not redirect
user to re-signin, but always return 403.

To fix it, proxy is changed to handle envoy check response in more
details, to distinguish between 401 and 403 status.

Thanks to @simbaja for rasing the problem and come up with original fix.

Fixes #1014
Fixes #858
This commit is contained in:
Cuong Manh Le 2020-07-14 01:07:49 +07:00 committed by GitHub
parent 7437a4967d
commit 58fb6ea3c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 34 deletions

View file

@ -18,6 +18,11 @@ import (
"github.com/pomerium/pomerium/internal/urlutil"
)
type authorizeResponse struct {
authorized bool
statusCode int32
}
// AuthenticateSession is middleware to enforce a valid authentication
// session state is retrieved from the users's request context.
func (p *Proxy) AuthenticateSession(next http.Handler) http.Handler {
@ -45,10 +50,10 @@ func (p *Proxy) redirectToSignin(w http.ResponseWriter, r *http.Request) error {
return nil
}
func (p *Proxy) isAuthorized(w http.ResponseWriter, r *http.Request) (bool, error) {
func (p *Proxy) isAuthorized(w http.ResponseWriter, r *http.Request) (*authorizeResponse, error) {
tm, err := ptypes.TimestampProto(time.Now())
if err != nil {
return false, httputil.NewError(http.StatusInternalServerError, fmt.Errorf("error creating protobuf timestamp from current time: %w", err))
return nil, httputil.NewError(http.StatusInternalServerError, fmt.Errorf("error creating protobuf timestamp from current time: %w", err))
}
httpAttrs := &envoy_service_auth_v2.AttributeContext_HttpRequest{
@ -76,18 +81,23 @@ func (p *Proxy) isAuthorized(w http.ResponseWriter, r *http.Request) (bool, erro
},
})
if err != nil {
return false, httputil.NewError(http.StatusInternalServerError, err)
return nil, httputil.NewError(http.StatusInternalServerError, err)
}
ar := &authorizeResponse{}
switch res.HttpResponse.(type) {
case *envoy_service_auth_v2.CheckResponse_OkResponse:
for _, hdr := range res.GetOkResponse().GetHeaders() {
w.Header().Set(hdr.GetHeader().GetKey(), hdr.GetHeader().GetValue())
}
return true, nil
ar.authorized = true
ar.statusCode = res.GetStatus().Code
case *envoy_service_auth_v2.CheckResponse_DeniedResponse:
ar.statusCode = int32(res.GetDeniedResponse().GetStatus().Code)
default:
return false, nil
ar.statusCode = http.StatusInternalServerError
}
return ar, nil
}
// SetResponseHeaders sets a map of response headers.