WikiFarmAlternative
Last Tested: This recipe was last tested on PmWiki version: pmwiki-2.3.38
Table of Contents
Question
I like the Wiki Farm Step By Step approach, but is there another way?
Abstract
PmWiki is a versatile tool for managing a web site. One under-recognized feature is Pmwiki's ability to manage several different web sites simultaneously from the same code base. This recipe demonstrates how a web administrator can configure PmWiki to service several sites while retaining the ability to smoothly handle upgrades. When implemented, this recipe provides centralized control of PmWiki administration. The final result is a web server that is easily maintained. This recipe can be applied in either a wiki farm or web site context.
Implementation
This recipe seeks to retain security while providing ease of administration and flexibility. For demonstration purposes, we will assume the administrator wants to manage three web sites: example.org, example.net, and example.com. The first step in this process is to build the framework in a non-web accessible directory. Using these sites as an example, the web directories would be:
/var/httpd/hosts/example.org /var/httpd/hosts/example.net /var/httpd/hosts/example.com
By way of background, I use the methods of this recipe to manage nearly a dozen wiki fields hosted on four co-located web sites. This recipe has allowed me to introduce seamless upgrades to all these sites while reducing the administrative overhead to running a simple shell script. Some of this recipe indicates my own personal ideocyncracies, such as explicitly setting configuration values rather than trust the PmWiki software to do it. This is because I've encountered some odd situations and this approach has worked in each, whereas I've had difficulties making it work following the PmWiki-way. Additionally, hard-coding these values reduces the unlikely possibility of PmWiki being subverted and those derived values being exploited.
Some of the steps below set variables. As my approach is not entirely
consistent with PmWiki, I am trying to rein in this recipe. Therefore, I
will provide a sample Attach:WAF_example-farmconfig.php Δ with those
settings. (Pending release of
implementation.)
$SkinLibDirs
Setting up the Centralized Configuration
So, let's use /var/httpd/hosts
as the non-web directory. For the
remainder of this recipe, we will refer to this as the
{core_directory}
. The directories you will need to create in the
{core_directory}
are those listed in red in the example below.
Note: This recipe can be in any directory that the web service's
user can access (e.g. apached or httpd), not necessarily the
{core_directory}
. We are using this directory to simplify the
documentation.
Core Directory Contents:
/example.org /example.net /example.com
/pmwiki-2.3.38
(Current version of PmWiki you are using)/pmwiki-latest
(symlink to /pmwiki-2.3.38)/pmwiki-share
/pub
/cookbook
/local
The contents of the /pmwiki-share
directory will be the parts of
PmWiki that would be common to all:
The /local
directory allows you to share common
configurations. Common configurations could be centralized here. For
example, If several sites use the same security configuration, then you
could put that configuration in /pmwiki-share/local/security.php
.
The /pub
directory could be put in the /pmwiki-share
directory. This is discussed below in greater detail. Each wiki field must have the pub directory, but if you can use symlinks, then you can maintain that directory in /pmwiki-share
and link it to these fields. This allows you to quickly update certain parts of the /pub
directory once and affect all fields. For example, if you use the same Skin for several sites or fields, then one change here is immediately effective in all those locations. Additionally, some recipes need to put files in /pub
. So, by putting them here, we improve upon the benefit of the central role of /pmwiki-share
.
The /cookbook
directory should be removed from the web directory for security purposes. This reduces the likelihood of a hacker exploiting a weak recipe. While this may not be an issue, security generally should be approached in a "belt and suspenders" manner (i.e., exercise more care than you think you ought). Also, putting that directory in /pmwiki-share
allows the sites and wiki fields to share the same recipes.
I am going to designate a single file as the universal configuration
file. I typically call it stdconfig.php
, but it is similar to
farmconfig.php
. I chose to name it stdconfig.php
to help it
stand out---it could be applied to multiple sites and fields.
As an alternative, /pmwiki-share
could be /usr/share/pmwiki/common
, with /pmwiki-2.3.38
being located in /usr/share/pmwiki/pmwiki-2.3.38
. However, this recipe uses the {core_directory}
in all examples for consistency.
Configuring the Fields
In order to take advantage of this recipe, you will need to slightly amend the structure of the fields which use it. As of August 26, 2006, I am revising this recipe to break the steps into more discrete parts. So, where there is a section dedicated to a part you are interested in, please refer to that part.
In each wiki field, you will want:
index.php /pub -> {core_directory}/pmwiki-share/pub (if you can symlink, otherwise you'll have to copy) /wiki.d (can be removed see below.) /local config.php
The structure above is the minimum of what you should have. The index.php
file is discussed in greater detail below. However, its purpose is to map the field to the centralized farm information. The /pub
directory here is symlinked to the /pmwiki-share/pub
sub-directory. The command that would create this (in Unix) is: "ln -sf {core_directory}/pmwiki-share/pub pub
". If you are in an FTP environment, or otherwise unable to create this directory, you lose the potential of this arrangement. However, to make this work without symlinking, you should copy the contents of the /pmwiki-share/pub
directory.
As suggested above, it is possible to move the wiki.d out of the web-accessible directory. As this directory has wide-open permissions, I believe it is inherently insecure to leave wiki.d
in the web-accessible directory. However, if you would prefer to leave this directory alone, then I left it in this listing.
To further map the field to the central directory, you will want to create an index file. There is an example for both symlinkable and non-symlinkable configurations.
in index.php (with symlinks):
<?php$FarmD
= '{core_directory}pmwiki-latest'; $FarmS = '{core_directory}pmwiki-share/'; $FarmC = '{core_directory}pmwiki-share/cookbook'; $FarmL = '{core_directory}pmwiki-share/local'; include_once("$FarmD
/pmwiki.php");
Because we can use symlinks, the site administrator can have
point to a directory which is actually a symlink to the current PmWiki
installation. How and why this is done is explained below. I have a
variation of this, which coincides with the sans symlinks approach. I
create a $FarmD
version.php
file, and put all the Farm(X) variables:
<?php
include_once('{core_directory}/pmwiki-share/version.php');
include_once("$FarmD
/pmwiki.php");
In this situation, the web administrator cannot use symlinks. To remedy
this, the administrator creates a file that tracks the version
/pmwiki-share/local/version.php
. This allows the web administrator to
upgrade all the sites and farms from one centralized file, rather than
have to edit each field with the new version number. This is further
explained below.
Contents of version.php
<?php
$version = 'pmwiki-2.3.38'; # or $version = 'pmwiki_latest'; with symlinks.
$FarmS = '{core_directory}pmwiki-share';
$FarmC = '$FarmS/cookbook';
$FarmD
= "{core_directory}/$version";
$FarmL = '$FarmS/local';
The actual version number would be whatever version you want to use. I move the variables into this common file so it is easy to change the values without having to hunt throughout a wiki plantation.
$FarmD
so there is no chance for PmWiki to get confused. This does not use the '../' approach as it is more secure to put in the full path. Also, this allows me to copy the index.php in sub-directories of a wiki farm and still find the correct version of pmwiki.php. My experience has been that an explicit definition of this variable remedies various problems I've encountered.
/cookbook
to be in $FarmD
. In those situations, I usually fix that by substituting $FarmD
/cookbook
with $FarmC
without much problem.
To access all recipes in the centralized /cookbook
:
include_once("$FarmC/{recipe.php}");
Note: you do not need to put the cookbook directory into each field! Just make sure the script knows where to look. I assign the location in index.php that works no matter where the field is. This leverages off of the centralization by allowing multiple fields to benefit from one recipe.
To access commonly shared configurations:
include_once("$FarmL/standard_configurations.php");
This is what is added to each wiki field's local/config.php
file to
enable the standard configurations.
Local configurations
You will create the "local/config.php" just like you would for a normal installation. Notice the commonly shared configurations can be site specific. So, if you have two sites, you may have
/pmwiki-share /local common_config-example_org.php common_config-example_com.php
Then, all fields of "example.org" can access the standard configuration thus:
include_once("$FarmL/common_config-example_org.php");
If you want both example.com and example.org to share a common set of core configurations (e.g. common security recipes), put the configurations into a super-common configuration (e.g. "common_security.php") and have this included in the "common_config-{site_name}.php".
Each field will include its own set of Cookbook recipes. I tend to run the same basic half dozen, so they are put into farmconfig.php, and each different recipe is then individually included via local.php.
Moving Cookbook Out of the Web Path
Moving the /cookbook
out of the web path is potentially the easiest
solution that reaps the largest return. However, as recipes vary in how
they are created, the potential for headache remains. First, we will
address the solution itself, then discuss the trade-offs.
Move the Cookbook. The first step is simple: locate the
/cookbook
directory in the /pmwiki-share
directory, either by
the shell mv
command, or FTP. Then, you will need to edit your
configuration files to point to the new location. Thus, when a recipe
says you should refer to it one way, you will need to use another. Below
this paragraph I demonstrate what I mean. By way of shorthand, I created
a $FarmC
variable, which points to
{core_directory}/pmwiki-share/cookbook
.
# Change from this: include_once("cookbook/GreatRecipe.php"); # To this: include_once("$FarmC/GreatRecipe.php"); # or include_once("{core_directory}/pmwiki-share/cookbook/GreatRecipe.php");
Advantage of Moving /Cookbook Out of the Web Path. This solution
offers certain benefits. The greatest benefit is the security
improvement. Pm is aggressive about resolving potential security
holes in the PmWiki core. However, not all recipe authors can be. Thus,
by moving the recipe out of harm's way, the likelihood of a recipe being
exploited is greatly reduced. The second advantage is the availability
of the recipes to all fields. In the WikiFarms configuration recipes in $FarmD
/cookbook are also available to all wikis.
Disadvantage of Moving /Cookbook Out of the Web Path. This solution
offers certain tradeoffs. Recipes are authored by different developers.
As a consequence, each developer has a different approach to how he
refers to files outside of his recipe. Sometimes, the author assumes that the
recipe is in the same directory as his is. Othertimes, the author tries to
recruit $FarmD
to help locate the outside file. By moving /cookbook
out of $FarmD
(and this entire recipe puts $FarmD
away from all local
directories), then those recipes fail. The solution is to either correct
the error yourself, and/or contact the recipe developer and recommend he
adopt an alternative approach (like the one below).
In other words (for non-technical readers) if you use this configuration you will have to edit the code in many recipes to make them work. Peter Bowers June 03, 2015, at 03:04 AM
define('GREATRECIPEPATH', dirname(__File__) . '/'); # or define('COOKBOOKPATH', dirname(__File__) . '/');
Moving wiki.d Out of the Web Path
If you also want to move the wiki.d outside the web area, just remember
to set $WorkDir
in each field's configuration file. To make this work,
create a new directory /pmwiki-share/work_dirs
, and copy the
/wiki.d
from each field. You will want to give every /wiki.d
a
unique name to prevent overwriting files. Here is how this solution
would look:
{core_directory} /pmwiki-share /work /example_org-wiki.d /example_com-wiki.d /example_net-wiki.d
Linking to Non-Web Path Wiki.D Before we change PmWiki's behavior,
I'd like to briefly mention some relevant variables. There are three
variables that are affected:
, $WorkDir
, and
$WikiDir
$LastModFile
. Typically, the first two point to the same place, but
the latter is a PageStore
object, and the former is a string.
$LastModFile
relies on
to know where to track the
modification on a file. A fourth variable $WorkDir
is another
variable we have to monkey with to make this work.
$WikiLibDirs
In the shared configuration, we are going to rebuild the
as shown below. Notice that I explicitly set all
variables rather than leave it to chance. We are also creating a new
per-farm variable called $WikiLibDirs
$WikiD
---this variable is set in the farm's
local configuration file. For example, if your new wiki.d is
example_org-wiki.d
, then you would put in your local file "$WikiD = 'example_org-wiki.d';
"
This becomes {core_directory}/pmwiki-share/work/example_org-wiki.d
.
$WorkDir = "$FarmS/work/$WikiD/"; $LastModFile = "$WorkDir.lastmod"; $WikiDir = new PageStore("$WorkDir\$FullName", 1); $WikiLibDirs = array( &$WikiDir, new PageStore("$FarmM/work/share.d/\$FullName"), new PageStore("$FarmD/wikilib.d/\$FullName") );
Advantage of Moving Wiki.d Out of the Web Path. This solution
offers certain benefits. The chief advantage of moving /wiki.d
out of the
web path is increased security. This is because the /wiki.d
directory is world-writable and world-executable, which gives a skilled
cracker the chance to put and execute code on your server. While PmWiki
seeks to mitigate this, the hazard remains. Moving it out reduces that
hazard further. Additionally, this allows the site administrator to put
all the /wiki.d
directories in a common directory, which helps with
archival and other administrative functions. That is, you can archive
all sites and fields by tarballing a single path.
Disadvantage of Moving Wiki.d Out of the Web Path. This solution
offers certain tradeoffs. The chief disadvantage results from having to
configure PmWiki to behave outside its default behavior. Also, by
grouping all /wiki.d
together, there is an increased chance of
accidently wiping out all of them. However, that is why we backup early
and often.
Moving /pub Out of the Web Path
This solution presumes Pm will be implementing
in a version after 2.1.15. Therefore, exact implementation of $SkinLibDirs
is
subject to change. There are three steps to this solution: 1)
relocating the /pub directory 2) editing the $SkinLibDirs
and
$SkinLibDirs
to instruct PmWiki where to look for skins and 3) editing $SkinDirUrl
.htaccess
to instruct the web server where to look for /pub
. Until
is implemented, this recipe will add an alternate step 4, which is to create a symlink. This step would be superceded by the implementation.
$SkinLibDirs
Relocating /pub directory. The first step is to relocate the /pub
directory. This is perhaps the more straight-forward step, as it only necessitates moving the contents. This is a simple mv pub {core_directory}/pmwiki-share/.
command. In FTP environments, just FTP the contents to the new location and then delete them from the default location.
Configure Skin Location. Not implemented
is an array of directories that Pmwiki uses to search for the skin directory. The 2.1.15 and earlier method checked the script's directory and $SkinLibDirs
$FarmD
. This array allows the site administrator to add a directory. In the
farmconfig.php
, add the following line of code. This will instruct
PmWiki where to find the skin directory, which is essential for PmWiki
to use the skin.
$SkinLibDirs = array('{core_directory}/pmwiki-share/pub');
Configure /pub Location for Web Server. Not implemented
Once /pub
has been removed from the web-path, the web server must be instructed where to find its contents. In Apache, this can be accomplished via editing the .htaccess
file. If you are using Cookbook:CleanUrls, then you may already be familiar with this file. In the .htaccess
file, after all the other RewriteRules, add the code line below. This line tells Apache to look in {core_directory}/pmwiki-share/pub
to satisfy any reference to /pub
. This means that a browser request for a file, say /pub/skins/pmwiki/screen.css
will cause Apache to deliver the file located at {core_directory}/pmwiki-share/pub/skins/pmwiki/screen.css
. I consider this a neat piece of voodoo. By the time you have reached this step, you should be able to view the Pmwiki site with your chosen skin without needing to have /pub
in every field.
RewriteRule ^/pub/?(.*) {core_directory}/pmwiki-share/pub/$1 [L,qsa]
Alternate: Symlink to /pub. If you are unable to implement the
and $SkinLibDirs
.htaccess
methods, described above, then the alternative is to create a symlink to /pmwiki-share/pub
to the wiki field. To do this, ensure that your current working directory is where you want the symlink (pwd
). Then, execute this shell command: ln -sf {core_directory}/pmwiki-share/pub pub
. You now have a linked /pub
directory.
Advantage of Moving /pub Out of the Web Path. Of course, with
every solution there needs to be a reason. The advantage to moving this directory out of the web path is largely administrative. When you manage several wiki fields that share the same skins, javascripts or CSS files, then you may prefer to update that file one time and have that update effective on all fields simultaneously. This solution provides that advantage. Additionally, not having multiple copies of the /pub
directory reduces the amount of disk space used by your implementation. While the amount of space used is probably trivial for many administrators, for some administrators disk space remains a concern.
Disadvantage of Moving /pub Out of the Web Path. All solutions offer some trade off. The disadvantage to this solution is that there is some overhead involved by editing files and some potential debuggery of the .htaccess
or
code. Additionally, some web servers prohibit symlinks or path redirection. My opinion on restrictive web hosts is to consider an alternative, as web hosting is a commodity market.
$SkinLibDirs
Advantage: Upgrades
The real advantage with this recipe lies in the ability to upgrade. (Upgrades are even simpler using the standardized configuration found in WikiFarms - you will do the first 2 steps below and then you will be done.) When you are in a symlink situation, here is what you need to do:
- Download newest version of PmWiki
- Untar the version in the {core_directory}
- Remove the
/pmwiki-latest
symlink - Add symlink from
/pmwiki-2.3.38
to/pmwiki-latest
wget -c http://www.pmwiki.org/pub/pmwiki/pmwiki-latest.tgz
- tar xvfz pmwiki-latest.tgz
rm pmwiki-latest ln -sf pmwiki-2.3.38 pmwiki-latest
Without symlinking, you still follow the first two steps. Instead of
steps three and four, you would instead edit
/pmwiki-share/local/version.php
and edit in the new version number.
In both cases, if the new installation does not work, just reverse the version assignment (i.e, revert the symlink change, or edit back the older, stable version number).
Releases
August 26, 2006. BenWilson August 26, 2006, at 03:33 PM
August 13, 2006. Added solution for moving wiki.d directories out of the web-accessible directory. BenWilson August 13, 2006, at 03:33 PM
July 2, 2006. I am trying a new rewrite to help clarify some confusion raised. BenWilson July 02, 2006, at 03:33 PM
February 10, 2006. I have used this to host multiple sites on the same server-where each site has its own Farm. I am making this available to other administrators. BenWilson February 10, 2006, at 03:33 PM
Comments
Comments to this recipe are available on its discussion page. Past discussions are also archived there.
See Also
Contributors
User notes? : If you use, used or reviewed this recipe, you can add your name. These statistics appear in the Cookbook listings and will help newcomers browsing through the wiki.