mirror of
https://github.com/openziti/zrok.git
synced 2024-12-22 14:50:55 +01:00
Merge pull request #705 from openziti/interstitial
Interstitial Pages Implementation (#704)
This commit is contained in:
commit
73c46759d7
@ -2,6 +2,8 @@
|
||||
|
||||
## v0.4.36
|
||||
|
||||
FEATURE: New interstitial pages that can be enabled per-frontend, and disabled per-account (https://github.com/openziti/zrok/issues/704)
|
||||
|
||||
CHANGE: Enable `"declaration": true` in `tsconfig.json` for Node SDK.
|
||||
|
||||
## v0.4.35
|
||||
|
@ -133,7 +133,17 @@ func (h *shareHandler) Handle(params share.ShareParams, principal *rest_model_zr
|
||||
logrus.Infof("added frontend selection '%v' with ziti identity '%v' for share '%v'", frontendSelection, sfe.ZId, shrToken)
|
||||
}
|
||||
}
|
||||
shrZId, frontendEndpoints, err = newPublicResourceAllocator().allocate(envZId, shrToken, frontendZIds, frontendTemplates, params, edge)
|
||||
var skipInterstitial bool
|
||||
if backendMode != sdk.DriveBackendMode {
|
||||
skipInterstitial, err = str.IsAccountGrantedSkipInterstitial(int(principal.ID), trx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error checking skip interstitial for account '%v': %v", principal.Email, err)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
} else {
|
||||
skipInterstitial = true
|
||||
}
|
||||
shrZId, frontendEndpoints, err = newPublicResourceAllocator().allocate(envZId, shrToken, frontendZIds, frontendTemplates, params, !skipInterstitial, edge)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewShareInternalServerError()
|
||||
|
@ -13,7 +13,7 @@ func newPublicResourceAllocator() *publicResourceAllocator {
|
||||
return &publicResourceAllocator{}
|
||||
}
|
||||
|
||||
func (a *publicResourceAllocator) allocate(envZId, shrToken string, frontendZIds, frontendTemplates []string, params share.ShareParams, edge *rest_management_api_client.ZitiEdgeManagement) (shrZId string, frontendEndpoints []string, err error) {
|
||||
func (a *publicResourceAllocator) allocate(envZId, shrToken string, frontendZIds, frontendTemplates []string, params share.ShareParams, interstitial bool, edge *rest_management_api_client.ZitiEdgeManagement) (shrZId string, frontendEndpoints []string, err error) {
|
||||
var authUsers []*sdk.AuthUserConfig
|
||||
for _, authUser := range params.Body.AuthUsers {
|
||||
authUsers = append(authUsers, &sdk.AuthUserConfig{Username: authUser.Username, Password: authUser.Password})
|
||||
@ -23,6 +23,7 @@ func (a *publicResourceAllocator) allocate(envZId, shrToken string, frontendZIds
|
||||
return "", nil, err
|
||||
}
|
||||
options := &zrokEdgeSdk.FrontendOptions{
|
||||
Interstitial: interstitial,
|
||||
AuthScheme: authScheme,
|
||||
BasicAuthUsers: authUsers,
|
||||
Oauth: &sdk.OauthConfig{
|
||||
|
18
controller/store/skipInterstitialGrant.go
Normal file
18
controller/store/skipInterstitialGrant.go
Normal file
@ -0,0 +1,18 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (str *Store) IsAccountGrantedSkipInterstitial(acctId int, trx *sqlx.Tx) (bool, error) {
|
||||
stmt, err := trx.Prepare("select count(0) from skip_interstitial_grants where account_id = $1")
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "error preparing skip_interstitial_grants select statement")
|
||||
}
|
||||
var count int
|
||||
if err := stmt.QueryRow(acctId).Scan(&count); err != nil {
|
||||
return false, errors.Wrap(err, "error querying skip_interstitial_grants count")
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
-- +migrate Up
|
||||
|
||||
create table skip_interstitial_grants (
|
||||
id serial primary key,
|
||||
|
||||
account_id integer references accounts (id) not null,
|
||||
|
||||
created_at timestamptz not null default(current_timestamp),
|
||||
updated_at timestamptz not null default(current_timestamp),
|
||||
deleted boolean not null default(false)
|
||||
);
|
||||
|
||||
create index skip_interstitial_grants_id_idx on skip_interstitial_grants (account_id);
|
@ -0,0 +1,13 @@
|
||||
-- +migrate Up
|
||||
|
||||
create table skip_interstitial_grants (
|
||||
id integer primary key,
|
||||
|
||||
account_id integer references accounts (id) not null,
|
||||
|
||||
created_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
updated_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
deleted boolean not null default(false)
|
||||
);
|
||||
|
||||
create index skip_interstitial_grants_id_idx on skip_interstitial_grants (account_id);
|
@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type FrontendOptions struct {
|
||||
Interstitial bool
|
||||
AuthScheme sdk.AuthScheme
|
||||
BasicAuthUsers []*sdk.AuthUserConfig
|
||||
Oauth *sdk.OauthConfig
|
||||
@ -19,6 +20,7 @@ type FrontendOptions struct {
|
||||
|
||||
func CreateConfig(cfgTypeZId, envZId, shrToken string, options *FrontendOptions, edge *rest_management_api_client.ZitiEdgeManagement) (cfgZId string, err error) {
|
||||
cfg := &sdk.FrontendConfig{
|
||||
Interstitial: options.Interstitial,
|
||||
AuthScheme: options.AuthScheme,
|
||||
}
|
||||
if cfg.AuthScheme == sdk.Basic {
|
||||
|
57
docs/guides/self-hosting/interstitial-page.md
Normal file
57
docs/guides/self-hosting/interstitial-page.md
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
title: Interstitial Pages
|
||||
sidebar_label: Interstitial Pages
|
||||
sidebar_position: 18
|
||||
---
|
||||
|
||||
On large zrok installations that support open registration and shared public frontends, abuse can become an issue. In order to mitigate phishing and other similar forms of abuse, zrok offers an interstitial page that announces to the visiting user that the share is hosted through zrok, and probably isn't their financial institution.
|
||||
|
||||
Interstitial pages can be enabled on a per-frontend basis. This allows the interstitial to be enabled on open public frontends but not closed public frontends (closed public frontends require a grant to use).
|
||||
|
||||
The interstitial page requirement can also be overridden on a per-account basis, allowing shares created by specific accounts to bypass the interstitial requirement on frontends that enable it. This facilitates building infrastructure that grants trusted users additional privileges.
|
||||
|
||||
By default, if you do not specifically enable interstitial pages on a public frontend, then your self-hosted service instance will not offer them.
|
||||
|
||||
Let's take a look at how the interstitial pages mechanism works. The following diagram shows the share configuration rendezvous made between the zrok controller and a zrok frontend:
|
||||
|
||||
![zrok_interstitial_rendezvous](../../images/zrok_interstitial_rendezvous.png)
|
||||
|
||||
Every zrok share has a _config_ recorded in the underlying OpenZiti network. The config is of type `zrok.proxy.v1`. The frontend uses the information in this config to understand the disposition of the share. The config can contain an `interstitial: true` setting. If the config has this setting, and the frontend is configured to enable interstitial pages, then end users accessing the share will receive the interstitial page on first visit.
|
||||
|
||||
By default the zrok controller will record `interstitial: true` in the share config _unless_ a row is present in the `skip_interstitial_grants` table in the underlying database for the account creating the share. The `skip_interstitial_grants` table is a basic SQL structure that allows inserting a row per account.
|
||||
|
||||
```
|
||||
create table skip_interstitial_grants (
|
||||
id serial primary key,
|
||||
|
||||
account_id integer references accounts (id) not null,
|
||||
|
||||
created_at timestamptz not null default(current_timestamp),
|
||||
updated_at timestamptz not null default(current_timestamp),
|
||||
deleted boolean not null default(false)
|
||||
);
|
||||
```
|
||||
|
||||
If an account has a row present in this table when creating a share, then the controller will write `interstitial: false` into the config for the share, which will bypass the interstitial regardless of frontend configuration. The `skip_interstitial_grants` controls what the zrok controller will store in the share config when creating the share.
|
||||
|
||||
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.
|
||||
#
|
||||
#interstitial: true
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
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.
|
||||
|
||||
The `skip_zrok_interstitial` header is especially useful for API clients (like `curl`) and other types of non-interactive clients.
|
||||
|
||||
The `drive` backend mode does not currently support `GET` requests and cannot be accessed with a conventional web browser, so it bypasses the interstitial page requirement.
|
BIN
docs/images/zrok_interstitial_rendezvous.png
Normal file
BIN
docs/images/zrok_interstitial_rendezvous.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
@ -52,7 +52,7 @@ func NewBackend(cfg *BackendConfig) (*Backend, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
handler := util.NewProxyHandler(proxy)
|
||||
handler := util.NewRequestsWrapper(proxy)
|
||||
return &Backend{
|
||||
cfg: cfg,
|
||||
listener: listener,
|
||||
|
@ -68,7 +68,7 @@ func NewFrontend(cfg *FrontendConfig) (*Frontend, error) {
|
||||
}
|
||||
proxy.Transport = zTransport
|
||||
|
||||
handler := authHandler(cfg.ShrToken, util.NewProxyHandler(proxy), "zrok", cfg, zCtx)
|
||||
handler := authHandler(cfg.ShrToken, util.NewRequestsWrapper(proxy), "zrok", cfg, zCtx)
|
||||
return &Frontend{
|
||||
cfg: cfg,
|
||||
zCtx: zCtx,
|
||||
|
@ -16,6 +16,7 @@ type Config struct {
|
||||
Identity string
|
||||
Address string
|
||||
HostMatch string
|
||||
Interstitial bool
|
||||
Oauth *OauthConfig
|
||||
Tls *endpoints.TlsConfig
|
||||
}
|
||||
@ -47,6 +48,7 @@ func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Identity: "public",
|
||||
Address: "0.0.0.0:8080",
|
||||
Interstitial: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/openziti/sdk-golang/ziti"
|
||||
"github.com/openziti/zrok/endpoints"
|
||||
"github.com/openziti/zrok/endpoints/publicProxy/healthUi"
|
||||
"github.com/openziti/zrok/endpoints/publicProxy/interstitialUi"
|
||||
"github.com/openziti/zrok/endpoints/publicProxy/notFoundUi"
|
||||
"github.com/openziti/zrok/endpoints/publicProxy/unauthorizedUi"
|
||||
"github.com/openziti/zrok/environment"
|
||||
@ -73,7 +74,7 @@ func NewHTTP(cfg *Config) (*HttpFrontend, error) {
|
||||
if err := configureOauthHandlers(context.Background(), cfg, cfg.Tls != nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handler := authHandler(util.NewProxyHandler(proxy), cfg, key, zCtx)
|
||||
handler := shareHandler(util.NewRequestsWrapper(proxy), cfg, key, zCtx)
|
||||
return &HttpFrontend{
|
||||
cfg: cfg,
|
||||
zCtx: zCtx,
|
||||
@ -151,12 +152,26 @@ func hostTargetReverseProxy(cfg *Config, ctx ziti.Context) *httputil.ReverseProx
|
||||
return &httputil.ReverseProxy{Director: director}
|
||||
}
|
||||
|
||||
func authHandler(handler http.Handler, pcfg *Config, key []byte, ctx ziti.Context) http.HandlerFunc {
|
||||
func shareHandler(handler http.Handler, pcfg *Config, key []byte, ctx ziti.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
shrToken := resolveService(pcfg.HostMatch, r.Host)
|
||||
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 scheme, found := cfg["auth_scheme"]; found {
|
||||
switch scheme {
|
||||
case string(sdk.None):
|
||||
|
6
endpoints/publicProxy/interstitialUi/embed.go
Normal file
6
endpoints/publicProxy/interstitialUi/embed.go
Normal file
@ -0,0 +1,6 @@
|
||||
package interstitialUi
|
||||
|
||||
import "embed"
|
||||
|
||||
//go:embed index.html
|
||||
var FS embed.FS
|
21
endpoints/publicProxy/interstitialUi/handler.go
Normal file
21
endpoints/publicProxy/interstitialUi/handler.go
Normal file
@ -0,0 +1,21 @@
|
||||
package interstitialUi
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func WriteInterstitialAnnounce(w http.ResponseWriter) {
|
||||
if data, err := FS.ReadFile("index.html"); err == nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
n, err := w.Write(data)
|
||||
if n != len(data) {
|
||||
logrus.Errorf("short write")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
250
endpoints/publicProxy/interstitialUi/index.html
Normal file
250
endpoints/publicProxy/interstitialUi/index.html
Normal file
@ -0,0 +1,250 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<meta name="theme-color" content="#000000"/>
|
||||
<meta name="description" content="zrok interstitial"/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Russo+One&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap" rel="stylesheet">
|
||||
<title>zrok</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'JetBrains Mono', Consolas, 'Courier New', monospace;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
h1, h2 {
|
||||
font-family: 'Russo One', sans-serif;
|
||||
}
|
||||
button {
|
||||
color: purple;
|
||||
}
|
||||
button {
|
||||
appearance: button;
|
||||
backface-visibility: hidden;
|
||||
background-image: linear-gradient(180deg, #0E0238 0%, #231069 100%);
|
||||
border-radius: 6px;
|
||||
border-width: 0;
|
||||
box-shadow: rgba(50, 50, 93, .1) 0 0 0 1px inset,rgba(50, 50, 93, .1) 0 2px 5px 0,rgba(0, 0, 0, .07) 0 1px 1px 0;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-family: 'Russo One', sans-serif;
|
||||
font-size: 100%;
|
||||
height: 44px;
|
||||
line-height: 1.15;
|
||||
margin: 12px 0 0;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
padding: 0 25px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
transform: translateZ(0);
|
||||
transition: all .2s,box-shadow .08s ease-in;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
button:hover {
|
||||
color: #9BF316;
|
||||
}
|
||||
.info {
|
||||
border: 1px solid #231069;
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
}
|
||||
.container {
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.row {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.row [class^="col"] {
|
||||
float: left;
|
||||
margin: 0.5rem 2%;
|
||||
min-height: 0.125rem;
|
||||
}
|
||||
.col {
|
||||
width: 96%;
|
||||
}
|
||||
.row::after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
@media only screen and (min-width: 33.75em) { /* 540px */
|
||||
.container {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 45em) { /* 720px */
|
||||
.col {
|
||||
margin-top: 25px;
|
||||
width: 96%;
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 60em) { /* 960px */
|
||||
.container {
|
||||
width: 75%;
|
||||
max-width: 60rem;
|
||||
}
|
||||
}
|
||||
#hostname {
|
||||
color: red;
|
||||
}
|
||||
#root, #zrok {
|
||||
margin-top: 25px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
function onClick() {
|
||||
let e=new Date; e.setTime(e.getTime()+6048e5);
|
||||
document.cookie = 'zrok_interstitial = 1; expires=${e}; path=/; SameSite=None';
|
||||
window.location.reload();
|
||||
}
|
||||
</script>
|
||||
<div id="root">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div id="warning" class="col info">
|
||||
<h1>You are about to visit a zrok share located at:</h1>
|
||||
<h2 id="hostname"></h2>
|
||||
|
||||
<ul>
|
||||
<li>This share is made available for free through <a href="https://zrok.io/" target="_">zrok</a>.</li>
|
||||
<li>You should only visit this share if you trust whoever sent you the link.</li>
|
||||
<li><strong>Be careful about disclosing any personal or financial information like passwords, phone numbers, or credit cards.</strong></li>
|
||||
</ul>
|
||||
|
||||
<button onClick="onClick()">Visit Share</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>Non-interactive clients:</h2>
|
||||
<ul>
|
||||
<li>This warning can be bypassed by setting the <strong>skip_zrok_interstitial</strong> HTTP
|
||||
header with any value set.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col info">
|
||||
<h1>Are you the owner of this zrok share?</h1>
|
||||
<p>
|
||||
We display this page to prevent abuse of zrok shares. Visitors to your share will only see it once.
|
||||
</p>
|
||||
|
||||
<h2>To remove this page:</h2>
|
||||
<ul>
|
||||
<li>If you are using the global zrok service at zrok.io, you can upgrade to any paid account to have this
|
||||
page removed.</li>
|
||||
<li>If you are using a zrok instance hosted elsewhere, contact the administrator of your instance for
|
||||
options to have this page removed.</li>
|
||||
<li>If you are operating a self-hosted zrok instance, see the
|
||||
<a href="https://docs.zrok.io/" target="_">documentation</a> for configuration options
|
||||
to remove the interstitial page.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div id="zrok" class="col">
|
||||
<a href="https://zrok.io/" target="_"><svg width="25%" height="25%" viewBox="0 0 100 30" xml:space="default" style="clip-rule:evenodd;fill-rule:evenodd;stroke-miterlimit:10" id="svg48" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs id="defs48" /><namedview id="namedview48" pagecolor="#ffffff" bordercolor="#000000" borderopacity="0.25"/>
|
||||
<g transform="matrix(0.0639619,0,0,0.0388212,-1.65041,-1.44321)" id="g1">
|
||||
<path d="m 1589.31,230.468 c 0,-106.573 -52.51,-193.096 -117.2,-193.096 H 143.033 c -64.683,0 -117.198,86.523 -117.198,193.096 v 386.191 c 0,106.573 52.515,193.096 117.198,193.096 H 1472.11 c 64.69,0 117.2,-86.523 117.2,-193.096 z" style="fill:#170549" id="path1" />
|
||||
</g>
|
||||
<g transform="matrix(-0.0296355,-0.233707,-0.233707,0.0296355,76.730746,23.501948)" id="g15">
|
||||
<path d="m -30.685,-47.339 c 0.924,3.31 1.524,5.653 1.574,6.05 0.401,2.368 -0.97,54.354 -1.574,56.749 -0.171,1.029 -1.342,5.311 -2.995,10.56 -11.859,-21.982 -11.292,-51.69 2.995,-73.359" style="fill:#a3a3a3" id="path14" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,89.174746,26.961548)" id="g23">
|
||||
<path d="m 0,-51.2 19.7,-3 c 5.6,-0.9 10.9,2.8 11.9,8.4 l 2.7,17.7 C 29.9,-18.1 23.6,-9 16,-1.3 10.9,3.9 15.9,-1.6 10,2.1 L 1,-39.8 C 0.1,-45.3 -5.7,-50.3 0,-51.2" style="fill:#b3b3b3" id="path22" />
|
||||
</g>
|
||||
<g transform="matrix(-0.160445,0.172496,0.172496,0.160445,96.828546,10.865078)" id="g24">
|
||||
<path d="m 61.55,-141.309 c -46.795,1.269 -84.666,39.929 -84.684,86.346 -0.018,46.417 37.889,83.042 84.684,81.772 46.795,-1.269 84.666,-39.929 84.684,-86.346 0.018,-46.417 -37.889,-83.042 -84.684,-81.772" id="path23" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,56.099446,11.884458)" id="g25">
|
||||
<path d="M 0,34.1 8.7,32.5 C 4.8,21.8 3.2,10.4 4,-0.9 V -1.1 L -6.1,0.7 c -5.4,0.9 -9.2,6.1 -8.3,11.6 l 2.5,13.8 c 1.1,5.5 6.5,9.1 11.9,8" style="fill:#ffffff" id="path24" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,99.257546,5.2882484)" id="g26">
|
||||
<path d="m 0,33.4 -8.8,1.1 C -8.3,23.1 -10.2,11.8 -14.4,1.2 V 1 L -4.2,-0.3 C 1.3,-1.1 6.4,2.8 7.2,8.3 L 9,22.2 C 9.5,27.8 5.5,32.8 0,33.4" style="fill:#ffffff" id="path25" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,76.712646,31.343248)" id="g27">
|
||||
<path d="m 0,-161.7 c -22,0.5 -42.9,9.5 -58.3,25.2 -15.3,15.3 -23.7,36.2 -23.3,57.8 0.4,21.5 9.6,42 25.5,56.5 C -40,-7.3 -18.7,0.7 3.2,0 25.2,-0.5 46.1,-9.5 61.5,-25.2 76.7,-40.6 85.1,-61.4 84.8,-83 84.4,-104.5 75.2,-125 59.4,-139.6 43.2,-154.4 21.9,-162.4 0,-161.7 m -61.7,22.1 c 16.3,-16.6 38.4,-26.2 61.6,-26.8 23.2,-0.7 45.7,7.7 62.7,23.4 16.8,15.4 26.5,37 26.9,59.8 C 89.9,-60.3 81,-38.3 64.9,-22 48.6,-5.4 26.5,4.1 3.3,4.7 -19.8,5.4 -42.3,-3 -59.3,-18.7 c -16.8,-15.4 -26.5,-37.1 -26.9,-59.8 -0.5,-22.9 8.4,-44.9 24.5,-61.1" style="fill-rule:nonzero" id="path26" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,56.099446,11.790228)" id="g28">
|
||||
<path d="M 0,34.5 8.7,32.9 C 4.8,22.2 3.2,10.8 4,-0.5 v -0.2 l -7.7,1.4 c 0.1,9.2 0.2,17.8 2.8,27.3 l -8.8,1.6 c -0.4,0 -0.7,0.3 -0.8,0.7 2.5,3.2 6.5,4.9 10.5,4.2" style="fill:#a3a3a3" id="path27" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,99.257546,5.5473884)" id="g29">
|
||||
<path d="m 0,32.3 -8.8,1.1 C -8.3,22 -10.2,10.7 -14.4,0.1 v -0.2 l 7.7,-1 c 2.7,8.8 5.2,17 5.6,26.8 L 7.7,24.6 C 8.1,24.5 8.5,24.7 8.7,25 7.4,29 4,31.8 0,32.3" style="fill:#a3a3a3" id="path28" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,57.418746,12.449848)" id="g30">
|
||||
<path d="m 0,28.3 -6,1.1 c -2,0.4 -4.2,-0.1 -5.9,-1.2 -1.7,-1.1 -2.9,-2.9 -3.3,-4.9 L -17.6,9.5 c -0.4,-2 0.1,-4.1 1.3,-5.7 1.2,-1.7 3,-2.8 5.1,-3.2 L -4,-0.7 c -0.4,9.8 1,19.6 4,29 M -5.2,34 3.6,32.4 6.4,31.9 5.5,29.3 C 1.7,18.9 0.1,7.9 0.9,-3.2 V -3.5 L 0.8,-4 0.4,-6.3 -2,-5.9 -12.1,-4.1 c -3.3,0.6 -6.2,2.4 -8.2,5.1 -1.9,2.7 -2.7,6 -2.1,9.2 l 2.5,13.8 c 0.6,3.2 2.5,6.1 5.2,7.9 2.9,2 6.2,2.7 9.5,2.1" style="fill-rule:nonzero" id="path29" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,97.749846,5.8536384)" id="g31">
|
||||
<path d="m 0,29.5 6.1,-0.8 c 2.1,-0.3 3.9,-1.3 5.2,-3 1.3,-1.6 1.9,-3.6 1.6,-5.7 L 11.1,6.1 C 10.8,4.1 9.8,2.2 8.1,1 6.4,-0.2 4.4,-0.8 2.3,-0.5 L -5,0.4 c 1.7,4.6 2.9,9.4 3.7,14.2 0.9,5.1 1.3,10 1.3,14.9 m 6.6,3.8 -8.8,1.1 -2.8,0.4 0.1,-2.7 C -4.4,21.1 -6.3,10 -10.4,-0.2 l -0.1,-0.3 -0.1,-0.5 -0.3,-2.3 2.4,-0.3 10.2,-1.3 c 3.3,-0.4 6.7,0.4 9.3,2.4 2.6,2 4.4,4.9 4.8,8.1 l 1.8,13.9 c 0.4,3.3 -0.5,6.6 -2.6,9.1 -2,2.6 -5,4.3 -8.4,4.7" style="fill-rule:nonzero" id="path30" />
|
||||
</g>
|
||||
<g transform="matrix(-0.094487,-0.2158,-0.2158,0.094487,60.008346,10.163168)" id="g32">
|
||||
<path d="m -68.968,-142.292 c -20.852,8.475 -37.24,25.148 -45.56,46.039 -17.44,43.442 2.976,93.561 45.56,112.032 9.157,3.96 18.932,6.339 28.941,6.76 -61.01,-17.172 -106.83,-100.38 -28.941,-164.831" style="fill:#ffffff" id="path31" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,95.346946,21.496048)" id="g33">
|
||||
<path d="m 0,-10.4 c 1.7,-4.2 3,-8.6 3.9,-13 -7.8,18.2 -18.4,31.7 -30.5,41.1 -11,8.6 -23.9,14.3 -37.7,16.6 -13.2,2.2 -26.8,1.5 -39.8,-2 -13.1,-3.4 -25.4,-9.4 -36.2,-17.6 1.5,1.7 3.2,3.4 4.9,5 16.1,14.9 37.4,22.8 59.3,22.1 22,-0.5 42.9,-9.5 58.3,-25.2 C -10,9 -4,-0.2 0,-10.4 m 10.4,-31.7 c 0.3,11.4 -1.7,22.7 -5.9,33.3 -4.3,10.7 -10.6,20.4 -18.7,28.5 -16.3,16.6 -38.4,26.1 -61.6,26.7 -23.1,0.7 -45.6,-7.7 -62.6,-23.4 -3.8,-3.5 -7.3,-7.3 -10.4,-11.4 -3.1,-4.1 -5.7,-8.4 -8,-13 l -5.1,-13.9 9.1,11.4 c 13,15.5 30.6,26.6 50.2,31.7 12.3,3.3 25.1,4 37.7,1.9 12.9,-2.1 25.1,-7.5 35.5,-15.6 15.1,-11.8 27.6,-30.2 35.2,-56.7 l 2.7,-14.9 z" style="fill-rule:nonzero" id="path32" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,62.507246,12.284948)" id="g34">
|
||||
<path d="m 0,-59 c 32.8,-33.4 87,-34.8 121,-3.2 16.1,14.8 25.6,35.6 26.2,57.4 -12.8,-31.9 -30.4,-46.9 -51.7,-54.1 -33.2,-11.2 -64.8,-8.3 -93,24.3 -20.7,23.7 -19.6,48.4 -18,69.4 C -30.5,4.5 -25.5,-33.1 0,-59" style="fill:#ffffff" id="path33" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,76.665546,22.037948)" id="g35">
|
||||
<path d="m 0,-124.1 c -22,0.5 -42.9,9.5 -58.3,25.2 -11.7,11.8 -19.5,27 -22.2,43.4 -1.9,11.4 -1.3,23 1.8,34.1 -0.1,-6.4 0.4,-12.9 1.4,-19.2 2.4,-13.7 8.5,-26.5 17.8,-36.9 14.5,-16.7 29.9,-25.9 46,-29.4 16.1,-3.5 32.7,-1.4 49.7,4.4 11.3,3.7 21.6,10 30,18.4 6.7,6.8 12.3,14.6 16.7,23.1 C 81.7,-66.3 80,-71.5 77.7,-76.5 73.3,-86.1 67.1,-94.7 59.3,-101.9 43.1,-116.8 21.9,-124.8 0,-124.1 m -61.8,22.1 c 16.3,-16.6 38.4,-26.2 61.7,-26.8 23.1,-0.7 45.6,7.7 62.6,23.4 8.2,7.6 14.8,16.7 19.4,26.9 4.6,10.1 7.1,21.1 7.4,32.2 l -0.2,13.2 -4.3,-12.2 C 78.5,-61 71.1,-72.4 62.7,-80.8 54.8,-88.7 45.1,-94.6 34.5,-98.1 c -16.2,-5.5 -32,-7.5 -47.1,-4.2 -15.1,3.3 -29.5,11.9 -43.3,27.8 -8.7,9.8 -14.5,21.8 -16.7,34.6 -2.1,11.5 -1.4,22.8 -0.6,33.1 l 0.9,12.2 -5.4,-11.1 c -7.7,-15.7 -10.4,-33.4 -7.5,-50.7 2.8,-17.1 11,-33.2 23.4,-45.6" style="fill-rule:nonzero" id="path34" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,97.043046,0.69445839)" id="g36">
|
||||
<path d="m 0,37.2 c 0.3,2.4 0.5,4.8 0.5,7.3 -12.8,-31.9 -30.4,-46.9 -51.7,-54.1 -33.2,-11.2 -75.4,-3.5 -98,26.7 -6.8,9 -18.6,29.7 -15.7,54.2 -7.6,-24.3 -1.1,-48.6 20.4,-70.4 32.7,-33.5 86.8,-35 120.8,-3.3 C -12.1,8.2 -3.9,22 0,37.2" style="fill:#e6e6e6" id="path35" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,92.166646,14.028248)" id="g37">
|
||||
<path d="m 0,-49.8 c 6.3,6.4 11.6,13.6 15.8,21.4 -1.6,-4 -3.5,-7.9 -5.7,-11.5 -3.9,-6.6 -8.8,-12.5 -14.4,-17.8 -16.2,-14.9 -37.5,-22.9 -59.5,-22.3 -22.2,0.6 -43.2,9.7 -58.7,25.5 -10.4,10.6 -17.2,21.7 -20.5,33.2 -1.4,4.9 -2.2,9.9 -2.3,15 0.5,-2.5 1,-4.8 1.7,-7.1 2.9,-9.8 7.5,-19.1 13.5,-27.3 11.1,-14.9 26.7,-24.4 43.8,-29.1 18.6,-5 38.2,-4.4 56.4,1.7 11.3,3.7 21.5,10 29.9,18.3 m 22.6,30.1 c 0.2,1.3 0.3,2.6 0.4,3.7 0.1,1.2 0.1,2.5 0.2,3.7 l 0.3,10.9 -4.1,-10.2 C 13.1,-27.3 5.6,-38.8 -2.8,-47.3 c -7.9,-7.9 -17.7,-13.9 -28.3,-17.4 -17.5,-5.8 -36.3,-6.4 -54.1,-1.6 -16.3,4.4 -31.2,13.5 -41.7,27.6 -5.8,7.9 -10.2,16.7 -13,26.1 -2.6,8.7 -3.4,17.8 -2.4,26.8 l 2.4,20.4 -6.2,-19.4 c -4,-12.2 -4.2,-25.2 -0.7,-37.5 3.5,-12 10.5,-23.7 21.4,-34.7 l 0.1,-0.1 c 16.2,-16.5 38.3,-26 61.4,-26.6 23,-0.7 45.3,7.6 62.2,23.2 l 0.1,0.1 c 5.9,5.4 10.9,11.7 15.1,18.6 4.1,6.8 7.2,14.2 9.1,21.9 z" style="fill-rule:nonzero" id="path36" />
|
||||
</g>
|
||||
<g transform="matrix(0.131129,0.19571,0.19571,-0.131129,60.042246,4.7938284)" id="g38">
|
||||
<path d="m 32.805,-16.969 c 0,0 -31.438,29.611 -14.637,74.808 1.315,4.657 15.792,4.346 14.637,-2.946 -1.154,-7.292 -9.456,-35.313 13.492,-66.457 1.659,-2.195 -8.429,-10.963 -13.492,-5.405" style="fill:#e6e6e6" id="path37" />
|
||||
</g>
|
||||
<g transform="matrix(0.235579,0,0,0.235579,52.094646,-8.3046516)" id="g45">
|
||||
<g opacity="0.25" id="g44">
|
||||
<g transform="matrix(-1,0,0,1,141,101.8)" id="g39">
|
||||
<path d="m 0,-36.8 c -10.162,0 -18.4,8.238 -18.4,18.4 C -18.4,-8.238 -10.162,0 0,0 10.162,0 18.4,-8.238 18.4,-18.4 18.4,-28.562 10.162,-36.8 0,-36.8" style="fill:#fefcf9;fill-rule:nonzero" id="path38" />
|
||||
</g>
|
||||
<g transform="matrix(-1,0,0,1,143.3,88.2)" id="g40">
|
||||
<path d="m 0,-14.2 c -3.921,0 -7.1,3.179 -7.1,7.1 0,3.921 3.179,7.1 7.1,7.1 3.921,0 7.1,-3.179 7.1,-7.1 0,-3.921 -3.179,-7.1 -7.1,-7.1" style="fill-rule:nonzero" id="path39" />
|
||||
</g>
|
||||
<g transform="matrix(-1,0,0,1,71.8,112.5)" id="g41">
|
||||
<path d="m 0,-36.8 c -10.162,0 -18.4,8.238 -18.4,18.4 C -18.4,-8.238 -10.162,0 0,0 10.162,0 18.4,-8.238 18.4,-18.4 18.4,-28.562 10.162,-36.8 0,-36.8" style="fill:#fefcf9;fill-rule:nonzero" id="path40" />
|
||||
</g>
|
||||
<g transform="translate(116.1,122.6)" id="g42">
|
||||
<path d="m 0,17.4 c -11.8,1.8 -23.8,-0.3 -34.3,-5.8 -0.9,-0.5 -1.3,-1.6 -0.8,-2.5 0.5,-0.9 1.6,-1.3 2.5,-0.8 20.1,10.8 44.9,7 60.7,-9.4 0.7,-0.7 1.9,-0.7 2.6,0 0.7,0.7 0.7,1.9 0,2.6 -7.9,8 -18,13.4 -29,15.6 -0.5,0 -1.1,0.2 -1.7,0.3 z" style="fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:16.98px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4" id="path41" />
|
||||
</g>
|
||||
<g transform="matrix(-1,0,0,1,76.2,97.8)" id="g43">
|
||||
<path d="m 0,-14.2 c -3.921,0 -7.1,3.179 -7.1,7.1 0,3.921 3.179,7.1 7.1,7.1 3.921,0 7.1,-3.179 7.1,-7.1 0,-3.921 -3.179,-7.1 -7.1,-7.1" style="fill-rule:nonzero" id="path42" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="zrok" transform="matrix(0.02734414,0,0,0.02978162,2.3966205,5.6320267)"> <path d="M 508.489,211.308 300.982,391.439 h 207.507 v 93.53 H 134.977 v -93.53 L 342.483,211.308 H 134.977 v -93.53 h 373.512 z" style="fill:#7bf600;fill-rule:nonzero" id="path45" />
|
||||
<path d="m 791.453,218.236 c -31.692,0 -64.39,6.928 -98.094,20.784 V 484.969 H 561.309 V 117.778 h 116.958 l 7.546,45.033 C 729.075,128.17 774.349,110.85 821.636,110.85 h 33.955 v 107.386 z" style="fill:#7bf600;fill-rule:nonzero" id="path46" />
|
||||
<path d="m 1300.79,381.047 c 0,34.64 -10.69,61.776 -32.07,81.405 -21.38,19.63 -50.93,29.445 -88.66,29.445 H 1006.5 c -37.723,0 -67.277,-9.815 -88.657,-29.445 -21.379,-19.629 -32.069,-46.765 -32.069,-81.405 V 221.7 c 0,-34.641 10.69,-61.776 32.069,-81.405 21.38,-19.63 50.934,-29.445 88.657,-29.445 h 173.56 c 37.73,0 67.28,9.815 88.66,29.445 21.38,19.629 32.07,46.764 32.07,81.405 z M 1168.74,232.092 c 0,-18.475 -10.06,-27.712 -30.18,-27.712 h -90.55 c -20.13,0 -30.19,9.237 -30.19,27.712 v 138.563 c 0,18.475 10.06,27.712 30.19,27.712 h 90.55 c 20.12,0 30.18,-9.237 30.18,-27.712 z" style="fill:#7bf600;fill-rule:nonzero" id="path47" />
|
||||
<path d="M 1500.75,339.478 V 484.969 H 1368.7 V 0 h 132.05 v 245.948 h 60.37 l 83,-128.17 h 139.59 l -113.18,176.667 113.18,190.524 h -139.59 l -86.78,-145.491 z" style="fill:#7bf600;fill-rule:nonzero" id="path48" />
|
||||
</g>
|
||||
</svg></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
document.getElementById("hostname").innerText = document.location.host;
|
||||
</script>
|
||||
</html>
|
@ -10,6 +10,11 @@ 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.
|
||||
#
|
||||
#interstitial: true
|
||||
|
||||
# The OAuth configuration is used when enabling OAuth authentication with your public frontend.
|
||||
#
|
||||
#oauth:
|
||||
|
@ -1,10 +1,13 @@
|
||||
package sdk
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const ZrokProxyConfig = "zrok.proxy.v1"
|
||||
|
||||
type FrontendConfig struct {
|
||||
Interstitial bool `json:"interstitial"`
|
||||
AuthScheme AuthScheme `json:"auth_scheme"`
|
||||
BasicAuth *BasicAuthConfig `json:"basic_auth"`
|
||||
OauthAuth *OauthConfig `json:"oauth"`
|
||||
|
@ -6,13 +6,13 @@ import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type proxyHandler struct {
|
||||
type requestsWrapper struct {
|
||||
proxy *httputil.ReverseProxy
|
||||
requests int32
|
||||
}
|
||||
|
||||
func NewProxyHandler(proxy *httputil.ReverseProxy) *proxyHandler {
|
||||
handler := &proxyHandler{proxy: proxy}
|
||||
func NewRequestsWrapper(proxy *httputil.ReverseProxy) *requestsWrapper {
|
||||
handler := &requestsWrapper{proxy: proxy}
|
||||
|
||||
director := proxy.Director
|
||||
proxy.Director = func(req *http.Request) {
|
||||
@ -23,10 +23,10 @@ func NewProxyHandler(proxy *httputil.ReverseProxy) *proxyHandler {
|
||||
return handler
|
||||
}
|
||||
|
||||
func (self *proxyHandler) Requests() int32 {
|
||||
func (self *requestsWrapper) Requests() int32 {
|
||||
return atomic.LoadInt32(&self.requests)
|
||||
}
|
||||
|
||||
func (self *proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func (self *requestsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
self.proxy.ServeHTTP(w, r)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user