Module:Protection banner/config and CollabNet Guide: Difference between pages

From Computernewb Wiki
(Difference between pages)
Jump to navigation Jump to search
enwikipedia>Fayenatic london
(Update categories from "fully-protected" to "fully protected", removing hyphen, per valid request at WP:CFDS)
 
No edit summary
 
Line 1: Line 1:
This guide will walk you through setting up a Virtual Network for your VMs. This will allow you to route your VM traffic behind a VM (strongly recommended for several reasons), filter web traffic, and prevent access to your local network from the VMs.
-- This module provides configuration data for [[Module:Protection banner]].


= Prerequisites =
return {
* An hour or two of your time
* A host running a Linux distribution
* Basic computer and command line literacy. Nobody is going to hold your hand


= Host Preparation =
--------------------------------------------------------------------------------
--
-- BANNER DATA
--
--------------------------------------------------------------------------------


== IP Forwarding ==
--[[
-- Banner data consists of six fields:
-- * text - the main protection text that appears at the top of protection
-- banners.
-- * explanation - the text that appears below the main protection text, used
-- to explain the details of the protection.
-- * tooltip - the tooltip text you see when you move the mouse over a small
-- padlock icon.
-- * link - the page that the small padlock icon links to.
-- * alt - the alt text for the small padlock icon. This is also used as tooltip
-- text for the large protection banners.
-- * image - the padlock image used in both protection banners and small padlock
-- icons.
--
-- The module checks in three separate tables to find a value for each field.
-- First it checks the banners table, which has values specific to the reason
-- for the page being protected. Then the module checks the defaultBanners
-- table, which has values specific to each protection level. Finally, the
-- module checks the masterBanner table, which holds data for protection
-- templates to use if no data has been found in the previous two tables.
--
-- The values in the banner data can take parameters. These are specified
-- using ${TEXTLIKETHIS} (a dollar sign preceding a parameter name
-- enclosed in curly braces).
--
-- Available parameters:
--
-- ${CURRENTVERSION} - a link to the page history or the move log, with the
-- display message "current-version-edit-display" or
-- "current-version-move-display".
--
-- ${EDITREQUEST} - a link to create an edit request for the current page.
--
-- ${EXPLANATIONBLURB} - an explanation blurb, e.g. "Please discuss any changes
-- on the talk page; you may submit a request to ask an administrator to make
-- an edit if it is minor or supported by consensus."
--
-- ${IMAGELINK} - a link to set the image to, depending on the protection
-- action and protection level.
--
-- ${INTROBLURB} - the PROTECTIONBLURB parameter, plus the expiry if an expiry
-- is set. E.g. "Editing of this page by new or unregistered users is currently
-- disabled until dd Month YYYY."
--
-- ${INTROFRAGMENT} - the same as ${INTROBLURB}, but without final punctuation
-- so that it can be used in run-on sentences.
--
-- ${PAGETYPE} - the type of the page, e.g. "article" or "template".
-- Defined in the cfg.pagetypes table.
--
-- ${PROTECTIONBLURB} - a blurb explaining the protection level of the page, e.g.
-- "Editing of this page by new or unregistered users is currently disabled"
--
-- ${PROTECTIONDATE} - the protection date, if it has been supplied to the
-- template.
--
-- ${PROTECTIONLEVEL} - the protection level, e.g. "fully protected" or
-- "semi-protected".
--
-- ${PROTECTIONLOG} - a link to the protection log or the pending changes log,
-- depending on the protection action.
--
-- ${TALKPAGE} - a link to the talk page. If a section is specified, links
-- straight to that talk page section.
--
-- ${TOOLTIPBLURB} - uses the PAGETYPE, PROTECTIONTYPE and EXPIRY parameters to
-- create a blurb like "This template is semi-protected", or "This article is
-- move-protected until DD Month YYYY".
--
-- ${VANDAL} - links for the specified username (or the root page name)
-- using Module:Vandal-m.
--
-- Functions
--
-- For advanced users, it is possible to use Lua functions instead of strings
-- in the banner config tables. Using functions gives flexibility that is not
-- possible just by using parameters. Functions take two arguments, the
-- protection object and the template arguments, and they must output a string.
--
-- For example:
--
-- text = function (protectionObj, args)
-- if protectionObj.level == 'autoconfirmed' then
-- return 'foo'
-- else
-- return 'bar'
-- end
-- end
--
-- Some protection object properties and methods that may be useful:
-- protectionObj.action - the protection action
-- protectionObj.level - the protection level
-- protectionObj.reason - the protection reason
-- protectionObj.expiry - the expiry. Nil if unset, the string "indef" if set
-- to indefinite, and the protection time in unix time if temporary.
-- protectionObj.protectionDate - the protection date in unix time, or nil if
-- unspecified.
-- protectionObj.bannerConfig - the banner config found by the module. Beware
-- of editing the config field used by the function, as it could create an
-- infinite loop.
-- protectionObj:isProtected - returns a boolean showing whether the page is
-- protected.
-- protectionObj:isTemporary - returns a boolean showing whether the expiry is
-- temporary.
-- protectionObj:isIncorrect - returns a boolean showing whether the protection
-- template is incorrect.
--]]


First, we're going to enable IP forwarding on your host. This will allow traffic from the VMs to be routed to and from the router. The following command will write this to the sysctl configuration.
-- The master banner data, used if no values have been found in banners or
{{code|<nowiki>
-- defaultBanners.
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/router.conf
masterBanner = {
</nowiki>}}
text = '${INTROBLURB}',
To apply, either reboot or run the following:
explanation = '${EXPLANATIONBLURB}',
{{code|
tooltip = '${TOOLTIPBLURB}',
sudo sysctl --system
link = '${IMAGELINK}',
}}
alt = 'Page ${PROTECTIONLEVEL}'
},


== Bridge configuration ==
-- The default banner data. This holds banner data for different protection
-- levels.
-- *required* - this table needs edit, move, autoreview and upload subtables.
defaultBanners = {
edit = {},
move = {},
autoreview = {
default = {
alt = 'Page protected with pending changes',
tooltip = 'All edits by unregistered and new users are subject to review prior to becoming visible to unregistered users',
image = 'Pending-protection-shackle.svg'
}
},
upload = {}
},


Next, we'll set up a network bridge with multiple TAP interfaces. You can think of a TAP interface as a virtual Ethernet port connected to your VM, and the network bridge as a virtual Ethernet switch connecting them all together. The instructions to do this vary based on your network daemon. For this guide we'll assume (and recommend) you're using systemd-networkd, or netifrc on OpenRC.
-- The banner data. This holds banner data for different protection reasons.
-- In fact, the reasons specified in this table control which reasons are
-- valid inputs to the first positional parameter.
--
-- There is also a non-standard "description" field that can be used for items
-- in this table. This is a description of the protection reason for use in the
-- module documentation.
--
-- *required* - this table needs edit, move, autoreview and upload subtables.
banners = {
edit = {
blp = {
description = 'For pages protected to promote compliance with the'
.. ' [[Wikipedia:Biographies of living persons'
.. '|biographies of living persons]] policy',
text = '${INTROFRAGMENT} to promote compliance with'
.. ' [[Wikipedia:Biographies of living persons'
.. "|Wikipedia's&nbsp;policy on&nbsp;the&nbsp;biographies"
.. ' of&nbsp;living&nbsp;people]].',
tooltip = '${TOOLTIPFRAGMENT} to promote compliance with the policy on'
.. ' biographies of living persons',
},
dmca = {
description = 'For pages protected by the Wikimedia Foundation'
.. ' due to [[Digital Millennium Copyright Act]] takedown requests',
explanation = function (protectionObj, args)
local ret = 'Pursuant to a rights owner notice under the Digital'
.. ' Millennium Copyright Act (DMCA) regarding some content'
.. ' in this article, the Wikimedia Foundation acted under'
.. ' applicable law and took down and restricted the content'
.. ' in question.'
if args.notice then
ret = ret .. ' A copy of the received notice can be found here: '
.. args.notice .. '.'
end
ret = ret .. ' For more information, including websites discussing'
.. ' how to file a counter-notice, please see'
.. " [[Wikipedia:Office actions]] and the article's ${TALKPAGE}."
.. "'''Do not remove this template from the article until the"
.. " restrictions are withdrawn'''."
return ret
end,
image = 'Office-protection-shackle.svg',
},
dispute = {
description = 'For pages protected due to editing disputes',
text = function (protectionObj, args)
-- Find the value of "disputes".
local display = 'disputes'
local disputes
if args.section then
disputes = string.format(
'[[%s:%s#%s|%s]]',
mw.site.namespaces[protectionObj.title.namespace].talk.name,
protectionObj.title.text,
args.section,
display
)
else
disputes = display
end


We'll also be giving the <code>collabvm</code> group access to the TAPs. If you have yet to create a user for CollabVM, do so now.
-- Make the blurb, depending on the expiry.
local msg
if type(protectionObj.expiry) == 'number' then
msg = '${INTROFRAGMENT} or until editing %s have been resolved.'
else
msg = '${INTROFRAGMENT} until editing %s have been resolved.'
end
return string.format(msg, disputes)
end,
explanation = "This protection is '''not''' an endorsement of the"
.. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}',
tooltip = '${TOOLTIPFRAGMENT} due to editing disputes',
},
ecp = {
description = 'For articles in topic areas authorized by'
.. ' [[Wikipedia:Arbitration Committee|ArbCom]] or'
.. ' meets the criteria for community use',
tooltip = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}',
alt = 'Extended-protected ${PAGETYPE}',
},
mainpage = {
description = 'For pages protected for being displayed on the [[Main Page]]',
text = 'This file is currently'
.. ' [[Wikipedia:This page is protected|protected]] from'
.. ' editing because it is currently or will soon be displayed'
.. ' on the [[Main Page]].',
explanation = 'Images on the Main Page are protected due to their high'
.. ' visibility. Please discuss any necessary changes on the ${TALKPAGE}.'
.. '<br /><span style="font-size:90%;">'
.. "'''Administrators:''' Once this image is definitely off the Main Page,"
.. ' please unprotect this file, or reduce to semi-protection,'
.. ' as appropriate.</span>',
},
office = {
description = 'For pages protected by the Wikimedia Foundation',
text = function (protectionObj, args)
local ret = 'This ${PAGETYPE} is currently under the'
.. ' scrutiny of the'
.. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]'
.. ' and is protected.'
if protectionObj.protectionDate then
ret = ret .. ' It has been protected since ${PROTECTIONDATE}.'
end
return ret
end,
explanation = "If you can edit this page, please discuss all changes and"
.. " additions on the ${TALKPAGE} first. '''Do not remove protection from this"
.. " page unless you are authorized by the Wikimedia Foundation to do"
.. " so.'''",
image = 'Office-protection-shackle.svg',
},
reset = {
description = 'For pages protected by the Wikimedia Foundation and'
.. ' "reset" to a bare-bones version',
text = 'This ${PAGETYPE} is currently under the'
.. ' scrutiny of the'
.. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]'
.. ' and is protected.',
explanation = function (protectionObj, args)
local ret = ''
if protectionObj.protectionDate then
ret = ret .. 'On ${PROTECTIONDATE} this ${PAGETYPE} was'
else
ret = ret .. 'This ${PAGETYPE} has been'
end
ret = ret .. ' reduced to a'
.. ' simplified, "bare bones" version so that it may be completely'
.. ' rewritten to ensure it meets the policies of'
.. ' [[WP:NPOV|Neutral Point of View]] and [[WP:V|Verifiability]].'
.. ' Standard Wikipedia policies will apply to its rewriting—which'
.. ' will eventually be open to all editors—and will be strictly'
.. ' enforced. The ${PAGETYPE} has been ${PROTECTIONLEVEL} while'
.. ' it is being rebuilt.\n\n'
.. 'Any insertion of material directly from'
.. ' pre-protection revisions of the ${PAGETYPE} will be removed, as'
.. ' will any material added to the ${PAGETYPE} that is not properly'
.. ' sourced. The associated talk page(s) were also cleared on the'
.. " same date.\n\n"
.. "If you can edit this page, please discuss all changes and"
.. " additions on the ${TALKPAGE} first. '''Do not override"
.. " this action, and do not remove protection from this page,"
.. " unless you are authorized by the Wikimedia Foundation"
.. " to do so. No editor may remove this notice.'''"


=== Systemd ===
return ret
end,
image = 'Office-protection-shackle.svg',
},
sock = {
description = 'For pages protected due to'
.. ' [[Wikipedia:Sock puppetry|sock puppetry]]',
text = '${INTROFRAGMENT} to prevent [[Wikipedia:Sock puppetry|sock puppets]] of'
.. ' [[Wikipedia:Blocking policy|blocked]] or'
.. ' [[Wikipedia:Banning policy|banned users]]'
.. ' from editing it.',
tooltip = '${TOOLTIPFRAGMENT} to prevent sock puppets of blocked or banned users from'
.. ' editing it',
},
template = {
description = 'For [[Wikipedia:High-risk templates|high-risk]]'
.. ' templates and Lua modules',
text = 'This is a permanently [[Help:Protection|protected]] ${PAGETYPE},'
.. ' as it is [[Wikipedia:High-risk templates|high-risk]].',
explanation = 'Please discuss any changes on the ${TALKPAGE}; you may'
.. ' ${EDITREQUEST} to ask an'
.. ' [[Wikipedia:Administrators|administrator]] or'
.. ' [[Wikipedia:Template editor|template editor]] to make an edit if'
.. ' it is [[Help:Minor edit#When to mark an edit as a minor edit'
.. '|uncontroversial]] or supported by'
.. ' [[Wikipedia:Consensus|consensus]]. You can also'
.. ' [[Wikipedia:Requests for page protection|request]] that the page be'
.. ' unprotected.',
tooltip = 'This high-risk ${PAGETYPE} is permanently ${PROTECTIONLEVEL}'
.. ' to prevent vandalism',
alt = 'Permanently protected ${PAGETYPE}',
},
usertalk = {
description = 'For pages protected against disruptive edits by a'
.. ' particular user',
text = '${INTROFRAGMENT} to prevent ${VANDAL} from using it to make disruptive edits,'
.. ' such as abusing the'
.. ' &#123;&#123;[[Template:unblock|unblock]]&#125;&#125; template.',
explanation = 'If you cannot edit this user talk page and you need to'
.. ' make a change or leave a message, you can'
.. ' [[Wikipedia:Requests for page protection'
.. '#Current requests for edits to a protected page'
.. '|request an edit]],'
.. ' [[Wikipedia:Requests for page protection'
.. '#Current requests for reduction in protection level'
.. '|request unprotection]],'
.. ' [[Special:Userlogin|log in]],'
.. ' or [[Special:UserLogin/signup|create an account]].',
},
vandalism = {
description = 'For pages protected against'
.. ' [[Wikipedia:Vandalism|vandalism]]',
text = '${INTROFRAGMENT} due to [[Wikipedia:Vandalism|vandalism]].',
explanation = function (protectionObj, args)
local ret = ''
if protectionObj.level == 'sysop' then
ret = ret .. "This protection is '''not''' an endorsement of the"
.. ' ${CURRENTVERSION}. '
end
return ret .. '${EXPLANATIONBLURB}'
end,
tooltip = '${TOOLTIPFRAGMENT} due to vandalism',
}
},
move = {
dispute = {
description = 'For pages protected against page moves due to'
.. ' disputes over the page title',
explanation = "This protection is '''not''' an endorsement of the"
.. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}',
image = 'Move-protection-shackle.svg'
},
vandalism = {
description = 'For pages protected against'
.. ' [[Wikipedia:Vandalism#Page-move vandalism'
.. ' |page-move vandalism]]'
}
},
autoreview = {},
upload = {}
},


All network configuration is done in the <code>/etc/systemd/network</code> directory. We'll start by creating the bridge itself. The following simply creates a network bridge named <code>collabnet</code>
--------------------------------------------------------------------------------
--
-- GENERAL DATA TABLES
--
--------------------------------------------------------------------------------


'''/etc/systemd/network/collabnet.netdev'''
--------------------------------------------------------------------------------
{{code|<nowiki>
-- Protection blurbs
[NetDev]
--------------------------------------------------------------------------------
Name=collabnet
Kind=bridge
</nowiki>}}


Next, we'll create a TAP for our router VM. The following creates a new TAP named <code>ktrouter</code>
-- This table produces the protection blurbs available with the
-- ${PROTECTIONBLURB} parameter. It is sorted by protection action and
-- protection level, and is checked by the module in the following order:
-- 1. page's protection action, page's protection level
-- 2. page's protection action, default protection level
-- 3. "edit" protection action, default protection level
--
-- It is possible to use banner parameters inside this table.
-- *required* - this table needs edit, move, autoreview and upload subtables.
protectionBlurbs = {
edit = {
default = 'This ${PAGETYPE} is currently [[Help:Protection|'
.. 'protected]] from editing',
autoconfirmed = 'Editing of this ${PAGETYPE} by [[Wikipedia:User access'
.. ' levels#New users|new]] or [[Wikipedia:User access levels#Unregistered'
.. ' users|unregistered]] users is currently [[Help:Protection|disabled]]',
extendedconfirmed = 'This ${PAGETYPE} is currently under extended confirmed protection',
},
move = {
default = 'This ${PAGETYPE} is currently [[Help:Protection|protected]]'
.. ' from [[Help:Moving a page|page moves]]'
},
autoreview = {
default = 'All edits made to this ${PAGETYPE} by'
.. ' [[Wikipedia:User access levels#New users|new]] or'
.. ' [[Wikipedia:User access levels#Unregistered users|unregistered]]'
.. ' users are currently'
.. ' [[Wikipedia:Pending changes|subject to review]]'
},
upload = {
default = 'Uploading new versions of this ${PAGETYPE} is currently disabled'
}
},


'''/etc/systemd/network/ktrouter.netdev'''
{{code|<nowiki>
[NetDev]
Name=ktrouter
Kind=tap


[Tap]
--------------------------------------------------------------------------------
Group=collabvm
-- Explanation blurbs
</nowiki>}}
--------------------------------------------------------------------------------


Now, you can create a TAP for each VM. The following adds a TAP named <code>ktvm1</code>. To add more, repeat the following, creating files named <code>ktvm2</code>, <code>ktvm3</code>, and so on. Make sure to also change the TAP name inside the file to match this.
-- This table produces the explanation blurbs available with the
-- ${EXPLANATIONBLURB} parameter. It is sorted by protection action,
-- protection level, and whether the page is a talk page or not. If the page is
-- a talk page it will have a talk key of "talk"; otherwise it will have a talk
-- key of "subject". The table is checked in the following order:
-- 1. page's protection action, page's protection level, page's talk key
-- 2. page's protection action, page's protection level, default talk key
-- 3. page's protection action, default protection level, page's talk key
-- 4. page's protection action, default protection level, default talk key
--
-- It is possible to use banner parameters inside this table.
-- *required* - this table needs edit, move, autoreview and upload subtables.
explanationBlurbs = {
edit = {
autoconfirmed = {
subject = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details. If you'
.. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can'
.. ' ${EDITREQUEST}, discuss changes on the ${TALKPAGE},'
.. ' [[Wikipedia:Requests for page protection'
.. '#Current requests for reduction in protection level'
.. '|request unprotection]], [[Special:Userlogin|log in]], or'
.. ' [[Special:UserLogin/signup|create an account]].',
default = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details. If you'
.. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can'
.. ' [[Wikipedia:Requests for page protection'
.. '#Current requests for reduction in protection level'
.. '|request unprotection]], [[Special:Userlogin|log in]], or'
.. ' [[Special:UserLogin/signup|create an account]].',
},
extendedconfirmed = {
default = 'Extended confirmed protection prevents edits from all unregistered editors'
.. ' and registered users with fewer than 30 days tenure and 500 edits.'
.. ' The [[Wikipedia:Protection policy#extended|policy on community use]]'
.. ' specifies that extended confirmed protection can be applied to combat'
.. ' disruption, if semi-protection has proven to be ineffective.'
.. ' Extended confirmed protection may also be applied to enforce'
.. ' [[Wikipedia:Arbitration Committee|arbitration sanctions]].'
.. ' Please discuss any changes on the ${TALKPAGE}; you may'
.. ' ${EDITREQUEST} to ask for uncontroversial changes supported by'
.. ' [[Wikipedia:Consensus|consensus]].'
},
default = {
subject = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'
.. ' Please discuss any changes on the ${TALKPAGE}; you'
.. ' may ${EDITREQUEST} to ask an'
.. ' [[Wikipedia:Administrators|administrator]] to make an edit if it'
.. ' is [[Help:Minor edit#When to mark an edit as a minor edit'
.. '|uncontroversial]] or supported by [[Wikipedia:Consensus'
.. '|consensus]]. You may also [[Wikipedia:Requests for'
.. ' page protection#Current requests for reduction in protection level'
.. '|request]] that this page be unprotected.',
default = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'
.. ' You may [[Wikipedia:Requests for page'
.. ' protection#Current requests for edits to a protected page|request an'
.. ' edit]] to this page, or [[Wikipedia:Requests for'
.. ' page protection#Current requests for reduction in protection level'
.. '|ask]] for it to be unprotected.'
}
},
move = {
default = {
subject = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'
.. ' The page may still be edited but cannot be moved'
.. ' until unprotected. Please discuss any suggested moves on the'
.. ' ${TALKPAGE} or at [[Wikipedia:Requested moves]]. You can also'
.. ' [[Wikipedia:Requests for page protection|request]] that the page be'
.. ' unprotected.',
default = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'
.. ' The page may still be edited but cannot be moved'
.. ' until unprotected. Please discuss any suggested moves at'
.. ' [[Wikipedia:Requested moves]]. You can also'
.. ' [[Wikipedia:Requests for page protection|request]] that the page be'
.. ' unprotected.'
}
},
autoreview = {
default = {
default = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'
.. ' Edits to this ${PAGETYPE} by new and unregistered users'
.. ' will not be visible to readers until they are accepted by'
.. ' a reviewer. To avoid the need for your edits to be'
.. ' reviewed, you may'
.. ' [[Wikipedia:Requests for page protection'
.. '#Current requests for reduction in protection level'
.. '|request unprotection]], [[Special:Userlogin|log in]], or'
.. ' [[Special:UserLogin/signup|create an account]].'
},
},
upload = {
default = {
default = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'
.. ' The page may still be edited but new versions of the file'
.. ' cannot be uploaded until it is unprotected. You can'
.. ' request that a new version be uploaded by using a'
.. ' [[Wikipedia:Edit requests|protected edit request]], or you'
.. ' can [[Wikipedia:Requests for page protection|request]]'
.. ' that the file be unprotected.'
}
}
},


'''/etc/systemd/network/ktvm1.netdev'''
--------------------------------------------------------------------------------
{{code|<nowiki>
-- Protection levels
[NetDev]
--------------------------------------------------------------------------------
Name=ktvm1
Kind=tap


[Tap]
-- This table provides the data for the ${PROTECTIONLEVEL} parameter, which
Group=collabvm
-- produces a short label for different protection levels. It is sorted by
</nowiki>}}
-- protection action and protection level, and is checked in the following
-- order:
-- 1. page's protection action, page's protection level
-- 2. page's protection action, default protection level
-- 3. "edit" protection action, default protection level
--
-- It is possible to use banner parameters inside this table.
-- *required* - this table needs edit, move, autoreview and upload subtables.
protectionLevels = {
edit = {
default = 'protected',
templateeditor = 'template-protected',
extendedconfirmed = 'extended-protected',
autoconfirmed = 'semi-protected',
},
move = {
default = 'move-protected'
},
autoreview = {
},
upload = {
default = 'upload-protected'
}
},


Finally, we connect all of our TAPs to the <code>collabnet</code> bridge.
--------------------------------------------------------------------------------
-- Images
--------------------------------------------------------------------------------


'''/etc/systemd/network/collabnet.network'''
-- This table lists different padlock images for each protection action and
{{code|<nowiki>
-- protection level. It is used if an image is not specified in any of the
[Match]
-- banner data tables, and if the page does not satisfy the conditions for using
Name=kt*
-- the ['image-filename-indef'] image. It is checked in the following order:
-- 1. page's protection action, page's protection level
-- 2. page's protection action, default protection level
images = {
edit = {
default = 'Full-protection-shackle.svg',
templateeditor = 'Template-protection-shackle.svg',
extendedconfirmed = 'Extended-protection-shackle.svg',
autoconfirmed = 'Semi-protection-shackle.svg'
},
move = {
default = 'Move-protection-shackle.svg',
},
autoreview = {
default = 'Pending-protection-shackle.svg'
},
upload = {
default = 'Upload-protection-shackle.svg'
}
},


[Network]
-- Pages with a reason specified in this table will show the special "indef"
Bridge=collabnet
-- padlock, defined in the 'image-filename-indef' message, if no expiry is set.
</nowiki>}}
indefImageReasons = {
template = true
},


Finally, you can either reboot or run the following to reload the network configuration
--------------------------------------------------------------------------------
{{code|
-- Image links
sudo systemctl restart systemd-networkd
--------------------------------------------------------------------------------
}}


=== OpenRC ===
-- This table provides the data for the ${IMAGELINK} parameter, which gets
-- the image link for small padlock icons based on the page's protection action
-- and protection level. It is checked in the following order:
-- 1. page's protection action, page's protection level
-- 2. page's protection action, default protection level
-- 3. "edit" protection action, default protection level
--
-- It is possible to use banner parameters inside this table.
-- *required* - this table needs edit, move, autoreview and upload subtables.
imageLinks = {
edit = {
default = 'Wikipedia:Protection policy#full',
templateeditor = 'Wikipedia:Protection policy#template',
extendedconfirmed = 'Wikipedia:Protection policy#extended',
autoconfirmed = 'Wikipedia:Protection policy#semi'
},
move = {
default = 'Wikipedia:Protection policy#move'
},
autoreview = {
default = 'Wikipedia:Protection policy#pending'
},
upload = {
default = 'Wikipedia:Protection policy#upload'
}
},


TODO
--------------------------------------------------------------------------------
-- Padlock indicator names
--------------------------------------------------------------------------------


= Setting up the router =
-- This table provides the "name" attribute for the <indicator> extension tag
-- with which small padlock icons are generated. All indicator tags on a page
-- are displayed in alphabetical order based on this attribute, and with
-- indicator tags with duplicate names, the last tag on the page wins.
-- The attribute is chosen based on the protection action; table keys must be a
-- protection action name or the string "default".
padlockIndicatorNames = {
autoreview = 'pp-autoreview',
default = 'pp-default'
},


Now for the """fun""" part. We're going to set up a router VM, responsible for routing all traffic from the VMs. You'll want to give it '''two''' network adapters, one for the WAN (the internet) and another LAN (your VMs, the collabnet bridge).
--------------------------------------------------------------------------------
-- Protection categories
--------------------------------------------------------------------------------


The router will be running Debian. You can either netboot it from within QEMU (Press Ctrl+B when prompted on boot, enter <code>dhcp && boot http://boot.netboot.xyz</code>) or download an ISO and mount it. Your choice.
--[[
-- The protection categories are stored in the protectionCategories table.
-- Keys to this table are made up of the following strings:
--
-- 1. the expiry date
-- 2. the namespace
-- 3. the protection reason (e.g. "dispute" or "vandalism")
-- 4. the protection level (e.g. "sysop" or "autoconfirmed")
-- 5. the action (e.g. "edit" or "move")
--
-- When the module looks up a category in the table, first it will will check to
-- see a key exists that corresponds to all five parameters. For example, a
-- user page semi-protected from vandalism for two weeks would have the key
-- "temp-user-vandalism-autoconfirmed-edit". If no match is found, the module
-- changes the first part of the key to "all" and checks the table again. It
-- keeps checking increasingly generic key combinations until it finds the
-- field, or until it reaches the key "all-all-all-all-all".
--
-- The module uses a binary matrix to determine the order in which to search.
-- This is best demonstrated by a table. In this table, the "0" values
-- represent "all", and the "1" values represent the original data (e.g.
-- "indef" or "file" or "vandalism").
--
-- expiry namespace reason level action
-- order
-- 1 1 1 1 1 1
-- 2 0 1 1 1 1
-- 3 1 0 1 1 1
-- 4 0 0 1 1 1
-- 5 1 1 0 1 1
-- 6 0 1 0 1 1
-- 7 1 0 0 1 1
-- 8 0 0 0 1 1
-- 9 1 1 1 0 1
-- 10 0 1 1 0 1
-- 11 1 0 1 0 1
-- 12 0 0 1 0 1
-- 13 1 1 0 0 1
-- 14 0 1 0 0 1
-- 15 1 0 0 0 1
-- 16 0 0 0 0 1
-- 17 1 1 1 1 0
-- 18 0 1 1 1 0
-- 19 1 0 1 1 0
-- 20 0 0 1 1 0
-- 21 1 1 0 1 0
-- 22 0 1 0 1 0
-- 23 1 0 0 1 0
-- 24 0 0 0 1 0
-- 25 1 1 1 0 0
-- 26 0 1 1 0 0
-- 27 1 0 1 0 0
-- 28 0 0 1 0 0
-- 29 1 1 0 0 0
-- 30 0 1 0 0 0
-- 31 1 0 0 0 0
-- 32 0 0 0 0 0
--
-- In this scheme the action has the highest priority, as it is the last
-- to change, and the expiry has the least priority, as it changes the most.
-- The priorities of the expiry, the protection level and the action are
-- fixed, but the priorities of the reason and the namespace can be swapped
-- through the use of the cfg.bannerDataNamespaceHasPriority table.
--]]


Here is an example QEMU start command for the router VM. You'll need to create the disk image and adjust paths.
-- If the reason specified to the template is listed in this table,
{{code|<nowiki>
-- namespace data will take priority over reason data in the protectionCategories
sudo -u collabvm qemu-system-x86_64 \
-- table.
-accel kvm \
reasonsWithNamespacePriority = {
-cpu host \
vandalism = true,
-m 2G \
},
-hda /srv/collabvm/router/router.qcow2 \
-netdev user,id=wan -device virtio-net,netdev=wan \
-netdev tap,id=lan,ifname=ktrouter,script=no,downscript=no -device virtio-net,netdev=lan \
-vnc 127.0.0.1:10
</nowiki>}}


With this command, you can SSH forward and VNC to port 5910. When installing Debian, you can accept defaults, although I recommend not using a desktop environment on your router.
-- The string to use as a namespace key for the protectionCategories table for each
-- namespace number.
categoryNamespaceKeys = {
[ 2] = 'user',
[ 3] = 'user',
[ 4] = 'project',
[ 6] = 'file',
[ 8] = 'mediawiki',
[ 10] = 'template',
[ 12] = 'project',
[ 14] = 'category',
[100] = 'portal',
[828] = 'module',
},


== Initial configuration ==
protectionCategories = {
['all|all|all|all|all'] = 'Wikipedia fully protected pages',
['all|all|office|all|all'] = 'Wikipedia Office-protected pages',
['all|all|reset|all|all'] = 'Wikipedia Office-protected pages',
['all|all|dmca|all|all'] = 'Wikipedia Office-protected pages',
['all|all|mainpage|all|all'] = 'Wikipedia fully protected main page files',
['all|all|all|extendedconfirmed|all'] = 'Wikipedia extended-confirmed-protected pages',
['all|all|ecp|extendedconfirmed|all'] = 'Wikipedia extended-confirmed-protected pages',
['all|template|all|all|edit'] = 'Wikipedia fully protected templates',
['all|all|all|autoconfirmed|edit'] = 'Wikipedia semi-protected pages',
['indef|all|all|autoconfirmed|edit'] = 'Wikipedia indefinitely semi-protected pages',
['all|all|blp|autoconfirmed|edit'] = 'Wikipedia indefinitely semi-protected biographies of living people',
['temp|all|blp|autoconfirmed|edit'] = 'Wikipedia temporarily semi-protected biographies of living people',
['all|all|dispute|autoconfirmed|edit'] = 'Wikipedia pages semi-protected due to dispute',
['all|all|sock|autoconfirmed|edit'] = 'Wikipedia pages semi-protected from banned users',
['all|all|vandalism|autoconfirmed|edit'] = 'Wikipedia pages semi-protected against vandalism',
['all|category|all|autoconfirmed|edit'] = 'Wikipedia semi-protected categories',
['all|file|all|autoconfirmed|edit'] = 'Wikipedia semi-protected files',
['all|portal|all|autoconfirmed|edit'] = 'Wikipedia semi-protected portals',
['all|project|all|autoconfirmed|edit'] = 'Wikipedia semi-protected project pages',
['all|talk|all|autoconfirmed|edit'] = 'Wikipedia semi-protected talk pages',
['all|template|all|autoconfirmed|edit'] = 'Wikipedia semi-protected templates',
['all|user|all|autoconfirmed|edit'] = 'Wikipedia semi-protected user and user talk pages',
['all|all|all|templateeditor|edit'] = 'Wikipedia template-protected pages other than templates and modules',
['all|template|all|templateeditor|edit'] = 'Wikipedia template-protected templates',
['all|template|all|templateeditor|move'] = 'Wikipedia template-protected templates', -- move-protected templates
['all|all|blp|sysop|edit'] = 'Wikipedia indefinitely protected biographies of living people',
['temp|all|blp|sysop|edit'] = 'Wikipedia temporarily protected biographies of living people',
['all|all|dispute|sysop|edit'] = 'Wikipedia pages protected due to dispute',
['all|all|sock|sysop|edit'] = 'Wikipedia pages protected from banned users',
['all|all|vandalism|sysop|edit'] = 'Wikipedia pages protected against vandalism',
['all|category|all|sysop|edit'] = 'Wikipedia fully protected categories',
['all|file|all|sysop|edit'] = 'Wikipedia fully protected files',
['all|project|all|sysop|edit'] = 'Wikipedia fully protected project pages',
['all|talk|all|sysop|edit'] = 'Wikipedia fully protected talk pages',
['all|template|all|extendedconfirmed|edit'] = 'Wikipedia extended-confirmed-protected templates',
['all|template|all|sysop|edit'] = 'Wikipedia fully protected templates',
['all|user|all|sysop|edit'] = 'Wikipedia fully protected user and user talk pages',
['all|module|all|all|edit'] = 'Wikipedia fully protected modules',
['all|module|all|templateeditor|edit'] = 'Wikipedia template-protected modules',
['all|module|all|extendedconfirmed|edit'] = 'Wikipedia extended-confirmed-protected modules',
['all|module|all|autoconfirmed|edit'] = 'Wikipedia semi-protected modules',
['all|all|all|sysop|move'] = 'Wikipedia move-protected pages',
['indef|all|all|sysop|move'] = 'Wikipedia indefinitely move-protected pages',
['all|all|dispute|sysop|move'] = 'Wikipedia pages move-protected due to dispute',
['all|all|vandalism|sysop|move'] = 'Wikipedia pages move-protected due to vandalism',
['all|portal|all|sysop|move'] = 'Wikipedia move-protected portals',
['all|project|all|sysop|move'] = 'Wikipedia move-protected project pages',
['all|talk|all|sysop|move'] = 'Wikipedia move-protected talk pages',
['all|template|all|sysop|move'] = 'Wikipedia move-protected templates',
['all|user|all|sysop|move'] = 'Wikipedia move-protected user and user talk pages',
['all|all|all|autoconfirmed|autoreview'] = 'Wikipedia pending changes protected pages',
['all|file|all|all|upload'] = 'Wikipedia upload-protected files',
},


Once you boot to a command line, the first thing we'll do is remove the builtin ifupdown network daemon and use systemd-networkd, as it's much easier to manage.
--------------------------------------------------------------------------------
-- Expiry category config
--------------------------------------------------------------------------------


{{code|
-- This table configures the expiry category behaviour for each protection
sudo apt-get purge -y ifupdown
-- action.
sudo ip addr flush
-- * If set to true, setting that action will always categorise the page if
sudo systemctl enable --now systemd-networkd
-- an expiry parameter is not set.
}}
-- * If set to false, setting that action will never categorise the page.
-- * If set to nil, the module will categorise the page if:
-- 1) an expiry parameter is not set, and
-- 2) a reason is provided, and
-- 3) the specified reason is not blacklisted in the reasonsWithoutExpiryCheck
-- table.


We'll then disable systemd-resolved and set up a static DNS config
expiryCheckActions = {
edit = nil,
move = false,
autoreview = true,
upload = false
},


{{code|
reasonsWithoutExpiryCheck = {
sudo systemctl disable --now systemd-resolved
blp = true,
sudo systemctl mask systemd-resolved
template = true,
sudo rm /etc/resolv.conf # remove the symlink
},
sudo tee /etc/resolv.conf <<EOF
nameserver 1.1.1.1
nameserver 1.0.0.1
EOF
}}


Note that the internet will die on the router. This is because systemd-networkd is not configured by default. First, let's figure out our interface names using the command <code>ip a</code>. For me, the WAN interface was <code>ens3</code> and the LAN was <code>ens4</code>. This may vary however if you're using the QEMU command above the WAN interface will appear first in the list. For the rest of the guide I will be assuming the above interface names. Make sure to change them if yours are different.
--------------------------------------------------------------------------------
-- Pagetypes
--------------------------------------------------------------------------------


Let's first configure the WAN interface to use DHCP.
-- This table produces the page types available with the ${PAGETYPE} parameter.
-- Keys are namespace numbers, or the string "default" for the default value.
pagetypes = {
[0] = 'article',
[6] = 'file',
[10] = 'template',
[14] = 'category',
[828] = 'module',
default = 'page'
},


'''/etc/systemd/network/wan.network'''
--------------------------------------------------------------------------------
{{code|<nowiki>
-- Strings marking indefinite protection
[Match]
--------------------------------------------------------------------------------
Name=ens3


[Network]
-- This table contains values passed to the expiry parameter that mean the page
DHCP=ipv4
-- is protected indefinitely.
</nowiki>}}
indefStrings = {
['indef'] = true,
['indefinite'] = true,
['indefinitely'] = true,
['infinite'] = true,
},


Then, we'll assign the LAN interface the static ip of <code>192.168.1.1</code>
--------------------------------------------------------------------------------
-- Group hierarchy
--------------------------------------------------------------------------------


'''/etc/systemd/network/lan.network'''
-- This table maps each group to all groups that have a superset of the original
{{code|<nowiki>
-- group's page editing permissions.
[Match]
hierarchy = {
Name=ens4
sysop = {},
reviewer = {'sysop'},
filemover = {'sysop'},
templateeditor = {'sysop'},
extendedconfirmed = {'sysop'},
autoconfirmed = {'reviewer', 'filemover', 'templateeditor', 'extendedconfirmed'},
user = {'autoconfirmed'},
['*'] = {'user'}
},


[Network]
--------------------------------------------------------------------------------
Address=192.168.1.1/24
-- Wrapper templates and their default arguments
</nowiki>}}
--------------------------------------------------------------------------------


You can then reload the network configuration:
-- This table contains wrapper templates used with the module, and their
-- default arguments. Templates specified in this table should contain the
-- following invocation, and no other template content:
--
-- {{#invoke:Protection banner|main}}
--
-- If other content is desired, it can be added between
-- <noinclude>...</noinclude> tags.
--
-- When a user calls one of these wrapper templates, they will use the
-- default arguments automatically. However, users can override any of the
-- arguments.
wrappers = {
['Template:Pp'] = {},
['Template:Pp-extended'] = {'ecp'},
['Template:Pp-blp'] = {'blp'},
-- we don't need Template:Pp-create
['Template:Pp-dispute'] = {'dispute'},
['Template:Pp-main-page'] = {'mainpage'},
['Template:Pp-move'] = {action = 'move', catonly = 'yes'},
['Template:Pp-move-dispute'] = {'dispute', action = 'move', catonly = 'yes'},
-- we don't need Template:Pp-move-indef
['Template:Pp-move-vandalism'] = {'vandalism', action = 'move', catonly = 'yes'},
['Template:Pp-office'] = {'office'},
['Template:Pp-office-dmca'] = {'dmca'},
['Template:Pp-pc'] = {action = 'autoreview', small = true},
['Template:Pp-pc1'] = {action = 'autoreview', small = true},
['Template:Pp-reset'] = {'reset'},
['Template:Pp-semi-indef'] = {small = true},
['Template:Pp-sock'] = {'sock'},
['Template:Pp-template'] = {'template', small = true},
['Template:Pp-upload'] = {action = 'upload'},
['Template:Pp-usertalk'] = {'usertalk'},
['Template:Pp-vandalism'] = {'vandalism'},
},


{{code|
--------------------------------------------------------------------------------
sudo systemctl restart systemd-networkd
--
}}
-- MESSAGES
--
--------------------------------------------------------------------------------


If all went well, you should be able to access the internet (test with <code>ping google.com</code>)
msg = {


Lastly, set some sysctl values:
--------------------------------------------------------------------------------
-- Intro blurb and intro fragment
--------------------------------------------------------------------------------


{{code|
-- These messages specify what is produced by the ${INTROBLURB} and
<nowiki>sudo tee /etc/sysctl.d/router.conf </nowiki><<<nowiki>EOF
-- ${INTROFRAGMENT} parameters. If the protection is temporary they use the
net.ipv4.conf.default.rp_filter = 2
-- intro-blurb-expiry or intro-fragment-expiry, and if not they use
net.ipv4.conf.all.rp_filter = 2
-- intro-blurb-noexpiry or intro-fragment-noexpiry.
net.ipv4.ip_forward = 1
-- It is possible to use banner parameters in these messages.
EOF</nowiki>
['intro-blurb-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY}.',
}}
['intro-blurb-noexpiry'] = '${PROTECTIONBLURB}.',
['intro-fragment-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY},',
['intro-fragment-noexpiry'] = '${PROTECTIONBLURB}',


== nftables ==
--------------------------------------------------------------------------------
Now, we can set up basic routing. First, install some required packages
-- Tooltip blurb
{{code|
--------------------------------------------------------------------------------
sudo apt-get install -y nftables dnsmasq curl
}}


Now, download our nftables config template. This sets up a simple router. We'll expand on it later.
-- These messages specify what is produced by the ${TOOLTIPBLURB} parameter.
-- If the protection is temporary the tooltip-blurb-expiry message is used, and
-- if not the tooltip-blurb-noexpiry message is used.
-- It is possible to use banner parameters in these messages.
['tooltip-blurb-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY}.',
['tooltip-blurb-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}.',
['tooltip-fragment-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY},',
['tooltip-fragment-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}',


{{code|
--------------------------------------------------------------------------------
sudo curl 'https://computernewb.com/~elijah/nftables.conf' -o /etc/nftables.conf
-- Special explanation blurb
}}
--------------------------------------------------------------------------------


If your WAN and LAN IPs differ from the examples above, edit nftables.conf and change them at the top.
-- An explanation blurb for pages that cannot be unprotected, e.g. for pages
-- in the MediaWiki namespace.
-- It is possible to use banner parameters in this message.
['explanation-blurb-nounprotect'] = 'See the [[Wikipedia:Protection policy|'
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'
.. ' Please discuss any changes on the ${TALKPAGE}; you'
.. ' may ${EDITREQUEST} to ask an'
.. ' [[Wikipedia:Administrators|administrator]] to make an edit if it'
.. ' is [[Help:Minor edit#When to mark an edit as a minor edit'
.. '|uncontroversial]] or supported by [[Wikipedia:Consensus'
.. '|consensus]].',


== dnsmasq ==
--------------------------------------------------------------------------------
Next, copy our DNSMASQ config. DNSMASQ is responsible for DHCP and DNS.
-- Protection log display values
--------------------------------------------------------------------------------


{{code|
-- These messages determine the display values for the protection log link
sudo curl 'https://computernewb.com/~elijah/dnsmasq.conf' -o /etc/dnsmasq.conf
-- or the pending changes log link produced by the ${PROTECTIONLOG} parameter.
}}
-- It is possible to use banner parameters in these messages.
['protection-log-display'] = 'protection log',
['pc-log-display'] = 'pending changes log',


There are a few instances of ens4 you'll need to change if that's not your WAN interface. You can also change the instance of <code>collabnet.local</code> if you want to use a different hostname.
--------------------------------------------------------------------------------
-- Current version display values
--------------------------------------------------------------------------------


There are also some examples of static leases. You can either use the MAC addresses provided in the examples on your VMs, or change them. You need to add a static lease to dnsmasq.conf for each VM you have or it won't be able to connect to the internet.
-- These messages determine the display values for the page history link
-- or the move log link produced by the ${CURRENTVERSION} parameter.
-- It is possible to use banner parameters in these messages.
['current-version-move-display'] = 'current title',
['current-version-edit-display'] = 'current version',


== Start it up ==
--------------------------------------------------------------------------------
-- Talk page
--------------------------------------------------------------------------------


The following commands should get all the router components up and running
-- This message determines the display value of the talk page link produced
{{code|
-- with the ${TALKPAGE} parameter.
sudo systemctl enable --now dnsmasq nftables
-- It is possible to use banner parameters in this message.
sudo nft -f /etc/nftables.conf
['talk-page-link-display'] = 'talk page',
}}


== Testing it all out ==
--------------------------------------------------------------------------------
-- Edit requests
--------------------------------------------------------------------------------


If all went well, you should now have a basic working router. To test this, start up one of your VMs on its TAP. You can do this by removing any <code>-net</code>, <code>-netdev</code>, or <code>-netdev</code> arguments from the QEMU command, and adding the following:
-- This message determines the display value of the edit request link produced
-- with the ${EDITREQUEST} parameter.
-- It is possible to use banner parameters in this message.
['edit-request-display'] = 'submit an edit request',


{{code|<nowiki>
--------------------------------------------------------------------------------
-netdev tap,id=lan,ifname=ktvm1,script=no,downscript=no -device virtio-net,netdev=lan,mac=c0:11:ab:69:42:01
-- Expiry date format
</nowiki>}}
--------------------------------------------------------------------------------


Note that the ifname should correspond to the TAPs added earlier and should be unique per VM. The MAC address should be unique to each VM and MUST be given a static lease in dnsmasq.conf.
-- This is the format for the blurb expiry date. It should be valid input for
-- the first parameter of the #time parser function.
['expiry-date-format'] = 'F j, Y "at" H:i e',


If everything went well, the VM should obtain an IP on boot and be able to access the internet. If not, you can try to troubleshoot or join our discord and create a post in #support, and we can try to help.
--------------------------------------------------------------------------------
-- Tracking categories
--------------------------------------------------------------------------------


= Optional router configuration =
-- These messages determine which tracking categories the module outputs.
['tracking-category-incorrect'] = 'Wikipedia pages with incorrect protection templates',
['tracking-category-template'] = 'Wikipedia template-protected pages other than templates and modules',


The following is not strictly required, however is recommended.
--------------------------------------------------------------------------------
-- Images
--------------------------------------------------------------------------------


== VPN ==
-- These are images that are not defined by their protection action and protection level.
If you don't want users to be able to make traffic from your IP address (you almost definitely do not), you should set up a VPN, for all your users' traffic to be run through. You'll need a VPN that supports wireguard (and additionally you'll want to make sure that this usecase is allowed by their Terms of Service). Here are a few:
['image-filename-indef'] = 'Full-protection-shackle.svg',
['image-filename-default'] = 'Transparent.gif',


* [https://mullvad.net/en Mullvad] ($5 a month)
--------------------------------------------------------------------------------
* [https://protonvpn.com/ ProtonVPN] (Premium plan is $10 a month, however their free plan allows WireGuard making this one preferable if you don't want to spend money)
-- End messages
--------------------------------------------------------------------------------
}


Once you have your VPN picked out, register an account and generate a WireGuard profile. This varies by VPN but its usually in the Downloads section or its own category. It should give you a wireguard configuration either as a downloadable file or to copy and paste.
--------------------------------------------------------------------------------

-- End configuration
Next, install wireguard into your router:
--------------------------------------------------------------------------------
{{code|
sudo apt-get install wireguard-tools
}}

Once this is installed, paste the contents of your WireGuard configuration into <code>/etc/wireguard/wg.conf</code>. An example file looks like this:
{{code|<nowiki>
[Interface]
PrivateKey = ThisIsAnExampleDontActuallyUseThis=
Address = 10.65.2.87/32,fc00:bbbb:bbbb:bb01::2:256/128
DNS = 10.64.0.1
[Peer]
PublicKey = gH/ThisIsAnExampleDontActuallyUseThis=
Endpoint = 1.1.1.1:51820
AllowedIPs = 0.0.0.0/0, ::0/0
</nowiki>}}

With that all set, you can now enable the VPN with the following command:
{{code|
sudo systemctl enable --now wg-quick@wg
}}
If all went well, you should now be connected to the VPN:
{{code|<nowiki>
$ curl ipinfo.io/what-is-my-ip
{
"ip": "143.244.47.86",
"hostname": "unn-143-244-47-86.datapacket.com",
"city": "Weehawken",
"region": "New Jersey",
"country": "US",
"loc": "40.7696,-74.0204",
"org": "AS212238 Datacamp Limited",
"postal": "07086",
"timezone": "America/New_York",
"readme": "https://ipinfo.io/missingauth"
}
}
</nowiki>}}
The last step is to connect the VMs to the VPN. First, get your private IP on the vpn network:
{{code|<nowiki>
$ ip a
(...)
7: wg: </nowiki><<nowiki>POINTOPOINT,NOARP,UP,LOWER_UP</nowiki>><nowiki> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.65.2.87/32 </nowiki><<nowiki>---- THIS RIGHT HERE scope global wg
</nowiki>}}
As we can see, our private IP in this example is <code>10.65.2.87</code>. All we have to do is open nftables.conf and set <code>WAN</code> to <code>wg</code>, and <code>SNAT</code> to this private IP:
{{code|<nowiki>
/etc/nftables.conf
(...)
define SNAT = 10.65.2.87
(...)
define WAN = wg
</nowiki>}}
Reload your nftables configuration:
{{code|
sudo nft -f /etc/nftables.conf
}}
If all went well, your VMs should now be connected to the VPN
== E2Guardian ==
E2Guardian can be used to implement network filtering on your VM. It supports transparent HTTP and HTTPS filtering making it ideal for a setup like CollabVM where the client can't be trusted to always use a proxy or DNS server. Here's how to set it up.
=== Installation ===
First is to install e2guardian. A small issue is that the e2guardian packages have not been updated for Debian 12 and still use OpenSSL 1.1, which was removed in Debian 12. As a workaround we can install the libssl1.1 version from Debian 11. Note that usually installing packages from another Debian version is [https://wiki.debian.org/DontBreakDebian#Don.27t_make_a_FrankenDebian a bad idea], but in this case its fine since the package has no outside dependencies.
{{code|
wget http://ftp.us.debian.org/debian/pool/main/o/openssl/libssl1.1_1.1.1w-0+deb11u1_amd64.deb
sudo apt-get install ./libssl1.1_1.1.1w-0+deb11u1_amd64.deb
}}
You can then install e2guardian:
{{code|
wget https://e2guardian.numsys.eu/v5.5/e2debian_bullseye_V5.5.4r_20231113.deb # IF THERE'S A NEWER VERSION AVAILABLE AT https://e2guardian.numsys.eu/, USE THAT INSTEAD
sudo apt-get install ./e2debian_bullseye_V5.5.4r_20231113.deb
}}
Install some other dependencies:
{{code|
sudo apt-get install git curl tar ftp
}}
=== Configuration ===
Now we need to do some configuration. The e2guardian config directory is not readable by a normal user, so shell in as root.
{{code|
$ sudo -i
# cd /etc/e2guardian
}}
Create a directory for generated certificates, and give e2guardian permissions:
{{code|
mkdir -p private/generatedcerts
chown e2guardian:e2guardian private/generatedcerts
}}
Generate the required keys and CA certificate for SSL MITM:
{{code|
openssl genrsa 4096 > private/ca.key
openssl genrsa 4096 > private/cert.key
openssl req -new -x509 -days 3650 -sha256 -key private/ca.key -out private/ca.pem
}}
The last command will ask for some information, you can leave most of it blank and fill what you want.

Next, some configuration. Open e2guardian.conf in an editor, then find and set the following values:
{{code|<nowiki>
transparenthttpsport = 8443
enablessl = on
sslcertificatepath = '/etc/ssl/certs'
useoriginalip = on
</nowiki>}}
Now open e2guardianf1.conf in an editor, then find and set the following values:
{{code|<nowiki>
## COMMENT OUT THESE LINES:
# storyboard = '/etc/e2guardian/examplef1.story' # comment out for production
# .Define LISTDIR <nowiki><</nowiki>/etc/e2guardian/lists/example.group<nowiki>></nowiki> # comment out for production

sslmitm = on
</nowiki>}}
Copy <code>examplef1.story</code> to <code>group1.story</code>:
{{code|
cp examplef1.story group1.story
}}
=== Filter lists ===
Now you can download the collabnet filter lists:
{{code|
cd lists
git clone --depth 1 https://git.computernewb.com/collabvm/e2guardian.git group1
}}
Download the ut1 and oisd filter lists:
{{code|
cd group1
./getall.sh
}}
==== Updating the filter lists ====
Every once in a while, you'll want to update the filter lists with this command:
{{code|
$ sudo -i
# cd /etc/e2guardian/lists/group1
# git pull
# ./getall.sh
}}
=== Start it up ===
You should now be able to start e2guardian without error using the following command:
{{code|
sudo systemctl enable --now e2guardian
}}
You can confirm that it's working by seeing if it's listening on ports 8080 and 8443:
{{code|<nowiki>
$ sudo ss -tulnp
(...)
tcp LISTEN 0 256 0.0.0.0:8080 0.0.0.0:* users:(("e2guardian",pid=168580,fd=5))
tcp LISTEN 0 256 0.0.0.0:8443 0.0.0.0:* users:(("e2guardian",pid=168580,fd=7))
(...)
</nowiki>}}
=== Routing the VM traffic ===
We can now set a firewall rule to redirect all VM traffic through e2guardian. Open /etc/nftables.conf and uncomment the following lines near the end:
{{code|<nowiki>
iifname $LAN ip daddr != 192.168.1.0/24 tcp dport 80 redirect to :8080
iifname $LAN ip daddr != 192.168.1.0/24 tcp dport 443 redirect to :8443
</nowiki>}}
Reload nftables:
{{code|
sudo nft -f /etc/nftables.conf
}}
=== Test it out ===
First, put /etc/e2guardian/private/ca.pem onto one of your VMs. This is usually done through installing nginx onto the router and copying ca.pem into the webroot. Next, install ca.pem as a Trusted Root Certification Authority (on Windows, you need to change the file extension to .crt for it to be recognized.)

If all went well, you should be able to browse to websites and see that the SSL certificate was signed by your generated CA. Additionally, sites blocked in the filter list should show a block page (try something like anydesk.com)
=== Change the block page ===
You can create a custom block page by editing /usr/share/e2guardian/languages/ukenglish/template.html. For assets like images you'll need to install a webserver (usually nginx) on the router and host them that way.

Revision as of 20:53, 17 December 2023

This guide will walk you through setting up a Virtual Network for your VMs. This will allow you to route your VM traffic behind a VM (strongly recommended for several reasons), filter web traffic, and prevent access to your local network from the VMs.

Prerequisites

  • An hour or two of your time
  • A host running a Linux distribution
  • Basic computer and command line literacy. Nobody is going to hold your hand

Host Preparation

IP Forwarding

First, we're going to enable IP forwarding on your host. This will allow traffic from the VMs to be routed to and from the router. The following command will write this to the sysctl configuration.

echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/router.conf

To apply, either reboot or run the following:

sudo sysctl --system

Bridge configuration

Next, we'll set up a network bridge with multiple TAP interfaces. You can think of a TAP interface as a virtual Ethernet port connected to your VM, and the network bridge as a virtual Ethernet switch connecting them all together. The instructions to do this vary based on your network daemon. For this guide we'll assume (and recommend) you're using systemd-networkd, or netifrc on OpenRC.

We'll also be giving the collabvm group access to the TAPs. If you have yet to create a user for CollabVM, do so now.

Systemd

All network configuration is done in the /etc/systemd/network directory. We'll start by creating the bridge itself. The following simply creates a network bridge named collabnet

/etc/systemd/network/collabnet.netdev

[NetDev]
Name=collabnet
Kind=bridge

Next, we'll create a TAP for our router VM. The following creates a new TAP named ktrouter

/etc/systemd/network/ktrouter.netdev

[NetDev]
Name=ktrouter
Kind=tap

[Tap]
Group=collabvm

Now, you can create a TAP for each VM. The following adds a TAP named ktvm1. To add more, repeat the following, creating files named ktvm2, ktvm3, and so on. Make sure to also change the TAP name inside the file to match this.

/etc/systemd/network/ktvm1.netdev

[NetDev]
Name=ktvm1
Kind=tap

[Tap]
Group=collabvm

Finally, we connect all of our TAPs to the collabnet bridge.

/etc/systemd/network/collabnet.network

[Match]
Name=kt*

[Network]
Bridge=collabnet

Finally, you can either reboot or run the following to reload the network configuration

sudo systemctl restart systemd-networkd

OpenRC

TODO

Setting up the router

Now for the """fun""" part. We're going to set up a router VM, responsible for routing all traffic from the VMs. You'll want to give it two network adapters, one for the WAN (the internet) and another LAN (your VMs, the collabnet bridge).

The router will be running Debian. You can either netboot it from within QEMU (Press Ctrl+B when prompted on boot, enter dhcp && boot http://boot.netboot.xyz) or download an ISO and mount it. Your choice.

Here is an example QEMU start command for the router VM. You'll need to create the disk image and adjust paths.

sudo -u collabvm qemu-system-x86_64 \
-accel kvm \
-cpu host \
-m 2G \
-hda /srv/collabvm/router/router.qcow2 \
-netdev user,id=wan -device virtio-net,netdev=wan \
-netdev tap,id=lan,ifname=ktrouter,script=no,downscript=no -device virtio-net,netdev=lan \
-vnc 127.0.0.1:10

With this command, you can SSH forward and VNC to port 5910. When installing Debian, you can accept defaults, although I recommend not using a desktop environment on your router.

Initial configuration

Once you boot to a command line, the first thing we'll do is remove the builtin ifupdown network daemon and use systemd-networkd, as it's much easier to manage.

sudo apt-get purge -y ifupdown
sudo ip addr flush
sudo systemctl enable --now systemd-networkd

We'll then disable systemd-resolved and set up a static DNS config

sudo systemctl disable --now systemd-resolved
sudo systemctl mask systemd-resolved
sudo rm /etc/resolv.conf # remove the symlink
sudo tee /etc/resolv.conf <<EOF
nameserver 1.1.1.1
nameserver 1.0.0.1
EOF

Note that the internet will die on the router. This is because systemd-networkd is not configured by default. First, let's figure out our interface names using the command ip a. For me, the WAN interface was ens3 and the LAN was ens4. This may vary however if you're using the QEMU command above the WAN interface will appear first in the list. For the rest of the guide I will be assuming the above interface names. Make sure to change them if yours are different.

Let's first configure the WAN interface to use DHCP.

/etc/systemd/network/wan.network

[Match]
Name=ens3

[Network]
DHCP=ipv4

Then, we'll assign the LAN interface the static ip of 192.168.1.1

/etc/systemd/network/lan.network

[Match]
Name=ens4

[Network]
Address=192.168.1.1/24

You can then reload the network configuration:

sudo systemctl restart systemd-networkd

If all went well, you should be able to access the internet (test with ping google.com)

Lastly, set some sysctl values:

sudo tee /etc/sysctl.d/router.conf <<EOF
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.all.rp_filter = 2
net.ipv4.ip_forward = 1
EOF

nftables

Now, we can set up basic routing. First, install some required packages

sudo apt-get install -y nftables dnsmasq curl

Now, download our nftables config template. This sets up a simple router. We'll expand on it later.

sudo curl 'https://computernewb.com/~elijah/nftables.conf' -o /etc/nftables.conf

If your WAN and LAN IPs differ from the examples above, edit nftables.conf and change them at the top.

dnsmasq

Next, copy our DNSMASQ config. DNSMASQ is responsible for DHCP and DNS.

sudo curl 'https://computernewb.com/~elijah/dnsmasq.conf' -o /etc/dnsmasq.conf

There are a few instances of ens4 you'll need to change if that's not your WAN interface. You can also change the instance of collabnet.local if you want to use a different hostname.

There are also some examples of static leases. You can either use the MAC addresses provided in the examples on your VMs, or change them. You need to add a static lease to dnsmasq.conf for each VM you have or it won't be able to connect to the internet.

Start it up

The following commands should get all the router components up and running

sudo systemctl enable --now dnsmasq nftables
sudo nft -f /etc/nftables.conf

Testing it all out

If all went well, you should now have a basic working router. To test this, start up one of your VMs on its TAP. You can do this by removing any -net, -netdev, or -netdev arguments from the QEMU command, and adding the following:

-netdev tap,id=lan,ifname=ktvm1,script=no,downscript=no -device virtio-net,netdev=lan,mac=c0:11:ab:69:42:01

Note that the ifname should correspond to the TAPs added earlier and should be unique per VM. The MAC address should be unique to each VM and MUST be given a static lease in dnsmasq.conf.

If everything went well, the VM should obtain an IP on boot and be able to access the internet. If not, you can try to troubleshoot or join our discord and create a post in #support, and we can try to help.

Optional router configuration

The following is not strictly required, however is recommended.

VPN

If you don't want users to be able to make traffic from your IP address (you almost definitely do not), you should set up a VPN, for all your users' traffic to be run through. You'll need a VPN that supports wireguard (and additionally you'll want to make sure that this usecase is allowed by their Terms of Service). Here are a few:

  • Mullvad ($5 a month)
  • ProtonVPN (Premium plan is $10 a month, however their free plan allows WireGuard making this one preferable if you don't want to spend money)

Once you have your VPN picked out, register an account and generate a WireGuard profile. This varies by VPN but its usually in the Downloads section or its own category. It should give you a wireguard configuration either as a downloadable file or to copy and paste.

Next, install wireguard into your router:

sudo apt-get install wireguard-tools

Once this is installed, paste the contents of your WireGuard configuration into /etc/wireguard/wg.conf. An example file looks like this:

[Interface]
PrivateKey = ThisIsAnExampleDontActuallyUseThis=
Address = 10.65.2.87/32,fc00:bbbb:bbbb:bb01::2:256/128
DNS = 10.64.0.1
[Peer]
PublicKey = gH/ThisIsAnExampleDontActuallyUseThis=
Endpoint = 1.1.1.1:51820
AllowedIPs = 0.0.0.0/0, ::0/0

With that all set, you can now enable the VPN with the following command:

sudo systemctl enable --now wg-quick@wg

If all went well, you should now be connected to the VPN:

$ curl ipinfo.io/what-is-my-ip
{
  "ip": "143.244.47.86",
  "hostname": "unn-143-244-47-86.datapacket.com",
  "city": "Weehawken",
  "region": "New Jersey",
  "country": "US",
  "loc": "40.7696,-74.0204",
  "org": "AS212238 Datacamp Limited",
  "postal": "07086",
  "timezone": "America/New_York",
  "readme": "https://ipinfo.io/missingauth"
}

The last step is to connect the VMs to the VPN. First, get your private IP on the vpn network:

$ ip a
(...)
7: wg: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 10.65.2.87/32 <---- THIS RIGHT HERE scope global wg

As we can see, our private IP in this example is 10.65.2.87. All we have to do is open nftables.conf and set WAN to wg, and SNAT to this private IP:

/etc/nftables.conf
(...)
define SNAT = 10.65.2.87
(...)
define WAN = wg

Reload your nftables configuration:

sudo nft -f /etc/nftables.conf

If all went well, your VMs should now be connected to the VPN

E2Guardian

E2Guardian can be used to implement network filtering on your VM. It supports transparent HTTP and HTTPS filtering making it ideal for a setup like CollabVM where the client can't be trusted to always use a proxy or DNS server. Here's how to set it up.

Installation

First is to install e2guardian. A small issue is that the e2guardian packages have not been updated for Debian 12 and still use OpenSSL 1.1, which was removed in Debian 12. As a workaround we can install the libssl1.1 version from Debian 11. Note that usually installing packages from another Debian version is a bad idea, but in this case its fine since the package has no outside dependencies.

wget http://ftp.us.debian.org/debian/pool/main/o/openssl/libssl1.1_1.1.1w-0+deb11u1_amd64.deb
sudo apt-get install ./libssl1.1_1.1.1w-0+deb11u1_amd64.deb

You can then install e2guardian:

wget https://e2guardian.numsys.eu/v5.5/e2debian_bullseye_V5.5.4r_20231113.deb # IF THERE'S A NEWER VERSION AVAILABLE AT https://e2guardian.numsys.eu/, USE THAT INSTEAD
sudo apt-get install ./e2debian_bullseye_V5.5.4r_20231113.deb

Install some other dependencies:

sudo apt-get install git curl tar ftp

Configuration

Now we need to do some configuration. The e2guardian config directory is not readable by a normal user, so shell in as root.

$ sudo -i
# cd /etc/e2guardian

Create a directory for generated certificates, and give e2guardian permissions:

mkdir -p private/generatedcerts
chown e2guardian:e2guardian private/generatedcerts

Generate the required keys and CA certificate for SSL MITM:

openssl genrsa 4096 > private/ca.key
openssl genrsa 4096 > private/cert.key
openssl req -new -x509 -days 3650 -sha256 -key private/ca.key -out private/ca.pem

The last command will ask for some information, you can leave most of it blank and fill what you want.

Next, some configuration. Open e2guardian.conf in an editor, then find and set the following values:

transparenthttpsport = 8443
enablessl = on
sslcertificatepath = '/etc/ssl/certs'
useoriginalip = on

Now open e2guardianf1.conf in an editor, then find and set the following values:

{{{1}}}

Copy examplef1.story to group1.story:

cp examplef1.story group1.story

Filter lists

Now you can download the collabnet filter lists:

cd lists
git clone --depth 1 https://git.computernewb.com/collabvm/e2guardian.git group1

Download the ut1 and oisd filter lists:

cd group1
./getall.sh

Updating the filter lists

Every once in a while, you'll want to update the filter lists with this command:

$ sudo -i
# cd /etc/e2guardian/lists/group1
# git pull
# ./getall.sh

Start it up

You should now be able to start e2guardian without error using the following command:

sudo systemctl enable --now e2guardian

You can confirm that it's working by seeing if it's listening on ports 8080 and 8443:

$ sudo ss -tulnp
(...)
tcp   LISTEN 0      256                                  0.0.0.0:8080       0.0.0.0:*    users:(("e2guardian",pid=168580,fd=5))
tcp   LISTEN 0      256                                  0.0.0.0:8443       0.0.0.0:*    users:(("e2guardian",pid=168580,fd=7))
(...)

Routing the VM traffic

We can now set a firewall rule to redirect all VM traffic through e2guardian. Open /etc/nftables.conf and uncomment the following lines near the end:

iifname $LAN ip daddr != 192.168.1.0/24 tcp dport 80 redirect to :8080
iifname $LAN ip daddr != 192.168.1.0/24 tcp dport 443 redirect to :8443

Reload nftables:

sudo nft -f /etc/nftables.conf

Test it out

First, put /etc/e2guardian/private/ca.pem onto one of your VMs. This is usually done through installing nginx onto the router and copying ca.pem into the webroot. Next, install ca.pem as a Trusted Root Certification Authority (on Windows, you need to change the file extension to .crt for it to be recognized.)

If all went well, you should be able to browse to websites and see that the SSL certificate was signed by your generated CA. Additionally, sites blocked in the filter list should show a block page (try something like anydesk.com)

Change the block page

You can create a custom block page by editing /usr/share/e2guardian/languages/ukenglish/template.html. For assets like images you'll need to install a webserver (usually nginx) on the router and host them that way.