diff --git a/404.html b/404.html index 8005cfee..b2a06dd5 100644 --- a/404.html +++ b/404.html @@ -9,7 +9,7 @@ - + diff --git a/assets/files/ctrl-6c22ae02cafe307b82e5a1f783497950.yml b/assets/files/ctrl-06b900e22aab525ebec542dea6769d51.yml similarity index 89% rename from assets/files/ctrl-6c22ae02cafe307b82e5a1f783497950.yml rename to assets/files/ctrl-06b900e22aab525ebec542dea6769d51.yml index e2c06c5d..0c680b8d 100644 --- a/assets/files/ctrl-6c22ae02cafe307b82e5a1f783497950.yml +++ b/assets/files/ctrl-06b900e22aab525ebec542dea6769d51.yml @@ -9,7 +9,7 @@ # configuration, the software will expect this field to be incremented. This protects you against invalid configuration # versions and will refer to you to the documentation when the configuration structure changes. # -v: 3 +v: 4 admin: # The `secrets` array contains a list of strings that represent valid `ZROK_ADMIN_TOKEN` values to be used for @@ -74,44 +74,25 @@ invites: # token_contact: invite@zrok.io -# Service instance limits configuration. +# Service instance limits global configuration. # # See `docs/guides/metrics-and-limits/configuring-limits.md` for details. # limits: environments: -1 shares: -1 + reserved_shares: -1 + unique_names: -1 bandwidth: - per_account: - period: 5m - warning: - rx: -1 - tx: -1 - total: 7242880 - limit: - rx: -1 - tx: -1 - total: 10485760 - per_environment: - period: 5m - warning: - rx: -1 - tx: -1 - total: -1 - limit: - rx: -1 - tx: -1 - total: -1 - per_share: - period: 5m - warning: - rx: -1 - tx: -1 - total: -1 - limit: - rx: -1 - tx: -1 - total: -1 + period: 5m + warning: + rx: -1 + tx: -1 + total: 7242880 + limit: + rx: -1 + tx: -1 + total: 10485760 enforcing: false cycle: 5m diff --git a/assets/js/600b2345.9d8edfd8.js b/assets/js/600b2345.9d8edfd8.js deleted file mode 100644 index 13c29489..00000000 --- a/assets/js/600b2345.9d8edfd8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4900],{169:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>a,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var t=n(5893),s=n(1151);const r={sidebar_position:40},o="Configuring Limits",l={id:"guides/self-hosting/metrics-and-limits/configuring-limits",title:"Configuring Limits",description:"If you have not yet configured metrics, please visit the metrics guide first before working through the limits configuration.",source:"@site/../docs/guides/self-hosting/metrics-and-limits/configuring-limits.md",sourceDirName:"guides/self-hosting/metrics-and-limits",slug:"/guides/self-hosting/metrics-and-limits/configuring-limits",permalink:"/docs/guides/self-hosting/metrics-and-limits/configuring-limits",draft:!1,unlisted:!1,editUrl:"https://github.com/openziti/zrok/blob/main/docs/../docs/guides/self-hosting/metrics-and-limits/configuring-limits.md",tags:[],version:"current",sidebarPosition:40,frontMatter:{sidebar_position:40},sidebar:"tutorialSidebar",previous:{title:"Configuring Metrics",permalink:"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics"},next:{title:"OAuth",permalink:"/docs/category/oauth"}},a={},c=[{value:"The Global Controls",id:"the-global-controls",level:2},{value:"Resource Limits",id:"resource-limits",level:2},{value:"Bandwidth Limits",id:"bandwidth-limits",level:2},{value:"Limit Actions",id:"limit-actions",level:3},{value:"Unlimited Accounts",id:"unlimited-accounts",level:2}];function h(e){const i={a:"a",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,s.a)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"configuring-limits",children:"Configuring Limits"}),"\n",(0,t.jsxs)(i.blockquote,{children:["\n",(0,t.jsxs)(i.p,{children:["If you have not yet configured ",(0,t.jsx)(i.a,{href:"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics",children:"metrics"}),", please visit the ",(0,t.jsx)(i.a,{href:"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics",children:"metrics guide"})," first before working through the limits configuration."]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["The limits facility in ",(0,t.jsx)(i.code,{children:"zrok"})," is responsible for controlling the number of resources in use (environments, shares) and also for ensuring that any single account, environment, or share is held below the configured thresholds."]}),"\n",(0,t.jsxs)(i.p,{children:["Take this ",(0,t.jsx)(i.code,{children:"zrok"})," controller configuration stanza as an example:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-yaml",children:"limits:\n enforcing: true\n cycle: 1m\n environments: -1\n shares: -1\n bandwidth:\n per_account:\n period: 5m\n warning:\n rx: -1\n tx: -1\n total: 7242880\n limit:\n rx: -1\n tx: -1\n total: 10485760\n per_environment:\n period: 5m\n warning:\n rx: -1\n tx: -1\n total: -1\n limit:\n rx: -1\n tx: -1\n total: -1\n per_share:\n period: 5m\n warning:\n rx: -1\n tx: -1\n total: -1\n limit:\n rx: -1\n tx: -1\n total: -1\n"})}),"\n",(0,t.jsx)(i.h2,{id:"the-global-controls",children:"The Global Controls"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"enforcing"})," boolean will globally enable or disable limits for the controller."]}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"cycle"})," value controls how frequently the limits system will look for limited resources to re-enable."]}),"\n",(0,t.jsx)(i.h2,{id:"resource-limits",children:"Resource Limits"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"environments"})," and ",(0,t.jsx)(i.code,{children:"shares"})," values control the number of environments and shares that are allowed per-account. Any limit value can be set to ",(0,t.jsx)(i.code,{children:"-1"}),", which means ",(0,t.jsx)(i.em,{children:"unlimited"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"bandwidth-limits",children:"Bandwidth Limits"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"bandwidth"})," section is designed to provide a configurable system for controlling the amount of data transfer that can be performed by users of the ",(0,t.jsx)(i.code,{children:"zrok"})," service instance. The bandwidth limits are configurable for each share, environment, and account."]}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.code,{children:"per_account"}),", ",(0,t.jsx)(i.code,{children:"per_environment"}),", and ",(0,t.jsx)(i.code,{children:"per_share"})," are all configured the same way:"]}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"period"})," specifies the time window for the bandwidth limit. See the documentation for ",(0,t.jsx)(i.a,{href:"https://pkg.go.dev/time#ParseDuration",children:(0,t.jsx)(i.code,{children:"time.Duration.ParseDuration"})})," for details about the format used for these durations. If the ",(0,t.jsx)(i.code,{children:"period"})," is set to 5 minutes, then the limits implementation will monitor the send and receive traffic for the resource (share, environment, or account) for the last 5 minutes, and if the amount of data is greater than either the ",(0,t.jsx)(i.code,{children:"warning"})," or the ",(0,t.jsx)(i.code,{children:"limit"})," threshold, action will be taken."]}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"rx"})," value is the number of bytes ",(0,t.jsx)(i.em,{children:"received"})," by the resource. The ",(0,t.jsx)(i.code,{children:"tx"})," value is the number of bytes ",(0,t.jsx)(i.em,{children:"transmitted"})," by the resource. And ",(0,t.jsx)(i.code,{children:"total"})," is the combined ",(0,t.jsx)(i.code,{children:"rx"}),"+",(0,t.jsx)(i.code,{children:"tx"})," value."]}),"\n",(0,t.jsxs)(i.p,{children:["If the traffic quantity is greater than the ",(0,t.jsx)(i.code,{children:"warning"})," threshold, the user will receive an email notification letting them know that their data transfer size is rising and will eventually be limited (the email details the limit threshold)."]}),"\n",(0,t.jsxs)(i.p,{children:["If the traffic quantity is greater than the ",(0,t.jsx)(i.code,{children:"limit"})," threshold, the resources will be limited until the traffic in the window (the last 5 minutes in our example) falls back below the ",(0,t.jsx)(i.code,{children:"limit"})," threshold."]}),"\n",(0,t.jsx)(i.h3,{id:"limit-actions",children:"Limit Actions"}),"\n",(0,t.jsx)(i.p,{children:"When a resource is limited, the actions taken differ depending on what kind of resource is being limited."}),"\n",(0,t.jsxs)(i.p,{children:["When a share is limited, the dial service policies for that share are removed. No other action is taken. This means that public frontends will simply return a ",(0,t.jsx)(i.code,{children:"404"})," as if the share is no longer there. Private frontends will also return ",(0,t.jsx)(i.code,{children:"404"})," errors. When the limit is relaxed, the dial policies are put back in place and the share will continue operating normally."]}),"\n",(0,t.jsx)(i.p,{children:"When an environment is limited, all of the shares in that environment become limited, and the user is not able to create new shares in that environment. When the limit is relaxed, all of the share limits are relaxed and the user is again able to add shares to the environment."}),"\n",(0,t.jsx)(i.p,{children:"When an account is limited, all of the environments in that account become limited (limiting all of the shares), and the user is not able to create new environments or shares. When the limit is relaxed, all of the environments and shares will return to normal operation."}),"\n",(0,t.jsx)(i.h2,{id:"unlimited-accounts",children:"Unlimited Accounts"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"accounts"})," table in the database includes a ",(0,t.jsx)(i.code,{children:"limitless"})," column. When this column is set to ",(0,t.jsx)(i.code,{children:"true"})," the account is not subject to any of the limits in the system."]})]})}function d(e={}){const{wrapper:i}={...(0,s.a)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},1151:(e,i,n)=>{n.d(i,{Z:()=>l,a:()=>o});var t=n(7294);const s={},r=t.createContext(s);function o(e){const i=t.useContext(r);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function l(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),t.createElement(r.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/600b2345.b5dbe8be.js b/assets/js/600b2345.b5dbe8be.js new file mode 100644 index 00000000..6f4ab809 --- /dev/null +++ b/assets/js/600b2345.b5dbe8be.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4900],{169:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});var t=n(5893),s=n(1151);const a={sidebar_position:40},l="Configuring Limits",r={id:"guides/self-hosting/metrics-and-limits/configuring-limits",title:"Configuring Limits",description:"This guide is current as of zrok version v0.4.31.",source:"@site/../docs/guides/self-hosting/metrics-and-limits/configuring-limits.md",sourceDirName:"guides/self-hosting/metrics-and-limits",slug:"/guides/self-hosting/metrics-and-limits/configuring-limits",permalink:"/docs/guides/self-hosting/metrics-and-limits/configuring-limits",draft:!1,unlisted:!1,editUrl:"https://github.com/openziti/zrok/blob/main/docs/../docs/guides/self-hosting/metrics-and-limits/configuring-limits.md",tags:[],version:"current",sidebarPosition:40,frontMatter:{sidebar_position:40},sidebar:"tutorialSidebar",previous:{title:"Configuring Metrics",permalink:"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics"},next:{title:"OAuth",permalink:"/docs/category/oauth"}},o={},c=[{value:"Understanding the zrok Limits Agent",id:"understanding-the-zrok-limits-agent",level:2},{value:"Types of Limits",id:"types-of-limits",level:3},{value:"The Global Configuration",id:"the-global-configuration",level:2},{value:"Global Resouce Count Limits",id:"global-resouce-count-limits",level:3},{value:"Global Bandwidth Limits",id:"global-bandwidth-limits",level:3},{value:"Limit Classes",id:"limit-classes",level:2},{value:"Unscoped Resource Count Classes",id:"unscoped-resource-count-classes",level:3},{value:"Unscoped Bandwidth Classes",id:"unscoped-bandwidth-classes",level:3},{value:"Scoped Classes",id:"scoped-classes",level:3},{value:"Limit Actions",id:"limit-actions",level:2},{value:"Unlimited Accounts",id:"unlimited-accounts",level:2},{value:"Experimental Limits Locking",id:"experimental-limits-locking",level:2},{value:"Caveats",id:"caveats",level:2},{value:"Aggregate Bandwidth",id:"aggregate-bandwidth",level:3},{value:"Administration Through SQL",id:"administration-through-sql",level:3},{value:"Performance",id:"performance",level:3}];function d(e){const i={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,s.a)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"configuring-limits",children:"Configuring Limits"}),"\n",(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsxs)(i.p,{children:["This guide is current as of zrok version ",(0,t.jsx)(i.code,{children:"v0.4.31"}),"."]})}),"\n",(0,t.jsx)(i.admonition,{type:"warning",children:(0,t.jsxs)(i.p,{children:["If you have not yet configured ",(0,t.jsx)(i.a,{href:"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics",children:"metrics"}),", please visit the ",(0,t.jsx)(i.a,{href:"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics",children:"metrics guide"})," first before working through the limits configuration."]})}),"\n",(0,t.jsx)(i.h2,{id:"understanding-the-zrok-limits-agent",children:"Understanding the zrok Limits Agent"}),"\n",(0,t.jsx)(i.p,{children:"The limits agent is a component of the zrok controller. It can be enabled and configured through the zrok controller configuration."}),"\n",(0,t.jsx)(i.p,{children:"The limits agent is responsible for controlling the number of resources in use (environments, shares, etc.) and also for ensuring that accounts are held below the configured data transfer bandwidth thresholds. The limits agent exists to manage resource consumption for larger, multi-user zrok installations."}),"\n",(0,t.jsx)(i.h3,{id:"types-of-limits",children:"Types of Limits"}),"\n",(0,t.jsxs)(i.p,{children:["Limits can be specified that control the number of environments, shares, reserved shares, and unique names that can be created by an account. Limits that control the allowed number of resources are called ",(0,t.jsx)(i.em,{children:"resource count limits"}),"."]}),"\n",(0,t.jsxs)(i.p,{children:["Limits can be specified to control the amount of data that can be transferred within a time period. Limits that control the amount of data that can be transferred are called ",(0,t.jsx)(i.em,{children:"bandwidth limits"}),"."]}),"\n",(0,t.jsxs)(i.p,{children:["zrok limits can be specified ",(0,t.jsx)(i.em,{children:"globally"}),", applying to all users in a service instance. Limit ",(0,t.jsx)(i.em,{children:"classes"})," can be created to provide additional levels of resource allocation. Limit classes can then be ",(0,t.jsx)(i.em,{children:"applied"})," to multiple accounts, to alter their limit allocation beyond what's configured in the global configuration."]}),"\n",(0,t.jsx)(i.h2,{id:"the-global-configuration",children:"The Global Configuration"}),"\n",(0,t.jsxs)(i.p,{children:["The reference configuration for the zrok controller (found at ",(0,t.jsx)(i.a,{href:"https://github.com/openziti/zrok/blob/main/etc/ctrl.yml",children:(0,t.jsx)(i.code,{children:"etc/ctrl.yaml"})})," in the ",(0,t.jsx)(i.a,{href:"https://github.com/openziti/zrok",children:"repository"}),") contains the global limits configuration, which looks like this:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-yaml",children:"# Service instance limits global configuration.\n#\n# See `docs/guides/metrics-and-limits/configuring-limits.md` for details.\n#\nlimits:\n environments: -1\n shares: -1\n reserved_shares: -1\n unique_names: -1\n bandwidth:\n period: 5m\n warning:\n rx: -1\n tx: -1\n total: 7242880\n limit:\n rx: -1\n tx: -1\n total: 10485760\n enforcing: false\n cycle: 5m\n"})}),"\n",(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsxs)(i.p,{children:["A value of ",(0,t.jsx)(i.code,{children:"-1"})," appearing in the limits configuration mean the value is ",(0,t.jsx)(i.em,{children:"unlimited"}),"."]})}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"enforcing"})," boolean specifies whether or not limits are enabled in the service instance. By default, limits is disabled. No matter what else is configured in this stanza, if ",(0,t.jsx)(i.code,{children:"enforcing"})," is set to ",(0,t.jsx)(i.code,{children:"false"}),", there will be no limits placed on any account in the service instance."]}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"cycle"}),' value controls how frequently the limits agent will evaluate enforced limits. When a user exceeds a limit and has their shares disabled, the limits agent will evaluate their bandwidth usage on this interval looking to "relax" the limit once their usage falls below the threshold.']}),"\n",(0,t.jsx)(i.h3,{id:"global-resouce-count-limits",children:"Global Resouce Count Limits"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"environments"}),", ",(0,t.jsx)(i.code,{children:"shares"}),", ",(0,t.jsx)(i.code,{children:"reserved_shares"}),", and ",(0,t.jsx)(i.code,{children:"unique_names"})," specify the resource count limits, globally for the service instance."]}),"\n",(0,t.jsx)(i.p,{children:"These resource counts will be applied to all users in the service instance by default."}),"\n",(0,t.jsx)(i.h3,{id:"global-bandwidth-limits",children:"Global Bandwidth Limits"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"bandwidth"})," section defines the global bandwidth limits for all users in the service instance."]}),"\n",(0,t.jsxs)(i.p,{children:["There are two levels of bandwidth limits that can be specified in the global configuration. The first limit defines a ",(0,t.jsx)(i.em,{children:"warning"})," threshold where the user will receive an email that they are using increased data transfer amounts and will ultimately be subject to a limit. If you do not want this warning email to be sent, then configure all of the values to ",(0,t.jsx)(i.code,{children:"-1"})," (unlimited)."]}),"\n",(0,t.jsxs)(i.p,{children:["The second limit defines the the actual ",(0,t.jsx)(i.em,{children:"limit"})," threshold, where the limits agent will disabled traffic for the account's shares."]}),"\n",(0,t.jsxs)(i.p,{children:["Bandwidth limits can be specified in terms of ",(0,t.jsx)(i.code,{children:"tx"})," (or ",(0,t.jsx)(i.em,{children:"transmitted"})," data), ",(0,t.jsx)(i.code,{children:"rx"})," (or ",(0,t.jsx)(i.em,{children:"received"})," data), and the ",(0,t.jsx)(i.code,{children:"total"})," bytes that are sent in either direction. If you only want to set the ",(0,t.jsx)(i.code,{children:"total"})," transferred limit, you can set ",(0,t.jsx)(i.code,{children:"rx"})," and ",(0,t.jsx)(i.code,{children:"tx"})," to ",(0,t.jsx)(i.code,{children:"-1"})," (for ",(0,t.jsx)(i.em,{children:"unlimited"}),"). You can configure any combination of these these values at either the limit or warning levels."]}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"period"})," specifies the time window for the bandwidth limit. See the documentation for ",(0,t.jsx)(i.a,{href:"https://pkg.go.dev/time#ParseDuration",children:(0,t.jsx)(i.code,{children:"time.Duration.ParseDuration"})})," for details about the format used for these durations. If the ",(0,t.jsx)(i.code,{children:"period"})," is set to 5 minutes, then the limits agent will monitor the transmitted and receivde traffic for the account for the last 5 minutes, and if the amount of data is greater than either the ",(0,t.jsx)(i.code,{children:"warning"})," or the ",(0,t.jsx)(i.code,{children:"limit"})," threshold, action will be taken."]}),"\n",(0,t.jsxs)(i.p,{children:["In the global configuration example above users are allowed to transfer a total of ",(0,t.jsx)(i.code,{children:"10485760"})," bytes in a ",(0,t.jsx)(i.code,{children:"5m"})," period, and they will receive a warning email after they transfer more than ",(0,t.jsx)(i.code,{children:"7242880"})," bytes in a ",(0,t.jsx)(i.code,{children:"5m"})," period."]}),"\n",(0,t.jsx)(i.h2,{id:"limit-classes",children:"Limit Classes"}),"\n",(0,t.jsxs)(i.p,{children:["The zrok limits agent includes a concept called ",(0,t.jsx)(i.em,{children:"limit classes"}),". Limit classes can be used to define resource count and bandwidth limits that can be selectively applied to individual accounts in a service instance."]}),"\n",(0,t.jsxs)(i.p,{children:["Limit classes are created by creating a record in the ",(0,t.jsx)(i.code,{children:"limit_classes"})," table in the zrok controller database. The table has this schema:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-sql",children:"CREATE TABLE public.limit_classes (\n id integer NOT NULL,\n backend_mode public.backend_mode,\n environments integer DEFAULT '-1'::integer NOT NULL,\n shares integer DEFAULT '-1'::integer NOT NULL,\n reserved_shares integer DEFAULT '-1'::integer NOT NULL,\n unique_names integer DEFAULT '-1'::integer NOT NULL,\n period_minutes integer DEFAULT 1440 NOT NULL,\n rx_bytes bigint DEFAULT '-1'::integer NOT NULL,\n tx_bytes bigint DEFAULT '-1'::integer NOT NULL,\n total_bytes bigint DEFAULT '-1'::integer NOT NULL,\n limit_action public.limit_action DEFAULT 'limit'::public.limit_action NOT NULL,\n created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,\n updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,\n deleted boolean DEFAULT false NOT NULL\n);\n\n"})}),"\n",(0,t.jsx)(i.p,{children:"This schema supports constructing the 3 different types of limits classes that the system supports."}),"\n",(0,t.jsxs)(i.p,{children:["After defining a limit class in the database, it can be applied to specific user accounts (overriding the relevant parts of the global configuration) by inserting a row into the ",(0,t.jsx)(i.code,{children:"applied_limit_classes"})," table:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-sql",children:"CREATE TABLE public.applied_limit_classes (\n id integer NOT NULL,\n account_id integer NOT NULL,\n limit_class_id integer NOT NULL,\n created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,\n updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,\n deleted boolean DEFAULT false NOT NULL\n);\n"})}),"\n",(0,t.jsxs)(i.p,{children:["Create a row in this table linking the ",(0,t.jsx)(i.code,{children:"account_id"})," to the ",(0,t.jsx)(i.code,{children:"limit_class_id"})," to apply the limit class to a specific user account."]}),"\n",(0,t.jsx)(i.h3,{id:"unscoped-resource-count-classes",children:"Unscoped Resource Count Classes"}),"\n",(0,t.jsxs)(i.p,{children:["To support overriding the resource count limits defined in the global limits configuration, a site administrator can create a limit class by inserting a row into the ",(0,t.jsx)(i.code,{children:"limit_classes"})," table structured like this:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-sql",children:"insert into limit_classes (environments, shares, reserved_shares, unique_names) values (1, 1, 1, 1);\n"})}),"\n",(0,t.jsxs)(i.p,{children:["This creates a limit class that sets the ",(0,t.jsx)(i.code,{children:"environments"}),", ",(0,t.jsx)(i.code,{children:"shares"}),", ",(0,t.jsx)(i.code,{children:"reserved_shares"}),", and ",(0,t.jsx)(i.code,{children:"unique_names"})," all to ",(0,t.jsx)(i.code,{children:"1"}),"."]}),"\n",(0,t.jsx)(i.p,{children:"When this limit class is applied to a user account those values would override the default resource count values configured globally."}),"\n",(0,t.jsxs)(i.p,{children:["Applying an unscoped resource count class ",(0,t.jsx)(i.em,{children:"does not"})," affect the bandwidth limits (either globally configured, or via a limit class)."]}),"\n",(0,t.jsx)(i.h3,{id:"unscoped-bandwidth-classes",children:"Unscoped Bandwidth Classes"}),"\n",(0,t.jsxs)(i.p,{children:["To support overriding the bandwidth limits defined in the global configuration, a site administrator can create a limit class by inserting a row into the ",(0,t.jsx)(i.code,{children:"limit_classes"})," table structured like this:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-sql",children:"insert into limit_classes (period_minutes, total_bytes, limit_action) values (2, 204800, 'limit');\n"})}),"\n",(0,t.jsxs)(i.p,{children:["This inserts a limit class that allows for a total bandwidth transfer of ",(0,t.jsx)(i.code,{children:"204800"})," bytes every ",(0,t.jsx)(i.code,{children:"2"})," minutes."]}),"\n",(0,t.jsx)(i.p,{children:"When this limit class is applied to a user account, those values would override the default bandwidth values configured globally."}),"\n",(0,t.jsxs)(i.p,{children:["Applying an unscoped bandwidth class ",(0,t.jsx)(i.em,{children:"does not"})," affect the resource count limits (either globally configured, or via a limit class)."]}),"\n",(0,t.jsx)(i.h3,{id:"scoped-classes",children:"Scoped Classes"}),"\n",(0,t.jsxs)(i.p,{children:["A scoped limit class specifies ",(0,t.jsx)(i.em,{children:"both"})," the resource counts (",(0,t.jsx)(i.code,{children:"shares"}),", ",(0,t.jsx)(i.code,{children:"reserved_shares"}),", and ",(0,t.jsx)(i.code,{children:"unique_names"}),", but ",(0,t.jsx)(i.em,{children:"NOT"})," ",(0,t.jsx)(i.code,{children:"environments"}),") for a ",(0,t.jsx)(i.em,{children:"specific"})," backend mode. Insert a row like this:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-sql",children:"insert into limit_classes (backend_mode, shares, reserved_shares, unique_names, period_minutes, total_bytes, limit_action) values ('web', 2, 1, 1, 2, 4096000, 'limit');\n"})}),"\n",(0,t.jsxs)(i.p,{children:["Scoped limits are designed to ",(0,t.jsx)(i.em,{children:"increase"})," the limits for a specific backend mode beyond what the global configuration and the unscoped classes provide. The general approach is to use the global configuration and the unscoped classes to provide the general account limits, and then the scoped classes can be used to further increase (or potentially ",(0,t.jsx)(i.em,{children:"decrease"}),") the limits for a specific backend mode."]}),"\n",(0,t.jsx)(i.p,{children:"If a scoped limit class exists for a specific backend mode, then the limits agent will use that limit in making a decision about limiting the resource count or bandwidth. All other types of shares will fall back to the unscoped classes or the global configuration."}),"\n",(0,t.jsx)(i.h2,{id:"limit-actions",children:"Limit Actions"}),"\n",(0,t.jsx)(i.p,{children:"When an account exceeds a bandwidth limit, the limits agent will seek to limit the affected shares (based on the combination of global configuration, unscoped limit classes, and scoped limit classes). It applies the limit by removing the underlying OpenZiti dial policies for any frontends that are trying to access the share."}),"\n",(0,t.jsxs)(i.p,{children:["This means that public frontends will simply return a ",(0,t.jsx)(i.code,{children:"404"})," as if the share is no longer there. Private frontends will also return ",(0,t.jsx)(i.code,{children:"404"})," errors. When the limit is relaxed, the dial policies are put back in place and the share will continue operating normally."]}),"\n",(0,t.jsx)(i.h2,{id:"unlimited-accounts",children:"Unlimited Accounts"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"accounts"})," table in the database includes a ",(0,t.jsx)(i.code,{children:"limitless"})," column. When this column is set to ",(0,t.jsx)(i.code,{children:"true"})," the account is not subject to any of the limits in the system."]}),"\n",(0,t.jsx)(i.h2,{id:"experimental-limits-locking",children:"Experimental Limits Locking"}),"\n",(0,t.jsxs)(i.p,{children:["zrok versions prior to ",(0,t.jsx)(i.code,{children:"v0.4.31"})," had a potential race condition when enforcing resource count limits. This usually only manifested in cases where shares or environments were being allocated programmatically (and fast enough to win the limits race)."]}),"\n",(0,t.jsxs)(i.p,{children:["This occurs due to a lack of transactional database locking around the limited structures. ",(0,t.jsx)(i.code,{children:"v0.4.31"})," includes a pessimistic locking facility that can be enabled ",(0,t.jsx)(i.em,{children:"only"})," on the PostgreSQL store implemention."]}),"\n",(0,t.jsxs)(i.p,{children:["If you're running PostgreSQL for your service instance and you want to enable the new experimental locking facility that eliminates the potential resource count race condition, add the ",(0,t.jsx)(i.code,{children:"enable_locking: true"})," flag to your ",(0,t.jsx)(i.code,{children:"store"})," definition:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-yaml",children:"store:\n enable_locking: true\n"})}),"\n",(0,t.jsx)(i.h2,{id:"caveats",children:"Caveats"}),"\n",(0,t.jsx)(i.p,{children:"There are a number of caveats that are important to understand when using the limits agent with more complicated limits scenarios:"}),"\n",(0,t.jsx)(i.h3,{id:"aggregate-bandwidth",children:"Aggregate Bandwidth"}),"\n",(0,t.jsx)(i.p,{children:"The zrok limits agent is a work in progress. The system currently does not track bandwidth individually for each backend mode type, which means all bandwidth values are aggregated between all of the share types that an account might be using. This will likely change in an upcoming release."}),"\n",(0,t.jsx)(i.h3,{id:"administration-through-sql",children:"Administration Through SQL"}),"\n",(0,t.jsx)(i.p,{children:"There are currently no administrative API endpoints (or corresponding CLI tools) to support creating and applying limit classes in the current release. The limits agent infrastructure was designed to support software integrations that directly manipulate the underlying database structures."}),"\n",(0,t.jsx)(i.p,{children:"A future release may provide API and CLI tooling to support the human administration of the limits agent."}),"\n",(0,t.jsx)(i.h3,{id:"performance",children:"Performance"}),"\n",(0,t.jsxs)(i.p,{children:["Be sure to minimize the number of different periods used for specifying bandwidth limits. Specifying limits in multiple different periods can cause a multiplicity of queries to be executed against the metrics store (InfluxDB). Standardizing on a period like ",(0,t.jsx)(i.code,{children:"24h"})," or ",(0,t.jsx)(i.code,{children:"6h"})," and using that consistently is the best way to to manage the performance of the metrics store."]})]})}function h(e={}){const{wrapper:i}={...(0,s.a)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},1151:(e,i,n)=>{n.d(i,{Z:()=>r,a:()=>l});var t=n(7294);const s={},a=t.createContext(s);function l(e){const i=t.useContext(a);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function r(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:l(e.components),t.createElement(a.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/d768dc0f.9162b0b5.js b/assets/js/d768dc0f.d9dde1b6.js similarity index 99% rename from assets/js/d768dc0f.9162b0b5.js rename to assets/js/d768dc0f.d9dde1b6.js index 07292d01..235bba60 100644 --- a/assets/js/d768dc0f.9162b0b5.js +++ b/assets/js/d768dc0f.d9dde1b6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5882],{475:(e,n,r)=>{r.r(n),r.d(n,{assets:()=>c,contentTitle:()=>s,default:()=>h,frontMatter:()=>i,metadata:()=>l,toc:()=>d});var o=r(5893),t=r(1151);const i={sidebar_position:40,title:"Self-Hosting Guide for Linux",sidebar_label:"Linux"},s=void 0,l={id:"guides/self-hosting/linux/index",title:"Self-Hosting Guide for Linux",description:"Walkthrough Video",source:"@site/../docs/guides/self-hosting/linux/index.mdx",sourceDirName:"guides/self-hosting/linux",slug:"/guides/self-hosting/linux/",permalink:"/docs/guides/self-hosting/linux/",draft:!1,unlisted:!1,editUrl:"https://github.com/openziti/zrok/blob/main/docs/../docs/guides/self-hosting/linux/index.mdx",tags:[],version:"current",sidebarPosition:40,frontMatter:{sidebar_position:40,title:"Self-Hosting Guide for Linux",sidebar_label:"Linux"},sidebar:"tutorialSidebar",previous:{title:"Self Hosting",permalink:"/docs/category/self-hosting"},next:{title:"NGINX TLS",permalink:"/docs/guides/self-hosting/linux/nginx"}},c={},d=[{value:"Walkthrough Video",id:"walkthrough-video",level:2},{value:"Before you Begin",id:"before-you-begin",level:2},{value:"OpenZiti",id:"openziti",level:2},{value:"Install zrok",id:"install-zrok",level:2},{value:"Configure the Controller",id:"configure-the-controller",level:2},{value:"Environment Variables",id:"environment-variables",level:2},{value:"Bootstrap OpenZiti for zrok",id:"bootstrap-openziti-for-zrok",level:2},{value:"Run zrok Controller",id:"run-zrok-controller",level:2},{value:"Create zrok Frontend",id:"create-zrok-frontend",level:2},{value:"Configure the Public Frontend",id:"configure-the-public-frontend",level:2},{value:"Start Public Frontend",id:"start-public-frontend",level:2},{value:"Create a User Account",id:"create-a-user-account",level:2},{value:"Invite Additional Users",id:"invite-additional-users",level:2},{value:"Enable Your Environment",id:"enable-your-environment",level:2}];function a(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,t.a)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h2,{id:"walkthrough-video",children:"Walkthrough Video"}),"\n",(0,o.jsx)("iframe",{width:"100%",height:"315",src:"https://www.youtube.com/embed/870A5dke_u4",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",allowfullscreen:!0}),"\n",(0,o.jsx)(n.h2,{id:"before-you-begin",children:"Before you Begin"}),"\n",(0,o.jsxs)(n.p,{children:["This will get you up and running with a self-hosted instance of ",(0,o.jsx)(n.code,{children:"zrok"}),". I'll assume you have the following:"]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"a Linux server with a public IP"}),"\n",(0,o.jsxs)(n.li,{children:["a wildcard DNS record like ",(0,o.jsx)(n.code,{children:"*.zrok.quigley.com"})," that resolves to the server IP"]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"openziti",children:"OpenZiti"}),"\n",(0,o.jsxs)(n.p,{children:['OpenZiti (a.k.a. "Ziti") provides secure network backhaul for ',(0,o.jsx)(n.code,{children:"zrok"})," public and private shares. You need a Ziti Controller and a Ziti Router. You can run everything on the same Linux VPS."]}),"\n",(0,o.jsxs)(n.ol,{children:["\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Install the Ziti Controller package by following the ",(0,o.jsx)(n.a,{href:"https://openziti.io/docs/category/deployments",children:"Linux controller deployment guide"}),"."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Ensure your answer file (",(0,o.jsx)(n.code,{children:"/opt/openziti/etc/controller/bootstrap.env"}),") has the FQDN of your Linux server and an admin password defined."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Ensure your firewall allows the controller port from the answer file."}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Start the controller service (",(0,o.jsx)(n.code,{children:"ziti-controller.service"}),") and check the status."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Log in to the Ziti Controller"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"ziti edge login localhost:1280 -u admin -p \n"})}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Administratively Create a Ziti Router"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:'ziti edge create edge-router "router1" -o /tmp/router1.jwt\n'})}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Install the Ziti Router package by following ",(0,o.jsx)(n.a,{href:"https://openziti.io/docs/category/deployments",children:"the Linux router deployment guide"}),"."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Ensure your answer file (",(0,o.jsx)(n.code,{children:"/opt/openziti/etc/router/bootstrap.env"}),") has the FQDN of your Linux server for both controller and router addresses and the enrollment token from the previous step."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Ensure your firewall allows the router port from the answer file."}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Start the router service (",(0,o.jsx)(n.code,{children:"ziti-router.service"}),") and check the status."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Verify the new router is online."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"ziti edge list edge-routers\n"})}),"\n"]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"install-zrok",children:"Install zrok"}),"\n",(0,o.jsxs)(n.p,{children:["Debian and RPM packages are available for ",(0,o.jsx)(n.code,{children:"zrok"}),"."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"sudo apt install zrok\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Follow ",(0,o.jsx)(n.a,{href:"/docs/guides/install/linux",children:"the Linux installation guide"})," to install the ",(0,o.jsx)(n.code,{children:"zrok"})," package from the repository or manually install the binary for your platform."]}),"\n",(0,o.jsx)(n.h2,{id:"configure-the-controller",children:"Configure the Controller"}),"\n",(0,o.jsxs)(n.p,{children:["Create a ",(0,o.jsx)(n.code,{children:"zrok"})," controller configuration file in ",(0,o.jsx)(n.code,{children:"etc/ctrl.yml"}),". The controller can terminate TLS or you may front the server with a reverse proxy that continually renews the necessary wildcard certificate (e.g., Caddy w/ a DNS provider plugin). This example will expose the non-TLS listener for the controller."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:'# _____ __ ___ | | __\n# |_ / \'__/ _ \\| |/ /\n# / /| | | (_) | <\n# /___|_| \\___/|_|\\_\\\n# controller configuration\n\nv: 3\n\nadmin:\n # generate these admin tokens from a source of randomness, e.g. \n # LC_ALL=C tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c32\n secrets:\n - Q8V0LqnNb5wNX9kE1fgQ0H6VlcvJybB1 # be sure to change this!\n\nendpoint:\n host: 0.0.0.0\n port: 18080\n\ninvites:\n invites_open: true\n\nstore:\n path: zrok.db\n type: sqlite3\n\nziti:\n api_endpoint: "https://127.0.0.1:1280"\n username: admin\n password: "XO0xHp75uuyeireO2xmmVlK91T7B9fpD"\n\n# you can use certbot to renew the wildcard cert for the controller with a DNS provider API token or front this `zrok` # controller with Caddy\n#tls:\n# cert_path: "/Path/To/Cert/zrok.crt"\n# key_path: "/Path/To/Cert/zrok.key"\n\n'})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"admin"})," section defines privileged administrative credentials and must be set in the ",(0,o.jsx)(n.code,{children:"ZROK_ADMIN_TOKEN"})," environment variable in shells where you want to run ",(0,o.jsx)(n.code,{children:"zrok admin"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"endpoint"})," section defines where your ",(0,o.jsx)(n.code,{children:"zrok"})," controller will listen."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"store"})," section defines the local ",(0,o.jsx)(n.code,{children:"sqlite3"})," database used by the controller."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ziti"})," section defines how the ",(0,o.jsx)(n.code,{children:"zrok"})," controller should communicate with your OpenZiti installation. When using the OpenZiti quickstart, an administrative password will be generated; the ",(0,o.jsx)(n.code,{children:"password"})," in the ",(0,o.jsx)(n.code,{children:"ziti"})," stanza should reflect this password."]}),"\n",(0,o.jsxs)(n.admonition,{type:"note",children:[(0,o.jsxs)(n.p,{children:["Be sure to see the ",(0,o.jsxs)(n.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:r(1855).Z+"",children:["reference configuration at ",(0,o.jsx)(n.code,{children:"etc/ctrl.yml"})]})," for the complete documentation of the current configuration file format for the ",(0,o.jsx)(n.code,{children:"zrok"})," controller and service instance components."]}),(0,o.jsxs)(n.p,{children:["See the separate guides on ",(0,o.jsx)(n.a,{href:"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics",children:"configuring metrics"})," and ",(0,o.jsx)(n.a,{href:"/docs/guides/self-hosting/metrics-and-limits/configuring-limits",children:"configuring limits"})," for details about both of these specialized areas of service instance configuration."]})]}),"\n",(0,o.jsx)(n.h2,{id:"environment-variables",children:"Environment Variables"}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok"})," binaries are configured to work with the global ",(0,o.jsx)(n.code,{children:"zrok.io"})," service, and default to using ",(0,o.jsx)(n.code,{children:"api.zrok.io"})," as the endpoint for communicating with the service."]}),"\n",(0,o.jsxs)(n.p,{children:["To work with a self-hosted ",(0,o.jsx)(n.code,{children:"zrok"})," deployment, you'll need to set the ",(0,o.jsx)(n.code,{children:"ZROK_API_ENDPOINT"})," environment variable to point to the address where your ",(0,o.jsx)(n.code,{children:"zrok"})," controller will be listening, according to ",(0,o.jsx)(n.code,{children:"endpoint"})," in the configuration file above."]}),"\n",(0,o.jsx)(n.p,{children:"In my case, I've set:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"export ZROK_API_ENDPOINT=http://127.0.0.1:18080\n"})}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsxs)(n.a,{href:"/docs/guides/self-hosting/instance-configuration",children:["Read more about configuring your self-hosted ",(0,o.jsx)(n.code,{children:"zrok"})," instance"]}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"bootstrap-openziti-for-zrok",children:"Bootstrap OpenZiti for zrok"}),"\n",(0,o.jsxs)(n.p,{children:["With your OpenZiti network running and your configuration saved to a local file (I refer to mine as ",(0,o.jsx)(n.code,{children:"etc/ctrl.yml"})," in these examples), you're ready to bootstrap the Ziti network."]}),"\n",(0,o.jsxs)(n.p,{children:["Use the ",(0,o.jsx)(n.code,{children:"zrok admin bootstrap"})," command to bootstrap like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok admin bootstrap etc/ctrl.yml\n[ 0.002] INFO main.(*adminBootstrap).run: {\n\t...\n}\n[ 0.002] INFO zrok/controller/store.Open: database connected\n[ 0.006] INFO zrok/controller/store.(*Store).migrate: applied 0 migrations\n[ 0.006] INFO zrok/controller.Bootstrap: connecting to the ziti edge management api\n[ 0.039] INFO zrok/controller.Bootstrap: creating identity for controller ziti access\n[ 0.071] INFO zrok/controller.Bootstrap: controller identity: jKd8AINSz\n[ 0.082] INFO zrok/controller.assertIdentity: asserted identity 'jKd8AINSz'\n[ 0.085] INFO zrok/controller.assertErpForIdentity: asserted erps for 'ctrl' (jKd8AINSz)\n[ 0.085] INFO zrok/controller.Bootstrap: creating identity for frontend ziti access\n[ 0.118] INFO zrok/controller.Bootstrap: frontend identity: sqJRAINSiB\n[ 0.119] INFO zrok/controller.assertIdentity: asserted identity 'sqJRAINSiB'\n[ 0.120] INFO zrok/controller.assertErpForIdentity: asserted erps for 'frontend' (sqJRAINSiB)\n[ 0.120] WARNING zrok/controller.Bootstrap: missing public frontend for ziti id 'sqJRAINSiB'; please use 'zrok admin create frontend sqJRAINSiB public https://{token}.your.dns.name' to create a frontend instance\n[ 0.123] INFO zrok/controller.assertZrokProxyConfigType: found 'zrok.proxy.v1' config type with id '33CyjNbIepkXHN5VzGDA8L'\n[ 0.124] INFO zrok/controller.assertMetricsService: creating 'metrics' service\n[ 0.126] INFO zrok/controller.assertMetricsService: asserted 'metrics' service (5RpPZZ7T8bZf1ENjwGiPc3)\n[ 0.128] INFO zrok/controller.assertMetricsSerp: creating 'metrics' serp\n[ 0.130] INFO zrok/controller.assertMetricsSerp: asserted 'metrics' serp\n[ 0.134] INFO zrok/controller.assertCtrlMetricsBind: creating 'ctrl-metrics-bind' service policy\n[ 0.135] INFO zrok/controller.assertCtrlMetricsBind: asserted 'ctrl-metrics-bind' service policy\n[ 0.138] INFO zrok/controller.assertFrontendMetricsDial: creating 'frontend-metrics-dial' service policy\n[ 0.140] INFO zrok/controller.assertFrontendMetricsDial: asserted 'frontend-metrics-dial' service policy\n[ 0.140] INFO main.(*adminBootstrap).run: bootstrap complete!\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok admin bootstrap"})," command configures the ",(0,o.jsx)(n.code,{children:"zrok"})," database, the necessary OpenZiti identities, and all of the OpenZiti policies required to run a ",(0,o.jsx)(n.code,{children:"zrok"})," service."]}),"\n",(0,o.jsx)(n.p,{children:"Notice this warning:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:"[ 0.120] WARNING zrok/controller.Bootstrap: missing public frontend for ziti id 'sqJRAINSiB'; please use 'zrok admin create frontend sqJRAINSiB public https://{token}.your.dns.name' to create a frontend instance\n"})}),"\n",(0,o.jsxs)(n.p,{children:["If you find it necessary to re-run the ",(0,o.jsx)(n.code,{children:"zrok admin bootstrap"})," command, you may need to add the ",(0,o.jsx)(n.code,{children:"--skip-frontend"})," flag to avoid re-creating the default ",(0,o.jsx)(n.code,{children:"public"})," frontend's Ziti identity and router policy."]}),"\n",(0,o.jsx)(n.h2,{id:"run-zrok-controller",children:"Run zrok Controller"}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok"}),' bootstrap process wants us to create a "public frontend" for our service. ',(0,o.jsx)(n.code,{children:"zrok"})," uses public frontends to allow users to specify where they would like public traffic to ingress from."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok admin create frontend"})," command requires a running ",(0,o.jsx)(n.code,{children:"zrok"})," controller, so let's start that up first:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok controller etc/ctrl.yml \n[ 0.003] INFO main.(*controllerCommand).run: {\n\t...\n}\n[ 0.016] INFO zrok/controller.inspectZiti: inspecting ziti controller configuration\n[ 0.048] INFO zrok/controller.findZrokProxyConfigType: found 'zrok.proxy.v1' config type with id '33CyjNbIepkXHN5VzGDA8L'\n[ 0.048] INFO zrok/controller/store.Open: database connected\n[ 0.048] INFO zrok/controller/store.(*Store).migrate: applied 0 migrations\n[ 0.049] INFO zrok/controller.(*metricsAgent).run: starting\n[ 0.064] INFO zrok/rest_server_zrok.setupGlobalMiddleware: configuring\n[ 0.064] INFO zrok/ui.StaticBuilder: building\n[ 0.065] INFO zrok/rest_server_zrok.(*Server).Logf: Serving zrok at http://[::]:18080\n[ 0.085] INFO zrok/controller.(*metricsAgent).listen: started\n"})}),"\n",(0,o.jsx)(n.h2,{id:"create-zrok-frontend",children:"Create zrok Frontend"}),"\n",(0,o.jsxs)(n.p,{children:["With our ",(0,o.jsx)(n.code,{children:"ZROK_ADMIN_TOKEN"})," and ",(0,o.jsx)(n.code,{children:"ZROK_API_ENDPOINT"})," environment variables set, we can create our public frontend like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok admin create frontend sqJRAINSiB public http://{token}.zrok.quigley.com:8080\n[ 0.037] INFO main.(*adminCreateFrontendCommand).run: created global public frontend 'WEirJNHVlcW9'\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The id of the frontend was emitted earlier in by the ",(0,o.jsx)(n.code,{children:"zrok"})," controller when we ran the bootstrap command. If you don't have that log message the you can find the id again with the ",(0,o.jsx)(n.code,{children:"ziti"})," CLI like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"# log in as admin (example)\nziti edge login localhost:1280 -u admin -p XO0xHp75uuyeireO2xmmVlK91T7B9fpD\n\n# list Ziti identities created by the quickstart and bootstrap\nziti edge list identities\n"})}),"\n",(0,o.jsx)(n.p,{children:'The id is shown for the frontend identity named "public."'}),"\n",(0,o.jsxs)(n.p,{children:["Nice work! The ",(0,o.jsx)(n.code,{children:"zrok"})," controller is fully configured now that you have created the ",(0,o.jsx)(n.code,{children:"zrok"})," frontend."]}),"\n",(0,o.jsx)(n.h2,{id:"configure-the-public-frontend",children:"Configure the Public Frontend"}),"\n",(0,o.jsxs)(n.p,{children:["Create an http frontend configuration file in ",(0,o.jsx)(n.code,{children:"etc/http-frontend.yml"}),"."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"v: 3\nhost_match: zrok.quigley.com\naddress: 0.0.0.0:8080\n"})}),"\n",(0,o.jsxs)(n.p,{children:["This frontend config file has a ",(0,o.jsx)(n.code,{children:"host_match"})," pattern that represents the DNS zone you're using with this instance of ",(0,o.jsx)(n.code,{children:"zrok"}),". Incoming HTTP requests with a matching ",(0,o.jsx)(n.code,{children:"Host"})," header will be handled by this frontend. You may also specify the interface address where the frontend will listen for public access requests."]}),"\n",(0,o.jsxs)(n.p,{children:["The frontend does not provide server TLS, but you may front the server with a reverse proxy. It is essential the reverse proxy forwards the ",(0,o.jsx)(n.code,{children:"Host"})," header supplied by the viewer. This example will expose the non-TLS listener for the frontend."]}),"\n",(0,o.jsxs)(n.p,{children:["You can also specify an ",(0,o.jsx)(n.code,{children:"oauth"})," configuration in this file, full details of are found in ",(0,o.jsx)(n.a,{href:"/docs/guides/self-hosting/oauth/configuring-oauth#configuring-your-public-frontend",children:"OAuth Public Frontend Configuration"}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"start-public-frontend",children:"Start Public Frontend"}),"\n",(0,o.jsx)(n.p,{children:"In another terminal window, run:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok access public etc/http-frontend.yml\n[ 0.002] INFO main.(*accessPublicCommand).run: {\n\t...\n}\n[ 0.002] INFO zrok/endpoints/public_frontend.newMetricsAgent: loaded 'public' identity\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok"})," frontend uses the ",(0,o.jsx)(n.code,{children:"public"})," identity created during the bootstrap process to securely access zrok backends. to provide public access for the ",(0,o.jsx)(n.code,{children:"zrok"})," deployment. It is expected that the configured listener for this frontend corresponds to the DNS template specified when creating the public frontend record above."]}),"\n",(0,o.jsx)(n.h2,{id:"create-a-user-account",children:"Create a User Account"}),"\n",(0,o.jsxs)(n.p,{children:["With our ",(0,o.jsx)(n.code,{children:"ZROK_ADMIN_TOKEN"})," and ",(0,o.jsx)(n.code,{children:"ZROK_API_ENDPOINT"})," environment variables set, we can create our first user account."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"zrok admin create account etc/ctrl.yml \n"})}),"\n",(0,o.jsx)(n.p,{children:"The output is the account token you will use to enable each device's zrok environment."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-buttonless",metastring:'title="Example output"',children:"SuGzRPjVDIcF\n"})}),"\n",(0,o.jsx)(n.h2,{id:"invite-additional-users",children:"Invite Additional Users"}),"\n",(0,o.jsxs)(n.p,{children:["Offer this onboarding method to your users if you have configured an email-sending service in your ",(0,o.jsx)(n.code,{children:"zrok"})," controller configuration."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok invite\nNew Email: user@domain.com\nConfirm Email: user@domain.com\ninvitation sent to 'user@domain.com'!\n"})}),"\n",(0,o.jsxs)(n.p,{children:["If you look at the console output from your ",(0,o.jsx)(n.code,{children:"zrok"})," controller, you'll see a message like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:"[ 238.168] INFO zrok/controller.(*inviteHandler).Handle: account request for 'user@domain.com' has registration token 'U2Ewt1UCn3ql'\n"})}),"\n",(0,o.jsxs)(n.p,{children:["You can access your ",(0,o.jsx)(n.code,{children:"zrok"})," controller's registration UI by pointing a web browser at:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:"http://localhost:18080/register/U2Ewt1UCn3ql\n"})}),"\n",(0,o.jsx)(n.p,{children:"The UI will ask you to set a password for your new account. Go ahead and do that."}),"\n",(0,o.jsx)(n.p,{children:"After doing that, I see the following output in my controller console:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:"[ 516.778] INFO zrok/controller.(*registerHandler).Handle: created account 'user@domain.com' with token 'SuGzRPjVDIcF'\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Keep track of the token listed above (",(0,o.jsx)(n.code,{children:"SuGzRPjVDIcF"}),"). We'll use this to enable our shell for this ",(0,o.jsx)(n.code,{children:"zrok"})," deployment."]}),"\n",(0,o.jsx)(n.h2,{id:"enable-your-environment",children:"Enable Your Environment"}),"\n",(0,o.jsx)(n.p,{children:"On another device that can reach your Linux server by FQDN, configure the API endpoint and enable the environment with the account token you received when you created the first user account."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"export ZROK_API_ENDPOINT=https://zrok.quigley.com\n# or\nzrok config set apiEndpoint https://zrok.quigley.com\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"zrok enable SuGzRPjVDIcF\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-buttonless",metastring:'title="Example output"',children:"zrok environment '2AS1WZ3Sz' enabled for 'SuGzRPjVDIcF'\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"zrok status --secrets\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-buttonless",metastring:'title="Example output"',children:"Config:\n\n CONFIG VALUE SOURCE\n apiEndpoint https://zrok.quigley.com env\n\nEnvironment:\n\n PROPERTY VALUE\n Secret Token SuGzRPjVDIcF\n Ziti Identity 2AS1WZ3Sz\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Congratulations. You have a working ",(0,o.jsx)(n.code,{children:"zrok"})," environment!"]})]})}function h(e={}){const{wrapper:n}={...(0,t.a)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(a,{...e})}):a(e)}},1855:(e,n,r)=>{r.d(n,{Z:()=>o});const o=r.p+"assets/files/ctrl-6c22ae02cafe307b82e5a1f783497950.yml"},1151:(e,n,r)=>{r.d(n,{Z:()=>l,a:()=>s});var o=r(7294);const t={},i=o.createContext(t);function s(e){const n=o.useContext(i);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:s(e.components),o.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5882],{475:(e,n,r)=>{r.r(n),r.d(n,{assets:()=>c,contentTitle:()=>s,default:()=>h,frontMatter:()=>i,metadata:()=>l,toc:()=>d});var o=r(5893),t=r(1151);const i={sidebar_position:40,title:"Self-Hosting Guide for Linux",sidebar_label:"Linux"},s=void 0,l={id:"guides/self-hosting/linux/index",title:"Self-Hosting Guide for Linux",description:"Walkthrough Video",source:"@site/../docs/guides/self-hosting/linux/index.mdx",sourceDirName:"guides/self-hosting/linux",slug:"/guides/self-hosting/linux/",permalink:"/docs/guides/self-hosting/linux/",draft:!1,unlisted:!1,editUrl:"https://github.com/openziti/zrok/blob/main/docs/../docs/guides/self-hosting/linux/index.mdx",tags:[],version:"current",sidebarPosition:40,frontMatter:{sidebar_position:40,title:"Self-Hosting Guide for Linux",sidebar_label:"Linux"},sidebar:"tutorialSidebar",previous:{title:"Self Hosting",permalink:"/docs/category/self-hosting"},next:{title:"NGINX TLS",permalink:"/docs/guides/self-hosting/linux/nginx"}},c={},d=[{value:"Walkthrough Video",id:"walkthrough-video",level:2},{value:"Before you Begin",id:"before-you-begin",level:2},{value:"OpenZiti",id:"openziti",level:2},{value:"Install zrok",id:"install-zrok",level:2},{value:"Configure the Controller",id:"configure-the-controller",level:2},{value:"Environment Variables",id:"environment-variables",level:2},{value:"Bootstrap OpenZiti for zrok",id:"bootstrap-openziti-for-zrok",level:2},{value:"Run zrok Controller",id:"run-zrok-controller",level:2},{value:"Create zrok Frontend",id:"create-zrok-frontend",level:2},{value:"Configure the Public Frontend",id:"configure-the-public-frontend",level:2},{value:"Start Public Frontend",id:"start-public-frontend",level:2},{value:"Create a User Account",id:"create-a-user-account",level:2},{value:"Invite Additional Users",id:"invite-additional-users",level:2},{value:"Enable Your Environment",id:"enable-your-environment",level:2}];function a(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,t.a)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h2,{id:"walkthrough-video",children:"Walkthrough Video"}),"\n",(0,o.jsx)("iframe",{width:"100%",height:"315",src:"https://www.youtube.com/embed/870A5dke_u4",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",allowfullscreen:!0}),"\n",(0,o.jsx)(n.h2,{id:"before-you-begin",children:"Before you Begin"}),"\n",(0,o.jsxs)(n.p,{children:["This will get you up and running with a self-hosted instance of ",(0,o.jsx)(n.code,{children:"zrok"}),". I'll assume you have the following:"]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"a Linux server with a public IP"}),"\n",(0,o.jsxs)(n.li,{children:["a wildcard DNS record like ",(0,o.jsx)(n.code,{children:"*.zrok.quigley.com"})," that resolves to the server IP"]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"openziti",children:"OpenZiti"}),"\n",(0,o.jsxs)(n.p,{children:['OpenZiti (a.k.a. "Ziti") provides secure network backhaul for ',(0,o.jsx)(n.code,{children:"zrok"})," public and private shares. You need a Ziti Controller and a Ziti Router. You can run everything on the same Linux VPS."]}),"\n",(0,o.jsxs)(n.ol,{children:["\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Install the Ziti Controller package by following the ",(0,o.jsx)(n.a,{href:"https://openziti.io/docs/category/deployments",children:"Linux controller deployment guide"}),"."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Ensure your answer file (",(0,o.jsx)(n.code,{children:"/opt/openziti/etc/controller/bootstrap.env"}),") has the FQDN of your Linux server and an admin password defined."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Ensure your firewall allows the controller port from the answer file."}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Start the controller service (",(0,o.jsx)(n.code,{children:"ziti-controller.service"}),") and check the status."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Log in to the Ziti Controller"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"ziti edge login localhost:1280 -u admin -p \n"})}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Administratively Create a Ziti Router"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:'ziti edge create edge-router "router1" -o /tmp/router1.jwt\n'})}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Install the Ziti Router package by following ",(0,o.jsx)(n.a,{href:"https://openziti.io/docs/category/deployments",children:"the Linux router deployment guide"}),"."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Ensure your answer file (",(0,o.jsx)(n.code,{children:"/opt/openziti/etc/router/bootstrap.env"}),") has the FQDN of your Linux server for both controller and router addresses and the enrollment token from the previous step."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Ensure your firewall allows the router port from the answer file."}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Start the router service (",(0,o.jsx)(n.code,{children:"ziti-router.service"}),") and check the status."]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Verify the new router is online."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"ziti edge list edge-routers\n"})}),"\n"]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"install-zrok",children:"Install zrok"}),"\n",(0,o.jsxs)(n.p,{children:["Debian and RPM packages are available for ",(0,o.jsx)(n.code,{children:"zrok"}),"."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"sudo apt install zrok\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Follow ",(0,o.jsx)(n.a,{href:"/docs/guides/install/linux",children:"the Linux installation guide"})," to install the ",(0,o.jsx)(n.code,{children:"zrok"})," package from the repository or manually install the binary for your platform."]}),"\n",(0,o.jsx)(n.h2,{id:"configure-the-controller",children:"Configure the Controller"}),"\n",(0,o.jsxs)(n.p,{children:["Create a ",(0,o.jsx)(n.code,{children:"zrok"})," controller configuration file in ",(0,o.jsx)(n.code,{children:"etc/ctrl.yml"}),". The controller can terminate TLS or you may front the server with a reverse proxy that continually renews the necessary wildcard certificate (e.g., Caddy w/ a DNS provider plugin). This example will expose the non-TLS listener for the controller."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:'# _____ __ ___ | | __\n# |_ / \'__/ _ \\| |/ /\n# / /| | | (_) | <\n# /___|_| \\___/|_|\\_\\\n# controller configuration\n\nv: 3\n\nadmin:\n # generate these admin tokens from a source of randomness, e.g. \n # LC_ALL=C tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c32\n secrets:\n - Q8V0LqnNb5wNX9kE1fgQ0H6VlcvJybB1 # be sure to change this!\n\nendpoint:\n host: 0.0.0.0\n port: 18080\n\ninvites:\n invites_open: true\n\nstore:\n path: zrok.db\n type: sqlite3\n\nziti:\n api_endpoint: "https://127.0.0.1:1280"\n username: admin\n password: "XO0xHp75uuyeireO2xmmVlK91T7B9fpD"\n\n# you can use certbot to renew the wildcard cert for the controller with a DNS provider API token or front this `zrok` # controller with Caddy\n#tls:\n# cert_path: "/Path/To/Cert/zrok.crt"\n# key_path: "/Path/To/Cert/zrok.key"\n\n'})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"admin"})," section defines privileged administrative credentials and must be set in the ",(0,o.jsx)(n.code,{children:"ZROK_ADMIN_TOKEN"})," environment variable in shells where you want to run ",(0,o.jsx)(n.code,{children:"zrok admin"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"endpoint"})," section defines where your ",(0,o.jsx)(n.code,{children:"zrok"})," controller will listen."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"store"})," section defines the local ",(0,o.jsx)(n.code,{children:"sqlite3"})," database used by the controller."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ziti"})," section defines how the ",(0,o.jsx)(n.code,{children:"zrok"})," controller should communicate with your OpenZiti installation. When using the OpenZiti quickstart, an administrative password will be generated; the ",(0,o.jsx)(n.code,{children:"password"})," in the ",(0,o.jsx)(n.code,{children:"ziti"})," stanza should reflect this password."]}),"\n",(0,o.jsxs)(n.admonition,{type:"note",children:[(0,o.jsxs)(n.p,{children:["Be sure to see the ",(0,o.jsxs)(n.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:r(1855).Z+"",children:["reference configuration at ",(0,o.jsx)(n.code,{children:"etc/ctrl.yml"})]})," for the complete documentation of the current configuration file format for the ",(0,o.jsx)(n.code,{children:"zrok"})," controller and service instance components."]}),(0,o.jsxs)(n.p,{children:["See the separate guides on ",(0,o.jsx)(n.a,{href:"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics",children:"configuring metrics"})," and ",(0,o.jsx)(n.a,{href:"/docs/guides/self-hosting/metrics-and-limits/configuring-limits",children:"configuring limits"})," for details about both of these specialized areas of service instance configuration."]})]}),"\n",(0,o.jsx)(n.h2,{id:"environment-variables",children:"Environment Variables"}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok"})," binaries are configured to work with the global ",(0,o.jsx)(n.code,{children:"zrok.io"})," service, and default to using ",(0,o.jsx)(n.code,{children:"api.zrok.io"})," as the endpoint for communicating with the service."]}),"\n",(0,o.jsxs)(n.p,{children:["To work with a self-hosted ",(0,o.jsx)(n.code,{children:"zrok"})," deployment, you'll need to set the ",(0,o.jsx)(n.code,{children:"ZROK_API_ENDPOINT"})," environment variable to point to the address where your ",(0,o.jsx)(n.code,{children:"zrok"})," controller will be listening, according to ",(0,o.jsx)(n.code,{children:"endpoint"})," in the configuration file above."]}),"\n",(0,o.jsx)(n.p,{children:"In my case, I've set:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"export ZROK_API_ENDPOINT=http://127.0.0.1:18080\n"})}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsxs)(n.a,{href:"/docs/guides/self-hosting/instance-configuration",children:["Read more about configuring your self-hosted ",(0,o.jsx)(n.code,{children:"zrok"})," instance"]}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"bootstrap-openziti-for-zrok",children:"Bootstrap OpenZiti for zrok"}),"\n",(0,o.jsxs)(n.p,{children:["With your OpenZiti network running and your configuration saved to a local file (I refer to mine as ",(0,o.jsx)(n.code,{children:"etc/ctrl.yml"})," in these examples), you're ready to bootstrap the Ziti network."]}),"\n",(0,o.jsxs)(n.p,{children:["Use the ",(0,o.jsx)(n.code,{children:"zrok admin bootstrap"})," command to bootstrap like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok admin bootstrap etc/ctrl.yml\n[ 0.002] INFO main.(*adminBootstrap).run: {\n\t...\n}\n[ 0.002] INFO zrok/controller/store.Open: database connected\n[ 0.006] INFO zrok/controller/store.(*Store).migrate: applied 0 migrations\n[ 0.006] INFO zrok/controller.Bootstrap: connecting to the ziti edge management api\n[ 0.039] INFO zrok/controller.Bootstrap: creating identity for controller ziti access\n[ 0.071] INFO zrok/controller.Bootstrap: controller identity: jKd8AINSz\n[ 0.082] INFO zrok/controller.assertIdentity: asserted identity 'jKd8AINSz'\n[ 0.085] INFO zrok/controller.assertErpForIdentity: asserted erps for 'ctrl' (jKd8AINSz)\n[ 0.085] INFO zrok/controller.Bootstrap: creating identity for frontend ziti access\n[ 0.118] INFO zrok/controller.Bootstrap: frontend identity: sqJRAINSiB\n[ 0.119] INFO zrok/controller.assertIdentity: asserted identity 'sqJRAINSiB'\n[ 0.120] INFO zrok/controller.assertErpForIdentity: asserted erps for 'frontend' (sqJRAINSiB)\n[ 0.120] WARNING zrok/controller.Bootstrap: missing public frontend for ziti id 'sqJRAINSiB'; please use 'zrok admin create frontend sqJRAINSiB public https://{token}.your.dns.name' to create a frontend instance\n[ 0.123] INFO zrok/controller.assertZrokProxyConfigType: found 'zrok.proxy.v1' config type with id '33CyjNbIepkXHN5VzGDA8L'\n[ 0.124] INFO zrok/controller.assertMetricsService: creating 'metrics' service\n[ 0.126] INFO zrok/controller.assertMetricsService: asserted 'metrics' service (5RpPZZ7T8bZf1ENjwGiPc3)\n[ 0.128] INFO zrok/controller.assertMetricsSerp: creating 'metrics' serp\n[ 0.130] INFO zrok/controller.assertMetricsSerp: asserted 'metrics' serp\n[ 0.134] INFO zrok/controller.assertCtrlMetricsBind: creating 'ctrl-metrics-bind' service policy\n[ 0.135] INFO zrok/controller.assertCtrlMetricsBind: asserted 'ctrl-metrics-bind' service policy\n[ 0.138] INFO zrok/controller.assertFrontendMetricsDial: creating 'frontend-metrics-dial' service policy\n[ 0.140] INFO zrok/controller.assertFrontendMetricsDial: asserted 'frontend-metrics-dial' service policy\n[ 0.140] INFO main.(*adminBootstrap).run: bootstrap complete!\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok admin bootstrap"})," command configures the ",(0,o.jsx)(n.code,{children:"zrok"})," database, the necessary OpenZiti identities, and all of the OpenZiti policies required to run a ",(0,o.jsx)(n.code,{children:"zrok"})," service."]}),"\n",(0,o.jsx)(n.p,{children:"Notice this warning:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:"[ 0.120] WARNING zrok/controller.Bootstrap: missing public frontend for ziti id 'sqJRAINSiB'; please use 'zrok admin create frontend sqJRAINSiB public https://{token}.your.dns.name' to create a frontend instance\n"})}),"\n",(0,o.jsxs)(n.p,{children:["If you find it necessary to re-run the ",(0,o.jsx)(n.code,{children:"zrok admin bootstrap"})," command, you may need to add the ",(0,o.jsx)(n.code,{children:"--skip-frontend"})," flag to avoid re-creating the default ",(0,o.jsx)(n.code,{children:"public"})," frontend's Ziti identity and router policy."]}),"\n",(0,o.jsx)(n.h2,{id:"run-zrok-controller",children:"Run zrok Controller"}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok"}),' bootstrap process wants us to create a "public frontend" for our service. ',(0,o.jsx)(n.code,{children:"zrok"})," uses public frontends to allow users to specify where they would like public traffic to ingress from."]}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok admin create frontend"})," command requires a running ",(0,o.jsx)(n.code,{children:"zrok"})," controller, so let's start that up first:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok controller etc/ctrl.yml \n[ 0.003] INFO main.(*controllerCommand).run: {\n\t...\n}\n[ 0.016] INFO zrok/controller.inspectZiti: inspecting ziti controller configuration\n[ 0.048] INFO zrok/controller.findZrokProxyConfigType: found 'zrok.proxy.v1' config type with id '33CyjNbIepkXHN5VzGDA8L'\n[ 0.048] INFO zrok/controller/store.Open: database connected\n[ 0.048] INFO zrok/controller/store.(*Store).migrate: applied 0 migrations\n[ 0.049] INFO zrok/controller.(*metricsAgent).run: starting\n[ 0.064] INFO zrok/rest_server_zrok.setupGlobalMiddleware: configuring\n[ 0.064] INFO zrok/ui.StaticBuilder: building\n[ 0.065] INFO zrok/rest_server_zrok.(*Server).Logf: Serving zrok at http://[::]:18080\n[ 0.085] INFO zrok/controller.(*metricsAgent).listen: started\n"})}),"\n",(0,o.jsx)(n.h2,{id:"create-zrok-frontend",children:"Create zrok Frontend"}),"\n",(0,o.jsxs)(n.p,{children:["With our ",(0,o.jsx)(n.code,{children:"ZROK_ADMIN_TOKEN"})," and ",(0,o.jsx)(n.code,{children:"ZROK_API_ENDPOINT"})," environment variables set, we can create our public frontend like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok admin create frontend sqJRAINSiB public http://{token}.zrok.quigley.com:8080\n[ 0.037] INFO main.(*adminCreateFrontendCommand).run: created global public frontend 'WEirJNHVlcW9'\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The id of the frontend was emitted earlier in by the ",(0,o.jsx)(n.code,{children:"zrok"})," controller when we ran the bootstrap command. If you don't have that log message the you can find the id again with the ",(0,o.jsx)(n.code,{children:"ziti"})," CLI like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"# log in as admin (example)\nziti edge login localhost:1280 -u admin -p XO0xHp75uuyeireO2xmmVlK91T7B9fpD\n\n# list Ziti identities created by the quickstart and bootstrap\nziti edge list identities\n"})}),"\n",(0,o.jsx)(n.p,{children:'The id is shown for the frontend identity named "public."'}),"\n",(0,o.jsxs)(n.p,{children:["Nice work! The ",(0,o.jsx)(n.code,{children:"zrok"})," controller is fully configured now that you have created the ",(0,o.jsx)(n.code,{children:"zrok"})," frontend."]}),"\n",(0,o.jsx)(n.h2,{id:"configure-the-public-frontend",children:"Configure the Public Frontend"}),"\n",(0,o.jsxs)(n.p,{children:["Create an http frontend configuration file in ",(0,o.jsx)(n.code,{children:"etc/http-frontend.yml"}),"."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"v: 3\nhost_match: zrok.quigley.com\naddress: 0.0.0.0:8080\n"})}),"\n",(0,o.jsxs)(n.p,{children:["This frontend config file has a ",(0,o.jsx)(n.code,{children:"host_match"})," pattern that represents the DNS zone you're using with this instance of ",(0,o.jsx)(n.code,{children:"zrok"}),". Incoming HTTP requests with a matching ",(0,o.jsx)(n.code,{children:"Host"})," header will be handled by this frontend. You may also specify the interface address where the frontend will listen for public access requests."]}),"\n",(0,o.jsxs)(n.p,{children:["The frontend does not provide server TLS, but you may front the server with a reverse proxy. It is essential the reverse proxy forwards the ",(0,o.jsx)(n.code,{children:"Host"})," header supplied by the viewer. This example will expose the non-TLS listener for the frontend."]}),"\n",(0,o.jsxs)(n.p,{children:["You can also specify an ",(0,o.jsx)(n.code,{children:"oauth"})," configuration in this file, full details of are found in ",(0,o.jsx)(n.a,{href:"/docs/guides/self-hosting/oauth/configuring-oauth#configuring-your-public-frontend",children:"OAuth Public Frontend Configuration"}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"start-public-frontend",children:"Start Public Frontend"}),"\n",(0,o.jsx)(n.p,{children:"In another terminal window, run:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok access public etc/http-frontend.yml\n[ 0.002] INFO main.(*accessPublicCommand).run: {\n\t...\n}\n[ 0.002] INFO zrok/endpoints/public_frontend.newMetricsAgent: loaded 'public' identity\n"})}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"zrok"})," frontend uses the ",(0,o.jsx)(n.code,{children:"public"})," identity created during the bootstrap process to securely access zrok backends. to provide public access for the ",(0,o.jsx)(n.code,{children:"zrok"})," deployment. It is expected that the configured listener for this frontend corresponds to the DNS template specified when creating the public frontend record above."]}),"\n",(0,o.jsx)(n.h2,{id:"create-a-user-account",children:"Create a User Account"}),"\n",(0,o.jsxs)(n.p,{children:["With our ",(0,o.jsx)(n.code,{children:"ZROK_ADMIN_TOKEN"})," and ",(0,o.jsx)(n.code,{children:"ZROK_API_ENDPOINT"})," environment variables set, we can create our first user account."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"zrok admin create account etc/ctrl.yml \n"})}),"\n",(0,o.jsx)(n.p,{children:"The output is the account token you will use to enable each device's zrok environment."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-buttonless",metastring:'title="Example output"',children:"SuGzRPjVDIcF\n"})}),"\n",(0,o.jsx)(n.h2,{id:"invite-additional-users",children:"Invite Additional Users"}),"\n",(0,o.jsxs)(n.p,{children:["Offer this onboarding method to your users if you have configured an email-sending service in your ",(0,o.jsx)(n.code,{children:"zrok"})," controller configuration."]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"$ zrok invite\nNew Email: user@domain.com\nConfirm Email: user@domain.com\ninvitation sent to 'user@domain.com'!\n"})}),"\n",(0,o.jsxs)(n.p,{children:["If you look at the console output from your ",(0,o.jsx)(n.code,{children:"zrok"})," controller, you'll see a message like this:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:"[ 238.168] INFO zrok/controller.(*inviteHandler).Handle: account request for 'user@domain.com' has registration token 'U2Ewt1UCn3ql'\n"})}),"\n",(0,o.jsxs)(n.p,{children:["You can access your ",(0,o.jsx)(n.code,{children:"zrok"})," controller's registration UI by pointing a web browser at:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:"http://localhost:18080/register/U2Ewt1UCn3ql\n"})}),"\n",(0,o.jsx)(n.p,{children:"The UI will ask you to set a password for your new account. Go ahead and do that."}),"\n",(0,o.jsx)(n.p,{children:"After doing that, I see the following output in my controller console:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{children:"[ 516.778] INFO zrok/controller.(*registerHandler).Handle: created account 'user@domain.com' with token 'SuGzRPjVDIcF'\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Keep track of the token listed above (",(0,o.jsx)(n.code,{children:"SuGzRPjVDIcF"}),"). We'll use this to enable our shell for this ",(0,o.jsx)(n.code,{children:"zrok"})," deployment."]}),"\n",(0,o.jsx)(n.h2,{id:"enable-your-environment",children:"Enable Your Environment"}),"\n",(0,o.jsx)(n.p,{children:"On another device that can reach your Linux server by FQDN, configure the API endpoint and enable the environment with the account token you received when you created the first user account."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"export ZROK_API_ENDPOINT=https://zrok.quigley.com\n# or\nzrok config set apiEndpoint https://zrok.quigley.com\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"zrok enable SuGzRPjVDIcF\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-buttonless",metastring:'title="Example output"',children:"zrok environment '2AS1WZ3Sz' enabled for 'SuGzRPjVDIcF'\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:"zrok status --secrets\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-buttonless",metastring:'title="Example output"',children:"Config:\n\n CONFIG VALUE SOURCE\n apiEndpoint https://zrok.quigley.com env\n\nEnvironment:\n\n PROPERTY VALUE\n Secret Token SuGzRPjVDIcF\n Ziti Identity 2AS1WZ3Sz\n"})}),"\n",(0,o.jsxs)(n.p,{children:["Congratulations. You have a working ",(0,o.jsx)(n.code,{children:"zrok"})," environment!"]})]})}function h(e={}){const{wrapper:n}={...(0,t.a)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(a,{...e})}):a(e)}},1855:(e,n,r)=>{r.d(n,{Z:()=>o});const o=r.p+"assets/files/ctrl-06b900e22aab525ebec542dea6769d51.yml"},1151:(e,n,r)=>{r.d(n,{Z:()=>l,a:()=>s});var o=r(7294);const t={},i=o.createContext(t);function s(e){const n=o.useContext(i);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:s(e.components),o.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/ea84f538.298d3bea.js b/assets/js/ea84f538.aabd2b4e.js similarity index 91% rename from assets/js/ea84f538.298d3bea.js rename to assets/js/ea84f538.aabd2b4e.js index 04df35a0..f9e594f0 100644 --- a/assets/js/ea84f538.298d3bea.js +++ b/assets/js/ea84f538.aabd2b4e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9944],{7105:e=>{e.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Getting Started","href":"/docs/getting-started","docId":"getting-started","unlisted":false},{"type":"category","label":"Concepts","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Private Shares","href":"/docs/concepts/sharing-private","docId":"concepts/sharing-private","unlisted":false},{"type":"link","label":"Public Shares","href":"/docs/concepts/sharing-public","docId":"concepts/sharing-public","unlisted":false},{"type":"link","label":"Reserved Shares","href":"/docs/concepts/sharing-reserved","docId":"concepts/sharing-reserved","unlisted":false},{"type":"link","label":"Sharing HTTP Servers","href":"/docs/concepts/http","docId":"concepts/http","unlisted":false},{"type":"link","label":"Sharing TCP and UDP Servers","href":"/docs/concepts/tunnels","docId":"concepts/tunnels","unlisted":false},{"type":"link","label":"Sharing Websites and Files","href":"/docs/concepts/files","docId":"concepts/files","unlisted":false},{"type":"link","label":"Open Source","href":"/docs/concepts/opensource","docId":"concepts/opensource","unlisted":false},{"type":"link","label":"Hosting","href":"/docs/concepts/hosting","docId":"concepts/hosting","unlisted":false}],"href":"/docs/concepts/"},{"type":"category","label":"Guides","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Install","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Linux","href":"/docs/guides/install/linux","docId":"guides/install/linux","unlisted":false},{"type":"link","label":"macOS","href":"/docs/guides/install/macos","docId":"guides/install/macos","unlisted":false},{"type":"link","label":"Windows","href":"/docs/guides/install/windows","docId":"guides/install/windows","unlisted":false}],"href":"/docs/guides/install/"},{"type":"link","label":"frontdoor","href":"/docs/guides/frontdoor","docId":"guides/frontdoor","unlisted":false},{"type":"link","label":"Permission Modes","href":"/docs/guides/permission-modes","docId":"guides/permission-modes","unlisted":false},{"type":"category","label":"Docker Share","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Public Share","href":"/docs/guides/docker-share/docker_public_share_guide","docId":"guides/docker-share/docker_public_share_guide","unlisted":false},{"type":"link","label":"Private Share","href":"/docs/guides/docker-share/docker_private_share_guide","docId":"guides/docker-share/docker_private_share_guide","unlisted":false}],"href":"/docs/guides/docker-share/"},{"type":"category","label":"Self Hosting","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Linux","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"NGINX TLS","href":"/docs/guides/self-hosting/linux/nginx","docId":"guides/self-hosting/linux/nginx","unlisted":false}],"href":"/docs/guides/self-hosting/linux/"},{"type":"link","label":"Docker","href":"/docs/guides/self-hosting/docker","docId":"guides/self-hosting/docker","unlisted":false},{"type":"category","label":"Metrics and Limits","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Configuring Metrics","href":"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics","docId":"guides/self-hosting/metrics-and-limits/configuring-metrics","unlisted":false},{"type":"link","label":"Configuring Limits","href":"/docs/guides/self-hosting/metrics-and-limits/configuring-limits","docId":"guides/self-hosting/metrics-and-limits/configuring-limits","unlisted":false}],"href":"/docs/category/metrics-and-limits"},{"type":"category","label":"OAuth","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"OAuth Public Frontend Configuration","href":"/docs/guides/self-hosting/oauth/configuring-oauth","docId":"guides/self-hosting/oauth/configuring-oauth","unlisted":false}],"href":"/docs/category/oauth"},{"type":"link","label":"Instance Config","href":"/docs/guides/self-hosting/instance-configuration","docId":"guides/self-hosting/instance-configuration","unlisted":false}],"href":"/docs/category/self-hosting"},{"type":"category","label":"drives","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"The Drives CLI","href":"/docs/guides/drives/cli","docId":"guides/drives/cli","unlisted":false}]},{"type":"link","label":"VPN","href":"/docs/guides/vpn/","docId":"guides/vpn/vpn","unlisted":false}],"href":"/docs/category/guides"}]},"docs":{"concepts/files":{"id":"concepts/files","title":"Sharing Websites and Files","description":"With zrok it is possible to share files quickly and easily as well. To share files using zrok use","sidebar":"tutorialSidebar"},"concepts/hosting":{"id":"concepts/hosting","title":"Hosting","description":"Self-Hosted","sidebar":"tutorialSidebar"},"concepts/http":{"id":"concepts/http","title":"Sharing HTTP Servers","description":"zrok can share HTTP and HTTPS resources natively. If you have an existing web server that you want to share with other users, you can use the zrok share command using the --backend-mode proxy flag.","sidebar":"tutorialSidebar"},"concepts/index":{"id":"concepts/index","title":"Concepts","description":"zrok was designed to make sharing local resources both secure and easy. In this section of the zrok documentation, we\'ll tour through all of the most important features.","sidebar":"tutorialSidebar"},"concepts/opensource":{"id":"concepts/opensource","title":"Open Source","description":"It\'s important to the zrok project that it remain free and open source software. The code is available on GitHub","sidebar":"tutorialSidebar"},"concepts/sharing-private":{"id":"concepts/sharing-private","title":"Private Shares","description":"zrok was built to share and access digital resources. A private share allows a resource to be","sidebar":"tutorialSidebar"},"concepts/sharing-public":{"id":"concepts/sharing-public","title":"Public Shares","description":"zrok supports public sharing for web-based (HTTP and HTTPS) resources. These resources are easily shared with the general internet through public access points.","sidebar":"tutorialSidebar"},"concepts/sharing-reserved":{"id":"concepts/sharing-reserved","title":"Reserved Shares","description":"By default a public or private share is assigned a share token when you create a share using the zrok share command. The zrok share command is the bridge between your local environment and the users you are sharing with. When you terminate the zrok share, the bridge is eliminated and the share token is deleted. If you run zrok share again, you will be allocated a brand new share token.","sidebar":"tutorialSidebar"},"concepts/tunnels":{"id":"concepts/tunnels","title":"Sharing TCP and UDP Servers","description":"zrok includes support for sharing low-level TCP and UDP network resources using the tcpTunnel and udpTunnel backend modes.","sidebar":"tutorialSidebar"},"getting-started":{"id":"getting-started","title":"Getting Started with zrok","description":"What\'s a zrok?","sidebar":"tutorialSidebar"},"guides/docker-share/docker_private_share_guide":{"id":"guides/docker-share/docker_private_share_guide","title":"Docker Private Share","description":"Goal","sidebar":"tutorialSidebar"},"guides/docker-share/docker_public_share_guide":{"id":"guides/docker-share/docker_public_share_guide","title":"Docker Compose Public Share","description":"Goal","sidebar":"tutorialSidebar"},"guides/docker-share/index":{"id":"guides/docker-share/index","title":"Getting Started with Docker","description":"Overview","sidebar":"tutorialSidebar"},"guides/drives/cli":{"id":"guides/drives/cli","title":"The Drives CLI","description":"The zrok drives CLI tools allow for simple, ergonomic management and synchronization of local and remote files.","sidebar":"tutorialSidebar"},"guides/frontdoor":{"id":"guides/frontdoor","title":"zrok frontdoor","description":"zrok frontdoor is the heavy-duty front door to your app or site. It makes your website or app available to your online audience through the shield of zrok.io\'s hardened, managed frontends.","sidebar":"tutorialSidebar"},"guides/install/index":{"id":"guides/install/index","title":"Install","description":"{e.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Getting Started","href":"/docs/getting-started","docId":"getting-started","unlisted":false},{"type":"category","label":"Concepts","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Private Shares","href":"/docs/concepts/sharing-private","docId":"concepts/sharing-private","unlisted":false},{"type":"link","label":"Public Shares","href":"/docs/concepts/sharing-public","docId":"concepts/sharing-public","unlisted":false},{"type":"link","label":"Reserved Shares","href":"/docs/concepts/sharing-reserved","docId":"concepts/sharing-reserved","unlisted":false},{"type":"link","label":"Sharing HTTP Servers","href":"/docs/concepts/http","docId":"concepts/http","unlisted":false},{"type":"link","label":"Sharing TCP and UDP Servers","href":"/docs/concepts/tunnels","docId":"concepts/tunnels","unlisted":false},{"type":"link","label":"Sharing Websites and Files","href":"/docs/concepts/files","docId":"concepts/files","unlisted":false},{"type":"link","label":"Open Source","href":"/docs/concepts/opensource","docId":"concepts/opensource","unlisted":false},{"type":"link","label":"Hosting","href":"/docs/concepts/hosting","docId":"concepts/hosting","unlisted":false}],"href":"/docs/concepts/"},{"type":"category","label":"Guides","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Install","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Linux","href":"/docs/guides/install/linux","docId":"guides/install/linux","unlisted":false},{"type":"link","label":"macOS","href":"/docs/guides/install/macos","docId":"guides/install/macos","unlisted":false},{"type":"link","label":"Windows","href":"/docs/guides/install/windows","docId":"guides/install/windows","unlisted":false}],"href":"/docs/guides/install/"},{"type":"link","label":"frontdoor","href":"/docs/guides/frontdoor","docId":"guides/frontdoor","unlisted":false},{"type":"link","label":"Permission Modes","href":"/docs/guides/permission-modes","docId":"guides/permission-modes","unlisted":false},{"type":"category","label":"Docker Share","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Public Share","href":"/docs/guides/docker-share/docker_public_share_guide","docId":"guides/docker-share/docker_public_share_guide","unlisted":false},{"type":"link","label":"Private Share","href":"/docs/guides/docker-share/docker_private_share_guide","docId":"guides/docker-share/docker_private_share_guide","unlisted":false}],"href":"/docs/guides/docker-share/"},{"type":"category","label":"Self Hosting","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"Linux","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"NGINX TLS","href":"/docs/guides/self-hosting/linux/nginx","docId":"guides/self-hosting/linux/nginx","unlisted":false}],"href":"/docs/guides/self-hosting/linux/"},{"type":"link","label":"Docker","href":"/docs/guides/self-hosting/docker","docId":"guides/self-hosting/docker","unlisted":false},{"type":"category","label":"Metrics and Limits","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Configuring Metrics","href":"/docs/guides/self-hosting/metrics-and-limits/configuring-metrics","docId":"guides/self-hosting/metrics-and-limits/configuring-metrics","unlisted":false},{"type":"link","label":"Configuring Limits","href":"/docs/guides/self-hosting/metrics-and-limits/configuring-limits","docId":"guides/self-hosting/metrics-and-limits/configuring-limits","unlisted":false}],"href":"/docs/category/metrics-and-limits"},{"type":"category","label":"OAuth","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"OAuth Public Frontend Configuration","href":"/docs/guides/self-hosting/oauth/configuring-oauth","docId":"guides/self-hosting/oauth/configuring-oauth","unlisted":false}],"href":"/docs/category/oauth"},{"type":"link","label":"Instance Config","href":"/docs/guides/self-hosting/instance-configuration","docId":"guides/self-hosting/instance-configuration","unlisted":false}],"href":"/docs/category/self-hosting"},{"type":"category","label":"drives","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"The Drives CLI","href":"/docs/guides/drives/cli","docId":"guides/drives/cli","unlisted":false}]},{"type":"link","label":"VPN","href":"/docs/guides/vpn/","docId":"guides/vpn/vpn","unlisted":false}],"href":"/docs/category/guides"}]},"docs":{"concepts/files":{"id":"concepts/files","title":"Sharing Websites and Files","description":"With zrok it is possible to share files quickly and easily as well. To share files using zrok use","sidebar":"tutorialSidebar"},"concepts/hosting":{"id":"concepts/hosting","title":"Hosting","description":"Self-Hosted","sidebar":"tutorialSidebar"},"concepts/http":{"id":"concepts/http","title":"Sharing HTTP Servers","description":"zrok can share HTTP and HTTPS resources natively. If you have an existing web server that you want to share with other users, you can use the zrok share command using the --backend-mode proxy flag.","sidebar":"tutorialSidebar"},"concepts/index":{"id":"concepts/index","title":"Concepts","description":"zrok was designed to make sharing local resources both secure and easy. In this section of the zrok documentation, we\'ll tour through all of the most important features.","sidebar":"tutorialSidebar"},"concepts/opensource":{"id":"concepts/opensource","title":"Open Source","description":"It\'s important to the zrok project that it remain free and open source software. The code is available on GitHub","sidebar":"tutorialSidebar"},"concepts/sharing-private":{"id":"concepts/sharing-private","title":"Private Shares","description":"zrok was built to share and access digital resources. A private share allows a resource to be","sidebar":"tutorialSidebar"},"concepts/sharing-public":{"id":"concepts/sharing-public","title":"Public Shares","description":"zrok supports public sharing for web-based (HTTP and HTTPS) resources. These resources are easily shared with the general internet through public access points.","sidebar":"tutorialSidebar"},"concepts/sharing-reserved":{"id":"concepts/sharing-reserved","title":"Reserved Shares","description":"By default a public or private share is assigned a share token when you create a share using the zrok share command. The zrok share command is the bridge between your local environment and the users you are sharing with. When you terminate the zrok share, the bridge is eliminated and the share token is deleted. If you run zrok share again, you will be allocated a brand new share token.","sidebar":"tutorialSidebar"},"concepts/tunnels":{"id":"concepts/tunnels","title":"Sharing TCP and UDP Servers","description":"zrok includes support for sharing low-level TCP and UDP network resources using the tcpTunnel and udpTunnel backend modes.","sidebar":"tutorialSidebar"},"getting-started":{"id":"getting-started","title":"Getting Started with zrok","description":"What\'s a zrok?","sidebar":"tutorialSidebar"},"guides/docker-share/docker_private_share_guide":{"id":"guides/docker-share/docker_private_share_guide","title":"Docker Private Share","description":"Goal","sidebar":"tutorialSidebar"},"guides/docker-share/docker_public_share_guide":{"id":"guides/docker-share/docker_public_share_guide","title":"Docker Compose Public Share","description":"Goal","sidebar":"tutorialSidebar"},"guides/docker-share/index":{"id":"guides/docker-share/index","title":"Getting Started with Docker","description":"Overview","sidebar":"tutorialSidebar"},"guides/drives/cli":{"id":"guides/drives/cli","title":"The Drives CLI","description":"The zrok drives CLI tools allow for simple, ergonomic management and synchronization of local and remote files.","sidebar":"tutorialSidebar"},"guides/frontdoor":{"id":"guides/frontdoor","title":"zrok frontdoor","description":"zrok frontdoor is the heavy-duty front door to your app or site. It makes your website or app available to your online audience through the shield of zrok.io\'s hardened, managed frontends.","sidebar":"tutorialSidebar"},"guides/install/index":{"id":"guides/install/index","title":"Install","description":"{"use strict";var e,a,t,r,d,b={},c={};function f(e){var a=c[e];if(void 0!==a)return a.exports;var t=c[e]={id:e,loaded:!1,exports:{}};return b[e].call(t.exports,t,t.exports,f),t.loaded=!0,t.exports}f.m=b,f.c=c,f.amdO={},e=[],f.O=(a,t,r,d)=>{if(!t){var b=1/0;for(i=0;i=d)&&Object.keys(f.O).every((e=>f.O[e](t[o])))?t.splice(o--,1):(c=!1,d0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[t,r,d]},f.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return f.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,f.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var d=Object.create(null);f.r(d);var b={};a=a||[null,t({}),t([]),t(t)];for(var c=2&r&&e;"object"==typeof c&&!~a.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach((a=>b[a]=()=>e[a]));return b.default=()=>e,f.d(d,b),d},f.d=(e,a)=>{for(var t in a)f.o(a,t)&&!f.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},f.f={},f.e=e=>Promise.all(Object.keys(f.f).reduce(((a,t)=>(f.f[t](e,a),a)),[])),f.u=e=>"assets/js/"+({58:"0c66edb9",826:"47881d5c",960:"b186d866",1004:"c141421f",1360:"34e1d3b9",1387:"4555b262",1889:"339d500a",2108:"288b1075",2732:"c015c796",2914:"618abc13",2992:"f2348458",3182:"6e881e32",3629:"aba21aa0",4031:"ef8afbfd",4195:"c4f5d8e4",4196:"bbbe662c",4368:"a94703ab",4778:"e1dfe4fe",4838:"75b20590",4900:"600b2345",5327:"c304be44",5882:"d768dc0f",5889:"cda0d2e5",5893:"2da89d45",5980:"a7456010",6062:"88b6e020",6913:"b6569025",7076:"2e812224",7142:"1ba5bc99",7176:"6272ba0e",7918:"17896441",7920:"1a4e3797",8156:"21880a4d",8198:"50ef9c44",8518:"a7bd4aaa",8905:"07d0b302",8938:"f888b719",8945:"bc747cac",8993:"5cd0a723",9661:"5e95c892",9714:"d9f29b48",9817:"14eb3368",9944:"ea84f538"}[e]||e)+"."+{58:"025d65ff",174:"691dcdd4",826:"ae1dcb33",960:"64871ffa",1004:"3e38baa4",1272:"43cc57fb",1360:"01c3d2bc",1387:"9af6f458",1426:"726339ca",1772:"2df7b54f",1889:"039a1491",2108:"a9435f23",2312:"2f123ece",2732:"c04c1507",2914:"60953b95",2992:"19be6454",3182:"e03d93bb",3629:"b0420849",4031:"addfcac8",4195:"ef51316b",4196:"b1bdc9a0",4368:"21ee911e",4778:"ecb8805e",4838:"3455a494",4900:"9d8edfd8",5327:"4a777c27",5882:"9162b0b5",5889:"236e5d28",5893:"49a97a4b",5980:"37bc4934",6062:"fa69d636",6913:"e0ac939e",6945:"8e8e2060",7076:"5284992e",7142:"37030d6d",7176:"35172d6c",7918:"48128ac6",7920:"56015c27",8156:"ffe0573f",8198:"5df32fc9",8518:"45274bc2",8894:"46125374",8905:"2f581655",8938:"b3481d16",8945:"975267a6",8993:"e3585e3a",9661:"d1615a49",9714:"6d24467b",9817:"038d23ea",9944:"298d3bea"}[e]+".js",f.miniCssF=e=>{},f.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),f.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r={},d="website:",f.l=(e,a,t,b)=>{if(r[e])r[e].push(a);else{var c,o;if(void 0!==t)for(var n=document.getElementsByTagName("script"),i=0;i{c.onerror=c.onload=null,clearTimeout(s);var d=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),d&&d.forEach((e=>e(t))),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=l.bind(null,c.onerror),c.onload=l.bind(null,c.onload),o&&document.head.appendChild(c)}},f.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.p="/",f.gca=function(e){return e={17896441:"7918","0c66edb9":"58","47881d5c":"826",b186d866:"960",c141421f:"1004","34e1d3b9":"1360","4555b262":"1387","339d500a":"1889","288b1075":"2108",c015c796:"2732","618abc13":"2914",f2348458:"2992","6e881e32":"3182",aba21aa0:"3629",ef8afbfd:"4031",c4f5d8e4:"4195",bbbe662c:"4196",a94703ab:"4368",e1dfe4fe:"4778","75b20590":"4838","600b2345":"4900",c304be44:"5327",d768dc0f:"5882",cda0d2e5:"5889","2da89d45":"5893",a7456010:"5980","88b6e020":"6062",b6569025:"6913","2e812224":"7076","1ba5bc99":"7142","6272ba0e":"7176","1a4e3797":"7920","21880a4d":"8156","50ef9c44":"8198",a7bd4aaa:"8518","07d0b302":"8905",f888b719:"8938",bc747cac:"8945","5cd0a723":"8993","5e95c892":"9661",d9f29b48:"9714","14eb3368":"9817",ea84f538:"9944"}[e]||e,f.p+f.u(e)},(()=>{var e={1303:0,532:0};f.f.j=(a,t)=>{var r=f.o(e,a)?e[a]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var d=new Promise(((t,d)=>r=e[a]=[t,d]));t.push(r[2]=d);var b=f.p+f.u(a),c=new Error;f.l(b,(t=>{if(f.o(e,a)&&(0!==(r=e[a])&&(e[a]=void 0),r)){var d=t&&("load"===t.type?"missing":t.type),b=t&&t.target&&t.target.src;c.message="Loading chunk "+a+" failed.\n("+d+": "+b+")",c.name="ChunkLoadError",c.type=d,c.request=b,r[1](c)}}),"chunk-"+a,a)}},f.O.j=a=>0===e[a];var a=(a,t)=>{var r,d,b=t[0],c=t[1],o=t[2],n=0;if(b.some((a=>0!==e[a]))){for(r in c)f.o(c,r)&&(f.m[r]=c[r]);if(o)var i=o(f)}for(a&&a(t);n{"use strict";var e,a,t,r,d,b={},f={};function c(e){var a=f[e];if(void 0!==a)return a.exports;var t=f[e]={id:e,loaded:!1,exports:{}};return b[e].call(t.exports,t,t.exports,c),t.loaded=!0,t.exports}c.m=b,c.c=f,c.amdO={},e=[],c.O=(a,t,r,d)=>{if(!t){var b=1/0;for(i=0;i=d)&&Object.keys(c.O).every((e=>c.O[e](t[o])))?t.splice(o--,1):(f=!1,d0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[t,r,d]},c.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return c.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,c.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var d=Object.create(null);c.r(d);var b={};a=a||[null,t({}),t([]),t(t)];for(var f=2&r&&e;"object"==typeof f&&!~a.indexOf(f);f=t(f))Object.getOwnPropertyNames(f).forEach((a=>b[a]=()=>e[a]));return b.default=()=>e,c.d(d,b),d},c.d=(e,a)=>{for(var t in a)c.o(a,t)&&!c.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},c.f={},c.e=e=>Promise.all(Object.keys(c.f).reduce(((a,t)=>(c.f[t](e,a),a)),[])),c.u=e=>"assets/js/"+({58:"0c66edb9",826:"47881d5c",960:"b186d866",1004:"c141421f",1360:"34e1d3b9",1387:"4555b262",1889:"339d500a",2108:"288b1075",2732:"c015c796",2914:"618abc13",2992:"f2348458",3182:"6e881e32",3629:"aba21aa0",4031:"ef8afbfd",4195:"c4f5d8e4",4196:"bbbe662c",4368:"a94703ab",4778:"e1dfe4fe",4838:"75b20590",4900:"600b2345",5327:"c304be44",5882:"d768dc0f",5889:"cda0d2e5",5893:"2da89d45",5980:"a7456010",6062:"88b6e020",6913:"b6569025",7076:"2e812224",7142:"1ba5bc99",7176:"6272ba0e",7918:"17896441",7920:"1a4e3797",8156:"21880a4d",8198:"50ef9c44",8518:"a7bd4aaa",8905:"07d0b302",8938:"f888b719",8945:"bc747cac",8993:"5cd0a723",9661:"5e95c892",9714:"d9f29b48",9817:"14eb3368",9944:"ea84f538"}[e]||e)+"."+{58:"025d65ff",174:"691dcdd4",826:"ae1dcb33",960:"64871ffa",1004:"3e38baa4",1272:"43cc57fb",1360:"01c3d2bc",1387:"9af6f458",1426:"726339ca",1772:"2df7b54f",1889:"039a1491",2108:"a9435f23",2312:"2f123ece",2732:"c04c1507",2914:"60953b95",2992:"19be6454",3182:"e03d93bb",3629:"b0420849",4031:"addfcac8",4195:"ef51316b",4196:"b1bdc9a0",4368:"21ee911e",4778:"ecb8805e",4838:"3455a494",4900:"b5dbe8be",5327:"4a777c27",5882:"d9dde1b6",5889:"236e5d28",5893:"49a97a4b",5980:"37bc4934",6062:"fa69d636",6913:"e0ac939e",6945:"8e8e2060",7076:"5284992e",7142:"37030d6d",7176:"35172d6c",7918:"48128ac6",7920:"56015c27",8156:"ffe0573f",8198:"5df32fc9",8518:"45274bc2",8894:"46125374",8905:"2f581655",8938:"b3481d16",8945:"975267a6",8993:"e3585e3a",9661:"d1615a49",9714:"6d24467b",9817:"038d23ea",9944:"aabd2b4e"}[e]+".js",c.miniCssF=e=>{},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),c.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r={},d="website:",c.l=(e,a,t,b)=>{if(r[e])r[e].push(a);else{var f,o;if(void 0!==t)for(var n=document.getElementsByTagName("script"),i=0;i{f.onerror=f.onload=null,clearTimeout(s);var d=r[e];if(delete r[e],f.parentNode&&f.parentNode.removeChild(f),d&&d.forEach((e=>e(t))),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:f}),12e4);f.onerror=l.bind(null,f.onerror),f.onload=l.bind(null,f.onload),o&&document.head.appendChild(f)}},c.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.p="/",c.gca=function(e){return e={17896441:"7918","0c66edb9":"58","47881d5c":"826",b186d866:"960",c141421f:"1004","34e1d3b9":"1360","4555b262":"1387","339d500a":"1889","288b1075":"2108",c015c796:"2732","618abc13":"2914",f2348458:"2992","6e881e32":"3182",aba21aa0:"3629",ef8afbfd:"4031",c4f5d8e4:"4195",bbbe662c:"4196",a94703ab:"4368",e1dfe4fe:"4778","75b20590":"4838","600b2345":"4900",c304be44:"5327",d768dc0f:"5882",cda0d2e5:"5889","2da89d45":"5893",a7456010:"5980","88b6e020":"6062",b6569025:"6913","2e812224":"7076","1ba5bc99":"7142","6272ba0e":"7176","1a4e3797":"7920","21880a4d":"8156","50ef9c44":"8198",a7bd4aaa:"8518","07d0b302":"8905",f888b719:"8938",bc747cac:"8945","5cd0a723":"8993","5e95c892":"9661",d9f29b48:"9714","14eb3368":"9817",ea84f538:"9944"}[e]||e,c.p+c.u(e)},(()=>{var e={1303:0,532:0};c.f.j=(a,t)=>{var r=c.o(e,a)?e[a]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var d=new Promise(((t,d)=>r=e[a]=[t,d]));t.push(r[2]=d);var b=c.p+c.u(a),f=new Error;c.l(b,(t=>{if(c.o(e,a)&&(0!==(r=e[a])&&(e[a]=void 0),r)){var d=t&&("load"===t.type?"missing":t.type),b=t&&t.target&&t.target.src;f.message="Loading chunk "+a+" failed.\n("+d+": "+b+")",f.name="ChunkLoadError",f.type=d,f.request=b,r[1](f)}}),"chunk-"+a,a)}},c.O.j=a=>0===e[a];var a=(a,t)=>{var r,d,b=t[0],f=t[1],o=t[2],n=0;if(b.some((a=>0!==e[a]))){for(r in f)c.o(f,r)&&(c.m[r]=f[r]);if(o)var i=o(c)}for(a&&a(t);n