1217 lines
43 KiB
HTML
1217 lines
43 KiB
HTML
|
|
<!doctype html>
|
|
<html lang="en" class="no-js">
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
|
|
<meta name="description" content="Make your web services secure by default.">
|
|
|
|
|
|
|
|
<link rel="canonical" href="https://docs.bunkerweb.io/1.4/plugins/">
|
|
|
|
<link rel="icon" href="../assets/favicon.png">
|
|
<meta name="generator" content="mkdocs-1.2.3, mkdocs-material-8.2.5">
|
|
|
|
|
|
|
|
<title>Plugins - BunkerWeb</title>
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="../assets/stylesheets/main.2d9f7617.min.css">
|
|
|
|
|
|
<link rel="stylesheet" href="../assets/stylesheets/palette.e6a45f82.min.css">
|
|
|
|
|
|
|
|
|
|
<script
|
|
async
|
|
defer
|
|
data-domain="docs.bunkerweb.io"
|
|
src="https://data.bunkerity.com/js/script.js"
|
|
></script>
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="../assets/extra.css">
|
|
|
|
<script>__md_scope=new URL("..",location),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
|
|
|
|
|
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<body dir="ltr" data-md-color-scheme="" data-md-color-primary="none" data-md-color-accent="none">
|
|
|
|
|
|
|
|
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
|
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
|
<label class="md-overlay" for="__drawer"></label>
|
|
<div data-md-component="skip">
|
|
|
|
|
|
<a href="#plugins" class="md-skip">
|
|
Skip to content
|
|
</a>
|
|
|
|
</div>
|
|
<div data-md-component="announce">
|
|
|
|
<aside class="md-banner">
|
|
<div class="md-banner__inner md-grid md-typeset">
|
|
📢 Looking for tailored support, consulting or
|
|
development for BunkerWeb ? Contact us at
|
|
<a
|
|
href="mailto:contact@bunkerity.com"
|
|
style="color: #3f6ec6; text-decoration: underline"
|
|
>contact@bunkerity.com</a
|
|
>
|
|
for enterprise offers !
|
|
</div>
|
|
</aside>
|
|
|
|
</div>
|
|
|
|
<div data-md-component="outdated" hidden>
|
|
<aside class="md-banner md-banner--warning">
|
|
|
|
<div class="md-banner__inner md-grid md-typeset">
|
|
You're not viewing the
|
|
documentation for the current version.
|
|
<a href="../.."><strong>Click here to change.</strong></a>
|
|
</div>
|
|
<script>var el=document.querySelector("[data-md-component=outdated]"),outdated=__md_get("__outdated",sessionStorage);!0===outdated&&el&&(el.hidden=!1)</script>
|
|
|
|
</aside>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<header class="md-header md-header--lifted" data-md-component="header">
|
|
<nav class="md-header__inner md-grid" aria-label="Header">
|
|
<a href=".." title="BunkerWeb" class="md-header__button md-logo" aria-label="BunkerWeb" data-md-component="logo">
|
|
|
|
<img src="../assets/logo.png" alt="logo">
|
|
|
|
</a>
|
|
<label class="md-header__button md-icon" for="__drawer">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2z"/></svg>
|
|
</label>
|
|
<div class="md-header__title" data-md-component="header-title">
|
|
<div class="md-header__ellipsis">
|
|
<div class="md-header__topic">
|
|
<span class="md-ellipsis">
|
|
BunkerWeb
|
|
</span>
|
|
</div>
|
|
<div class="md-header__topic" data-md-component="header-topic">
|
|
<span class="md-ellipsis">
|
|
|
|
Plugins
|
|
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<label class="md-header__button md-icon" for="__search">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5z"/></svg>
|
|
</label>
|
|
<div class="md-search" data-md-component="search" role="dialog">
|
|
<label class="md-search__overlay" for="__search"></label>
|
|
<div class="md-search__inner" role="search">
|
|
<form class="md-search__form" name="search">
|
|
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
|
<label class="md-search__icon md-icon" for="__search">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5z"/></svg>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12z"/></svg>
|
|
</label>
|
|
<nav class="md-search__options" aria-label="Search">
|
|
|
|
<button type="reset" class="md-search__icon md-icon" aria-label="Clear" tabindex="-1">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>
|
|
</button>
|
|
</nav>
|
|
|
|
</form>
|
|
<div class="md-search__output">
|
|
<div class="md-search__scrollwrap" data-md-scrollfix>
|
|
<div class="md-search-result" data-md-component="search-result">
|
|
<div class="md-search-result__meta">
|
|
Initializing search
|
|
</div>
|
|
<ol class="md-search-result__list"></ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="md-header__source">
|
|
<a href="https://github.com/bunkerity/bunkerweb/" title="Go to repository" class="md-source" data-md-component="source">
|
|
<div class="md-source__icon md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
|
</div>
|
|
<div class="md-source__repository">
|
|
GitHub
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
|
<div class="md-tabs__inner md-grid">
|
|
<ul class="md-tabs__list">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href=".." class="md-tabs__link">
|
|
Introduction
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../migrating/" class="md-tabs__link">
|
|
Migrating from bunkerized
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../concepts/" class="md-tabs__link">
|
|
Concepts
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../integrations/" class="md-tabs__link">
|
|
Integrations
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../quickstart-guide/" class="md-tabs__link">
|
|
Quickstart guide
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../security-tuning/" class="md-tabs__link">
|
|
Security tuning
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../settings/" class="md-tabs__link">
|
|
Settings
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../web-ui/" class="md-tabs__link">
|
|
Web UI
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../troubleshooting/" class="md-tabs__link">
|
|
Troubleshooting
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="./" class="md-tabs__link md-tabs__link--active">
|
|
Plugins
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../about/" class="md-tabs__link">
|
|
About
|
|
</a>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
|
|
</header>
|
|
|
|
<div class="md-container" data-md-component="container">
|
|
|
|
|
|
|
|
|
|
<main class="md-main" data-md-component="main">
|
|
<div class="md-main__inner md-grid">
|
|
|
|
|
|
|
|
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
|
<div class="md-sidebar__scrollwrap">
|
|
<div class="md-sidebar__inner">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label="Navigation" data-md-level="0">
|
|
<label class="md-nav__title" for="__drawer">
|
|
<a href=".." title="BunkerWeb" class="md-nav__button md-logo" aria-label="BunkerWeb" data-md-component="logo">
|
|
|
|
<img src="../assets/logo.png" alt="logo">
|
|
|
|
</a>
|
|
BunkerWeb
|
|
</label>
|
|
|
|
<div class="md-nav__source">
|
|
<a href="https://github.com/bunkerity/bunkerweb/" title="Go to repository" class="md-source" data-md-component="source">
|
|
<div class="md-source__icon md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
|
|
</div>
|
|
<div class="md-source__repository">
|
|
GitHub
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href=".." class="md-nav__link">
|
|
Introduction
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../migrating/" class="md-nav__link">
|
|
Migrating from bunkerized
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../concepts/" class="md-nav__link">
|
|
Concepts
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../integrations/" class="md-nav__link">
|
|
Integrations
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../quickstart-guide/" class="md-nav__link">
|
|
Quickstart guide
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../security-tuning/" class="md-nav__link">
|
|
Security tuning
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../settings/" class="md-nav__link">
|
|
Settings
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../web-ui/" class="md-nav__link">
|
|
Web UI
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../troubleshooting/" class="md-nav__link">
|
|
Troubleshooting
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--active">
|
|
|
|
<input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc">
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
|
Plugins
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<a href="./" class="md-nav__link md-nav__link--active">
|
|
Plugins
|
|
</a>
|
|
|
|
|
|
|
|
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__title" for="__toc">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
Table of contents
|
|
</label>
|
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#official-plugins" class="md-nav__link">
|
|
Official plugins
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#how-to-use-a-plugin" class="md-nav__link">
|
|
How to use a plugin
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#writing-a-plugin" class="md-nav__link">
|
|
Writing a plugin
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Writing a plugin">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#metadata" class="md-nav__link">
|
|
Metadata
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#configurations" class="md-nav__link">
|
|
Configurations
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#lua" class="md-nav__link">
|
|
LUA
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="LUA">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#main-script" class="md-nav__link">
|
|
Main script
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#libraries" class="md-nav__link">
|
|
Libraries
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#helpers" class="md-nav__link">
|
|
Helpers
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#jobs" class="md-nav__link">
|
|
Jobs
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../about/" class="md-nav__link">
|
|
About
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="md-content" data-md-component="content">
|
|
<article class="md-content__inner md-typeset">
|
|
|
|
|
|
<a href="https://github.com/bunkerity/bunkerweb/edit/master/docs/plugins.md" title="Edit this page" class="md-content__button md-icon">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z"/></svg>
|
|
</a>
|
|
|
|
|
|
|
|
<h1 id="plugins">Plugins</h1>
|
|
<p>BunkerWeb comes with a plugin system to make it possible to easily add new features. Once a plugin is installed, you can manage it using additional settings defined by the plugin.</p>
|
|
<h2 id="official-plugins">Official plugins</h2>
|
|
<p>Here is the list of "official" plugins that we maintain (see the <a href="https://github.com/bunkerity/bunkerweb-plugins">bunkerweb-plugins</a> repository for more information) :</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th align="center">Name</th>
|
|
<th align="center">Version</th>
|
|
<th align="left">Description</th>
|
|
<th align="center">Link</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="center"><strong>ClamAV</strong></td>
|
|
<td align="center">0.1</td>
|
|
<td align="left">Automatically scans uploaded files with the ClamAV antivirus engine and denies the request when a file is detected as malicious.</td>
|
|
<td align="center"><a href="https://github.com/bunkerity/bunkerweb-plugins/tree/main/clamav">bunkerweb-plugins/clamav</a></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><strong>CrowdSec</strong></td>
|
|
<td align="center">0.1</td>
|
|
<td align="left">CrowdSec bouncer for BunkerWeb.</td>
|
|
<td align="center"><a href="https://github.com/bunkerity/bunkerweb-plugins/tree/main/crowdsec">bunkerweb-plugins/crowdsec</a></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><strong>VirusTotal</strong></td>
|
|
<td align="center">0.1</td>
|
|
<td align="left">Automatically scans uploaded files with the VirusTotal API and denies the request when a file is detected as malicious.</td>
|
|
<td align="center"><a href="https://github.com/bunkerity/bunkerweb-plugins/tree/main/virustotal">bunkerweb-plugins/virustotal</a></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h2 id="how-to-use-a-plugin">How to use a plugin</h2>
|
|
<p>The first step is to install the plugin by putting the plugin files inside the corresponding <code>plugins</code> data folder, the procedure depends on your integration :</p>
|
|
<div class="tabbed-set tabbed-alternate" data-tabs="1:5"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><input id="__tabbed_1_4" name="__tabbed_1" type="radio" /><input id="__tabbed_1_5" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Docker</label><label for="__tabbed_1_2">Docker autoconf</label><label for="__tabbed_1_3">Swarm</label><label for="__tabbed_1_4">Kubernetes</label><label for="__tabbed_1_5">Linux</label></div>
|
|
<div class="tabbed-content">
|
|
<div class="tabbed-block">
|
|
<p>When using the <a href="/1.4/integrations/#docker">Docker integration</a>, plugins must be written to the volume mounted on <code>/data</code>.</p>
|
|
<p>The first thing to do is to create the plugins folder :
|
|
<code>shell
|
|
mkdir -p ./bw-data/plugins</code></p>
|
|
<p>Then you can drop the plugins of your choice into that folder :
|
|
<code>shell
|
|
git clone https://github.com/bunkerity/bunkerweb-plugins && \
|
|
cp -rp ./bunkerweb-plugins/* ./bw-data/plugins</code></p>
|
|
<p>Because BunkerWeb runs as an unprivileged user with UID and GID 101, you will need to edit the permissions :
|
|
<code>shell
|
|
chown -R root:101 bw-data && \
|
|
chmod -R 770 bw-data</code></p>
|
|
<p>When starting the BunkerWeb container, you will need to mount the folder on <code>/data</code> :
|
|
<code>shell
|
|
docker run \
|
|
...
|
|
-v "${PWD}/bw-data:/data" \
|
|
...
|
|
bunkerity/bunkerweb:1.4.0</code></p>
|
|
<p>Here is the docker-compose equivalent :
|
|
<code>yaml
|
|
mybunker:
|
|
image: bunkerity/bunkerweb:1.4.0
|
|
volumes:
|
|
- ./bw-data:/data
|
|
...</code></p>
|
|
</div>
|
|
<div class="tabbed-block">
|
|
<p>When using the <a href="/1.4/integrations/#docker-autoconf">Docker autoconf integration</a>, plugins must be written to the volume mounted on <code>/data</code>.</p>
|
|
<p>The easiest way to do it is by starting the Docker autoconf stack with a folder mounted on <code>/data</code> (instead of a named volume). Once the stack is started, you can copy the plugins of your choice to the <code>plugins</code> folder from your host :
|
|
<code>shell
|
|
git clone https://github.com/bunkerity/bunkerweb-plugins && \
|
|
cp -rp ./bunkerweb-plugins/* ./bw-data/plugins</code></p>
|
|
<p>Because BunkerWeb runs as an unprivileged user with UID and GID 101, you will need to edit the permissions :
|
|
<code>shell
|
|
chown -R root:101 bw-data && \
|
|
chmod -R 770 bw-data</code></p>
|
|
</div>
|
|
<div class="tabbed-block">
|
|
<p>When using the <a href="/1.4/integrations/#swarm">Swarm integration</a>, the easiest way of installing plugins is by using <code>docker exec</code> and downloading the plugins from the container.</p>
|
|
<p>Execute a shell inside the autoconf container (use <code>docker ps</code> to get the name) :
|
|
<code>shell
|
|
docker exec -it myautoconf /bin/bash</code></p>
|
|
<p>Once you have a shell inside the container, you can drop the plugins of your choice inside the <code>/data/plugins</code> folder :
|
|
<code>shell
|
|
git clone https://github.com/bunkerity/bunkerweb-plugins && \
|
|
cp -rp ./bunkerweb-plugins/* /data/plugins</code></p>
|
|
</div>
|
|
<div class="tabbed-block">
|
|
<p>When using the <a href="/1.4/integrations/#kubernetes">Kubernetes integration</a>, the easiest way of installing plugins is by using <code>kubectl exec</code> and downloading the plugins from the container.</p>
|
|
<p>Execute a shell inside the autoconf container (use <code>kubectl get pods</code> to get the name) :
|
|
<code>shell
|
|
kubectl exec -it myautoconf -- /bin/bash</code></p>
|
|
<p>Once you have a shell inside the container, you can drop the plugins of your choice inside the <code>/data/plugins</code> folder :
|
|
<code>shell
|
|
git clone https://github.com/bunkerity/bunkerweb-plugins && \
|
|
cp -rp ./bunkerweb-plugins/* /data/plugins</code></p>
|
|
</div>
|
|
<div class="tabbed-block">
|
|
<p>When using the <a href="/1.4/integrations/#linux">Linux integration</a>, plugins must be written to the <code>/opt/bunkerweb/plugins</code> folder :
|
|
<code>shell
|
|
git clone https://github.com/bunkerity/bunkerweb-plugins && \
|
|
cp -rp ./bunkerweb-plugins/* /data/plugins</code></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p>When a plugin is installed, you are ready to use it, please refer to the plugin documentation for more information.</p>
|
|
<h2 id="writing-a-plugin">Writing a plugin</h2>
|
|
<div class="admonition tip">
|
|
<p class="admonition-title">Existing plugins</p>
|
|
<p>If the documentation is not enough you can have a look at the existing source code of <a href="https://github.com/bunkerity/bunkerweb-plugins">official plugins</a> and the <a href="https://github.com/bunkerity/bunkerweb/tree/master/core">core plugins</a> (already included in BunkerWeb but they are plugins technically speaking).</p>
|
|
</div>
|
|
<p>The first step is to create a folder that will contain the plugin :</p>
|
|
<p><code>shell
|
|
mkdir myplugin && \
|
|
cd myplugin</code></p>
|
|
<h3 id="metadata">Metadata</h3>
|
|
<p>A file named <strong>plugin.json</strong> and written at the root of the plugin folder must contain metadata about the plugin. Here is an example :</p>
|
|
<p><code>json
|
|
{
|
|
"id": "myplugin",
|
|
"order": 42,
|
|
"name": "My Plugin",
|
|
"description": "Just an example plugin.",
|
|
"version": "1.0",
|
|
"settings": {
|
|
"DUMMY_SETTING": {
|
|
"context": "multisite",
|
|
"default": "1234",
|
|
"help": "Here is the help of the setting.",
|
|
"id": "dummy-id",
|
|
"label": "Dummy setting",
|
|
"regex": "^.*$",
|
|
"type": "text"
|
|
}
|
|
}
|
|
"jobs": [
|
|
{
|
|
"name": "my-job",
|
|
"file": "my-job.py",
|
|
"every": "hour"
|
|
}
|
|
]
|
|
}</code></p>
|
|
<p>Here are the details of the fields :</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th align="center">Field</th>
|
|
<th align="center">Mandatory</th>
|
|
<th align="center">Type</th>
|
|
<th align="left">Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="center"><code>id</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Internal ID for the plugin : must be unique among other plugins (including "core" ones) and contain only lowercase chars.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>order</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">int</td>
|
|
<td align="left">When the plugin should be executed during the access phase : <code>1</code> for whitelisting, <code>2</code> for blacklisting, <code>3</code> for "standard security feature" or <code>999</code> if your settings don't use the access phase.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>name</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Name of your plugin.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>description</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Description of your plugin.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>version</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Version of your plugin.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>settings</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">dict</td>
|
|
<td align="left">List of the settings of your plugin.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>jobs</code></td>
|
|
<td align="center">no</td>
|
|
<td align="center">list</td>
|
|
<td align="left">List of the jobs of your plugin.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Each setting has the following fields (the key is the ID of the settings used in a configuration) :</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th align="center">Field</th>
|
|
<th align="center">Mandatory</th>
|
|
<th align="center">Type</th>
|
|
<th align="left">Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="center"><code>context</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Context of the setting : <code>multisite</code> or <code>global</code>.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>default</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">The default value of the setting.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>help</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Help text about the plugin (shown in web UI).</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>id</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Internal ID used by the web UI for HTML elements.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>label</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Label shown by the web UI.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>regex</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">The regex used to validate the value provided by the user.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>type</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">The type of the field : <code>text</code>, <code>check</code> or <code>select</code>.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>multiple</code></td>
|
|
<td align="center">no</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Unique ID to group multiple settings with numbers as suffix.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>select</code></td>
|
|
<td align="center">no</td>
|
|
<td align="center">list</td>
|
|
<td align="left">List of possible string values when <code>type</code> is <code>select</code>.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Each job has the following fields :</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th align="center">Field</th>
|
|
<th align="center">Mandatory</th>
|
|
<th align="center">Type</th>
|
|
<th align="left">Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="center"><code>name</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Name of the job.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>file</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Name of the file inside the jobs folder.</td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>every</code></td>
|
|
<td align="center">yes</td>
|
|
<td align="center">string</td>
|
|
<td align="left">Job scheduling frequency : <code>minute</code>, <code>hour</code>, <code>day</code>, <code>week</code> or <code>once</code> (no frequency, only once before (re)generating the configuration).</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="configurations">Configurations</h3>
|
|
<p>You can add custom NGINX configurations by adding a folder named <strong>confs</strong> with content similar to the <a href="/1.4/quickstart-guide/#custom-configurations">custom configurations</a>. Each subfolder inside the <strong>confs</strong> will contain <a href="https://jinja.palletsprojects.com">jinja2</a> templates that will be generated and loaded at the corresponding context (<code>http</code>, <code>server-http</code> and <code>default-server-http</code>).</p>
|
|
<p>Here is an example for a configuration template file inside the <strong>confs/server-http</strong> folder named <strong>example.conf</strong> :</p>
|
|
<p><code>conf
|
|
location /setting {
|
|
default_type 'text/plain';
|
|
content_by_lua_block {
|
|
ngx.say('{{ DUMMY_SETTING }}')
|
|
}
|
|
}</code></p>
|
|
<p><code>{{ DUMMY_SETTING }}</code> will be replaced by the value of the <code>DUMMY_SETTING</code> chosen by the user of the plugin.</p>
|
|
<h3 id="lua">LUA</h3>
|
|
<h4 id="main-script">Main script</h4>
|
|
<p>Under the hood, BunkerWeb is using the <a href="https://github.com/openresty/lua-nginx-module">NGINX LUA module</a> to execute code within NGINX. Plugins that need to execute code must provide a lua file at the root directory of the plugin folder using the <code>id</code> value of <strong>plugin.json</strong> as its name. Here is an example named <strong>myplugin.lua</strong> :</p>
|
|
<p>```lua
|
|
local _M = {}
|
|
_M.__index = _M</p>
|
|
<p>local utils = require "utils"
|
|
local datastore = require "datastore"
|
|
local logger = require "logger"</p>
|
|
<p>function _M.new()
|
|
local self = setmetatable({}, _M)
|
|
self.dummy = "dummy"
|
|
return self, nil
|
|
end</p>
|
|
<p>function _M:init()
|
|
logger.log(ngx.NOTICE, "MYPLUGIN", "init called")
|
|
return true, "success"
|
|
end</p>
|
|
<p>function _M:access()
|
|
logger.log(ngx.NOTICE, "MYPLUGIN", "access called")
|
|
return true, "success", nil, nil
|
|
end</p>
|
|
<p>function _M:log()
|
|
logger.log(ngx.NOTICE, "MYPLUGIN", "log called")
|
|
return true, "success"
|
|
end</p>
|
|
<p>return _M
|
|
```</p>
|
|
<p>The 3 functions <code>init</code>, <code>access</code>, and <code>log</code> are automatically called during specific contexts. Here are the details of each function :</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th align="center">Function</th>
|
|
<th align="center">Context</th>
|
|
<th align="left">Description</th>
|
|
<th align="left">Return value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td align="center"><code>init</code></td>
|
|
<td align="center"><a href="https://github.com/openresty/lua-nginx-module#init_by_lua">init_by_lua</a></td>
|
|
<td align="left">Called when NGINX just started or received a reload order. the typical use case is to prepare any data that will be used by your plugin.</td>
|
|
<td align="left"><code>ret</code>, <code>err</code><ul><li><code>ret</code> (boolean) : true if no error else false</li><li><code>err</code> (string) : success or error message</li></ul></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>access</code></td>
|
|
<td align="center"><a href="https://github.com/openresty/lua-nginx-module#access_by_lua">access_by_lua</a></td>
|
|
<td align="left">Called on each request received by the server. The typical use case is to do the security checks here and deny the request if needed.</td>
|
|
<td align="left"><code>ret</code>, <code>err</code>, <code>return</code>, <code>status</code><ul><li><code>ret</code> (boolean) : true if no error else false</li><li><code>err</code> (string) : success or error message</li><li><code>return</code> (boolean) : true if you want to stop the access phase and send a status to the client</li><li><code>status</code> (number) : the return value to set if <code>return</code> is set to true</li></ul></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center"><code>log</code></td>
|
|
<td align="center"><a href="https://github.com/openresty/lua-nginx-module#log_by_lua">log_by_lua</a></td>
|
|
<td align="left">Called when a request has finished (and before it gets logged to the access logs). The typical use case is to make stats or compute counters for example.</td>
|
|
<td align="left"><code>ret</code>, <code>err</code><ul><li><code>ret</code> (boolean) : true if no error else false</li><li><code>err</code> (string) : success or error message</li></ul></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h4 id="libraries">Libraries</h4>
|
|
<p>All directives from <a href="https://github.com/openresty/lua-nginx-module">NGINX LUA module</a> are available. On top of that, you can use the LUA libraries included within BunkerWeb : see <a href="https://github.com/bunkerity/bunkerweb/blob/master/deps/clone.sh">this script</a> for the complete list.</p>
|
|
<p>If you need additional libraries, you can put them in the root folder of the plugin and access them by prefixing them with your plugin ID. Here is an example file named <strong>mylibrary.lua</strong> :</p>
|
|
<p>```lua
|
|
local _M = {}</p>
|
|
<p>_M.dummy = function ()
|
|
return "dummy"
|
|
end</p>
|
|
<p>return _M
|
|
```</p>
|
|
<p>And here is how you can use it from the <strong>myplugin.lua</strong> file :</p>
|
|
<p>```lua
|
|
local mylibrary = require "myplugin.mylibrary"</p>
|
|
<p>...</p>
|
|
<p>mylibrary.dummy()</p>
|
|
<p>...
|
|
```</p>
|
|
<h4 id="helpers">Helpers</h4>
|
|
<p>Some helpers modules provide common helpful functions :</p>
|
|
<ul>
|
|
<li><strong>datastore</strong> : access the global shared data (key/value store)</li>
|
|
<li><strong>logger</strong> : generate logs</li>
|
|
<li><strong>utils</strong> : various useful functions</li>
|
|
</ul>
|
|
<p>To access the functions, you first need to <strong>require</strong> the module :</p>
|
|
<p>```lua
|
|
...</p>
|
|
<p>local utils = require "utils"
|
|
local datastore = require "datastore"
|
|
local logger = require "logger"</p>
|
|
<p>...
|
|
```</p>
|
|
<p>Retrieve a setting value :</p>
|
|
<p><code>lua
|
|
local value, err = utils:get_variable("DUMMY_SETTING")
|
|
if not value then
|
|
logger.log(ngx.ERR, "MYPLUGIN", "can't retrieve setting DUMMY_SETTING : " .. err)
|
|
else
|
|
logger.log(ngx.NOTICE, "MYPLUGIN", "DUMMY_SETTING = " .. value)
|
|
end</code></p>
|
|
<p>Store something in the cache :</p>
|
|
<p><code>lua
|
|
local ok, err = datastore:set("plugin_myplugin_something", "somevalue")
|
|
if not value then
|
|
logger.log(ngx.ERR, "MYPLUGIN", "can't save plugin_myplugin_something into datastore : " .. err)
|
|
else
|
|
logger.log(ngx.NOTICE, "MYPLUGIN", "successfully saved plugin_myplugin_something into datastore into datastore")
|
|
end</code></p>
|
|
<p>Check if an IP address is global :</p>
|
|
<p><code>lua
|
|
local ret, err = utils.ip_is_global(ngx.var.remote_addr)
|
|
if ret == nil then
|
|
logger.log(ngx.ERR, "MYPLUGIN", "error while checking if IP " .. ngx.var.remote_addr .. " is global or not : " .. err)
|
|
elseif not ret then
|
|
logger.log(ngx.NOTICE, "MYPLUGIN", "IP " .. ngx.var.remote_addr .. " is not global")
|
|
else
|
|
logger.log(ngx.NOTICE, "MYPLUGIN", "IP " .. ngx.var.remote_addr .. " is global")
|
|
end</code></p>
|
|
<div class="admonition tip">
|
|
<p class="admonition-title">More examples</p>
|
|
<p>If you want to see the full list of available functions, you can have a look at the files present in the <a href="https://github.com/bunkerity/bunkerweb/tree/master/lua">lua directory</a> of the repository.</p>
|
|
</div>
|
|
<h3 id="jobs">Jobs</h3>
|
|
<p>BunkerWeb uses an internal job scheduler for periodic tasks like renewing certificates with certbot, downloading blacklists, downloading MMDB files, ... You can add tasks of your choice by putting them inside a subfolder named <strong>jobs</strong> and listing them in the <strong>plugin.json</strong> metadata file. Don't forget to add the execution permissions for everyone to avoid any problems when a user is cloning and installing your plugin.</p>
|
|
|
|
|
|
</article>
|
|
</div>
|
|
</div>
|
|
|
|
</main>
|
|
|
|
<footer class="md-footer">
|
|
|
|
<nav class="md-footer__inner md-grid" aria-label="Footer">
|
|
|
|
|
|
<a href="../troubleshooting/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Troubleshooting" rel="prev">
|
|
<div class="md-footer__button md-icon">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12z"/></svg>
|
|
</div>
|
|
<div class="md-footer__title">
|
|
<div class="md-ellipsis">
|
|
<span class="md-footer__direction">
|
|
Previous
|
|
</span>
|
|
Troubleshooting
|
|
</div>
|
|
</div>
|
|
</a>
|
|
|
|
|
|
|
|
<a href="../about/" class="md-footer__link md-footer__link--next" aria-label="Next: About" rel="next">
|
|
<div class="md-footer__title">
|
|
<div class="md-ellipsis">
|
|
<span class="md-footer__direction">
|
|
Next
|
|
</span>
|
|
About
|
|
</div>
|
|
</div>
|
|
<div class="md-footer__button md-icon">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4z"/></svg>
|
|
</div>
|
|
</a>
|
|
|
|
</nav>
|
|
|
|
<div class="md-footer-meta md-typeset">
|
|
<div class="md-footer-meta__inner md-grid">
|
|
<div class="md-copyright">
|
|
|
|
<div class="md-copyright__highlight">
|
|
Copyright © 2022 Bunkerity
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
<div class="md-social">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<a href="https://discord.com/invite/fTf46FmtyD" target="_blank" rel="noopener" title="discord.com" class="md-social__link">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M524.531 69.836a1.5 1.5 0 0 0-.764-.7A485.065 485.065 0 0 0 404.081 32.03a1.816 1.816 0 0 0-1.923.91 337.461 337.461 0 0 0-14.9 30.6 447.848 447.848 0 0 0-134.426 0 309.541 309.541 0 0 0-15.135-30.6 1.89 1.89 0 0 0-1.924-.91 483.689 483.689 0 0 0-119.688 37.107 1.712 1.712 0 0 0-.788.676C39.068 183.651 18.186 294.69 28.43 404.354a2.016 2.016 0 0 0 .765 1.375 487.666 487.666 0 0 0 146.825 74.189 1.9 1.9 0 0 0 2.063-.676A348.2 348.2 0 0 0 208.12 430.4a1.86 1.86 0 0 0-1.019-2.588 321.173 321.173 0 0 1-45.868-21.853 1.885 1.885 0 0 1-.185-3.126 251.047 251.047 0 0 0 9.109-7.137 1.819 1.819 0 0 1 1.9-.256c96.229 43.917 200.41 43.917 295.5 0a1.812 1.812 0 0 1 1.924.233 234.533 234.533 0 0 0 9.132 7.16 1.884 1.884 0 0 1-.162 3.126 301.407 301.407 0 0 1-45.89 21.83 1.875 1.875 0 0 0-1 2.611 391.055 391.055 0 0 0 30.014 48.815 1.864 1.864 0 0 0 2.063.7A486.048 486.048 0 0 0 610.7 405.729a1.882 1.882 0 0 0 .765-1.352c12.264-126.783-20.532-236.912-86.934-334.541zM222.491 337.58c-28.972 0-52.844-26.587-52.844-59.239s23.409-59.241 52.844-59.241c29.665 0 53.306 26.82 52.843 59.239 0 32.654-23.41 59.241-52.843 59.241zm195.38 0c-28.971 0-52.843-26.587-52.843-59.239s23.409-59.241 52.843-59.241c29.667 0 53.307 26.82 52.844 59.239 0 32.654-23.177 59.241-52.844 59.241z"/></svg>
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<a href="https://github.com/bunkerity" target="_blank" rel="noopener" title="github.com" class="md-social__link">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<a href="https://www.linkedin.com/company/bunkerity/" target="_blank" rel="noopener" title="www.linkedin.com" class="md-social__link">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"/></svg>
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<a href="https://twitter.com/bunkerity" target="_blank" rel="noopener" title="twitter.com" class="md-social__link">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/></svg>
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
</div>
|
|
<div class="md-dialog" data-md-component="dialog">
|
|
<div class="md-dialog__inner md-typeset"></div>
|
|
</div>
|
|
<script id="__config" type="application/json">{"base": "..", "features": ["navigation.tracking", "navigation.tabs", "navigation.tabs.sticky", "toc.integrate"], "translations": {"clipboard.copy": "Copy to clipboard", "clipboard.copied": "Copied to clipboard", "search.config.lang": "en", "search.config.pipeline": "trimmer, stopWordFilter", "search.config.separator": "[\\s\\-]+", "search.placeholder": "Search", "search.result.placeholder": "Type to start searching", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.term.missing": "Missing", "select.version.title": "Select version"}, "search": "../assets/javascripts/workers/search.bd0b6b67.min.js", "version": {"provider": "mike", "version": "latest"}}</script>
|
|
|
|
|
|
<script src="../assets/javascripts/bundle.467223ff.min.js"></script>
|
|
|
|
|
|
</body>
|
|
</html> |