diff --git a/CHANGELOG.md b/CHANGELOG.md index 617c18b8..e2cd76e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## v0.4.38 + +FEATURE: Conditionally enable interstitial page based on `User-Agent` prefix list. See the [frontend configuration template](etc/frontend.yml) for details on the new configuration structure (https://github.com/openziti/zrok/issues/715) + +CHANGE: The interstitial configuration has been modified from a simple `interstitial: ` to a richer structure, but the config version has not been incremented; this feature has not been widely adopted yet. See the [frontend configuration template](etc/frontend.yml) for details on the new structure. + ## v0.4.37 FIX: Fix for setting the `zrok_interstitial` cookie on Chrome-based browsers. diff --git a/docs/guides/self-hosting/interstitial-page.md b/docs/guides/self-hosting/interstitial-page.md index 31de6664..182680cd 100644 --- a/docs/guides/self-hosting/interstitial-page.md +++ b/docs/guides/self-hosting/interstitial-page.md @@ -37,18 +37,28 @@ If an account has a row present in this table when creating a share, then the co The frontend configuration controls what the frontend will do with the share config it finds in OpenZiti. The new stanza looks like this: ``` -# Setting the `interstitial` setting to `true` will allow this frontend -# to offer interstitial pages if they are configured on the share by the -# controller. +# Configure interstitial pages for this frontend. The interstitial page presents a warning to internet users, alerting +# them to the fact that they're visiting a zrok share. # -#interstitial: true +#interstitial: +# # Enable or disable interstitial pages on this frontend. +# # +# enabled: true +# +# # Specify a list of User-Agent prefixes that should receive the interstitial page. If interstitial pages are enabled +# # and this list is not set, all user agents will receive an interstitial page. +# # +# user_agent_prefixes: +# - "Mozilla/5.0" ``` -Simply setting `interstitial: true` in the frontend config will allow the configured frontend to offer an interstitial page if the share config enables the interstitial page for that share. +Setting `enabled: true` in the `interstitial` stanza of the frontend config will allow the configured frontend to offer an interstitial page if the share config enables the interstitial page for that share. The `user_agent_prefixes` array can be used to specify which specific `User-Agent` types receive the interstitial. User agents that match a prefix in the list will receive the interstitial, while others will not. If the `user_agent_prefixes` list is omitted, _all_ user agents will receive the interstitial page. ## Bypassing the Interstitial -The interstitial page will be presented unless the client shows up with a `zrok_interstitial` cookie. When the user is presented with the interstitial page, there is a button they can click which sets the necessary cookie and allows them to visit the site. The cookie is set to expire in one week. +The interstitial page will be presented unless the client shows up with a `zrok_interstitial` cookie (depending on `user_agent_prefixes` configuration). When the user is presented with the interstitial page, there is a button they can click which sets the necessary cookie and allows them to visit the site. The cookie is set to expire in one week. + +Typically the `user_agent_prefixes` list contains `Mozilla/5.0`, which matches all typical interactive mobile and desktop browsers. Setting a non-standard `User-Agent` in an interactive browser will bypass the interstitial pages for frontends configured with the usual `Mozilla/5.0` prefix. End users can offer an HTTP header of `skip_zrok_interstitial`, set to any value to bypass the interstitial page. Setting this header means that the user most likely understands what a zrok share is and will hopefully not fall for a phishing attack. diff --git a/endpoints/publicProxy/config.go b/endpoints/publicProxy/config.go index cac3da35..233c1a4e 100644 --- a/endpoints/publicProxy/config.go +++ b/endpoints/publicProxy/config.go @@ -16,11 +16,16 @@ type Config struct { Identity string Address string HostMatch string - Interstitial bool + Interstitial *InterstitialConfig Oauth *OauthConfig Tls *endpoints.TlsConfig } +type InterstitialConfig struct { + Enabled bool + UserAgentPrefixes []string +} + type OauthConfig struct { BindAddress string RedirectUrl string @@ -46,9 +51,8 @@ type OauthProviderConfig struct { func DefaultConfig() *Config { return &Config{ - Identity: "public", - Address: "0.0.0.0:8080", - Interstitial: false, + Identity: "public", + Address: "0.0.0.0:8080", } } diff --git a/endpoints/publicProxy/http.go b/endpoints/publicProxy/http.go index 5e3ba4e8..69de6330 100644 --- a/endpoints/publicProxy/http.go +++ b/endpoints/publicProxy/http.go @@ -158,15 +158,31 @@ func shareHandler(handler http.Handler, pcfg *Config, key []byte, ctx ziti.Conte if shrToken != "" { if svc, found := endpoints.GetRefreshedService(shrToken, ctx); found { if cfg, found := svc.Config[sdk.ZrokProxyConfig]; found { - if pcfg.Interstitial { - if v, istlFound := cfg["interstitial"]; istlFound { - if istlEnabled, ok := v.(bool); ok && istlEnabled { - skip := r.Header.Get("skip_zrok_interstitial") - _, zrokOkErr := r.Cookie("zrok_interstitial") - if skip == "" && zrokOkErr != nil { - logrus.Debugf("forcing interstitial for '%v'", r.URL) - interstitialUi.WriteInterstitialAnnounce(w) - return + if pcfg.Interstitial != nil && pcfg.Interstitial.Enabled { + sendInterstitial := true + if len(pcfg.Interstitial.UserAgentPrefixes) > 0 { + ua := r.Header.Get("User-Agent") + matched := false + for _, prefix := range pcfg.Interstitial.UserAgentPrefixes { + if strings.HasPrefix(ua, prefix) { + matched = true + break + } + } + if !matched { + sendInterstitial = false + } + } + if sendInterstitial { + if v, istlFound := cfg["interstitial"]; istlFound { + if istlEnabled, ok := v.(bool); ok && istlEnabled { + skip := r.Header.Get("skip_zrok_interstitial") + _, zrokOkErr := r.Cookie("zrok_interstitial") + if skip == "" && zrokOkErr != nil { + logrus.Debugf("forcing interstitial for '%v'", r.URL) + interstitialUi.WriteInterstitialAnnounce(w) + return + } } } } diff --git a/etc/frontend.yml b/etc/frontend.yml index 7ea7e861..ecf723a9 100644 --- a/etc/frontend.yml +++ b/etc/frontend.yml @@ -10,10 +10,19 @@ v: 3 # #host_match: zrok.io -# Setting the `interstitial` setting to `true` will allow this frontend to offer interstitial pages if they are -# configured on the share by the controller. +# Configure interstitial pages for this frontend. The interstitial page presents a warning to internet users, alerting +# them to the fact that they're visiting a zrok share. # -#interstitial: true +#interstitial: +# # Enable or disable interstitial pages on this frontend. +# # +# enabled: true +# +# # Specify a list of User-Agent prefixes that should receive the interstitial page. If interstitial pages are enabled +# # and this list is not set, all user agents will receive an interstitial page. +# # +# user_agent_prefixes: +# - "Mozilla/5.0" # The OAuth configuration is used when enabling OAuth authentication with your public frontend. #