Difference between revisions of "Catalyst"
(11 intermediate revisions by the same user not shown) | |||
Line 20: | Line 20: | ||
<syntaxhighlight lang=bash> | <syntaxhighlight lang=bash> | ||
perlbrew use myperl | perlbrew use myperl | ||
− | cpanm Catalyst::Runtime Catalyst::Devel | + | cpanm Catalyst::Runtime Catalyst::Devel \ |
+ | Catalyst::TraitFor::Request::ProxyBase \ | ||
+ | Catalyst::Helper::View::TT | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 48: | Line 50: | ||
Here, <code>sub.example.com</code> is a domain that has been set up with FastCGI enabled. | Here, <code>sub.example.com</code> is a domain that has been set up with FastCGI enabled. | ||
+ | |||
+ | Save the following script, modify the variables SITENAME, CATALYST, PERLENV, and PARENT as necessary, and run. This script will: | ||
+ | |||
+ | * Go to the root specified by $PARENT | ||
+ | * Create a new, empty site named $SITENAME at $PARENT/$SITENAME | ||
+ | * Create and chmod $PARENT/$SITENAME/script/dispatch.fcgi to automatically run the generated FastCGI script | ||
+ | ** The reason for this naming is discussed in the Dreamhost wiki. | ||
+ | ** This part is skipped if catalyst.pl has not generated the expected *_fastcgi.pl file. | ||
+ | * Replace all instances of "/usr/bin/env perl" with the path of the specified perlbrew perl | ||
+ | * Run perl Makefile.PL, as suggested by catalyst.pl to "make sure your install is complete" | ||
<syntaxhighlight lang=bash> | <syntaxhighlight lang=bash> | ||
− | perlbrew | + | #!/bin/bash |
− | + | ||
− | + | # Load perlbrew env | |
− | # | + | source ~/perl5/perlbrew/etc/bashrc |
− | + | ||
+ | SITENAME=Foo | ||
+ | CATALYST=catalyst.pl | ||
+ | PERLENV=myperl | ||
+ | PARENT=~/sub.example.com | ||
+ | |||
+ | myperl="`perlbrew use "$PERLENV" && which perl`" | ||
+ | |||
+ | perlbrew use "$PERLENV" && | ||
+ | cd "$PARENT" && | ||
+ | "$CATALYST" "$SITENAME" && | ||
+ | cd "$SITENAME" && | ||
+ | ( | ||
+ | cd script && | ||
+ | for fcs in *_fastcgi.pl; do | ||
+ | cat > dispatch.fcgi <<EOF && | ||
+ | #!/usr/bin/env perl | ||
+ | do '$fcs'; | ||
+ | EOF | ||
+ | chmod 755 dispatch.fcgi | ||
+ | done | ||
+ | ) && | ||
# This part corrects all the "/usr/bin/env perl" shebangs with the perlbrew perl | # This part corrects all the "/usr/bin/env perl" shebangs with the perlbrew perl | ||
− | find -type f -exec perl -p -i -e | + | find -type f -exec perl -p -i -e "s!/usr/bin/env perl!$myperl!g" {} \; && |
# "make sure your install is complete" | # "make sure your install is complete" | ||
perl Makefile.PL | perl Makefile.PL | ||
Line 63: | Line 96: | ||
After this, visiting the page | After this, visiting the page | ||
− | <nowiki>http://sub.example.com/ | + | <nowiki>http://sub.example.com/Foo/script/dispatch.fcgi/</nowiki> |
+ | |||
+ | (note the trailing slash) results in a default welcome screen. (If the result is a 500, something wasn't set up correctly.) | ||
+ | |||
+ | ====Well-hidden dispatch==== | ||
+ | |||
+ | With the following methodology, it is possible to: | ||
+ | |||
+ | * Store the project separately from the document root, preventing access to material in the event of a misconfiguration. | ||
+ | * Prevent, with caveats, the dispatch script from being requested directly by a client. | ||
+ | |||
+ | =====Pre-flight===== | ||
+ | |||
+ | * Ensure FastCGI is enabled on the domain. | ||
+ | * Ensure that <code>dash</code>, or some other minimalist/low-overhead shell, is installed. | ||
+ | ** We will use a wrapper script rather than a symlink to run the project dispatch script. This way, the script is able to locate itself correctly. | ||
+ | * Ensure the CPAN module <code>Catalyst::TraitFor::Request::ProxyBase</code> is installed. | ||
+ | ** This module is used here to alter the base URI so that requests to the dispatch script cannot be made directly by the client. | ||
+ | * If no Catalyst project has been created yet, create one using catalyst.pl (in particular, the copy installed under the desired perl environment). | ||
+ | ** The project should be ''outside'' the document root for the domain. This walkthrough is designed such that none of the actual application server exists anyplace that might be directly accessible to a client. | ||
+ | |||
+ | =====Locations===== | ||
+ | |||
+ | These variables are assumed set. If they are set correctly, the shell snippets in the rest of this walkthrough should be runnable by pasting, without any changes. | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | # The full path to dash (or your choice of minimal sh) | ||
+ | export DASH="$HOME/bin/dash" | ||
+ | # The name of the perlbrew env to use | ||
+ | export MYPERLENV=myperl | ||
+ | # The full path to the domain document root | ||
+ | export DOCUMENT_ROOT="$HOME/sub.example.com" | ||
+ | # The name of the Catalyst project | ||
+ | export PROJECT_NAME=Foo | ||
+ | # The root dir of the Catalyst project | ||
+ | export PROJECT_ROOT="$HOME/catalyst-projects/$PROJECT_NAME" | ||
+ | |||
+ | # The full path to main perl module for the Catalyst project | ||
+ | export PROJECT_MAIN_MODULE="$PROJECT_ROOT/lib/$PROJECT_NAME.pm" | ||
+ | # The full path to the FastCGI script for the Catalyst project | ||
+ | export PROJECT_FASTCGI_SCRIPT="`find "$PROJECT_ROOT/script/" -name '*_fastcgi.pl'`" | ||
+ | |||
+ | # The full path to the perl that should be used | ||
+ | export MYPERL="`perlbrew use "$MYPERLENV" && which perl`" | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | =====Main project module===== | ||
+ | |||
+ | $PROJECT_MAIN_MODULE must be updated to apply the base-altering trait. Do this in one of two ways: | ||
+ | |||
+ | ======Change automatically====== | ||
+ | |||
+ | If the format of the generated module hasn't changed much, you can just run this patch (which is based on <code>diff -U1</code>): | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | patch "$PROJECT_MAIN_MODULE" <<'EOF' | ||
+ | @@ -24,2 +24,4 @@ | ||
+ | |||
+ | +use CatalystX::RoleApplicator; | ||
+ | + | ||
+ | extends 'Catalyst'; | ||
+ | @@ -44,2 +46,7 @@ | ||
+ | |||
+ | +# Apply additional roles to the request class | ||
+ | +__PACKAGE__->apply_request_class_roles(qw/ | ||
+ | + Catalyst::TraitFor::Request::ProxyBase | ||
+ | +/); | ||
+ | + | ||
+ | # Start the application | ||
+ | EOF | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ======Change manually====== | ||
+ | |||
+ | If you can't run the patch as above, you can still simply edit $PROJECT_MAIN_MODULE, making these additions: | ||
+ | |||
+ | <syntaxhighlight lang=perl> | ||
+ | # Insert before the line: extends 'Catalyst'; | ||
+ | |||
+ | use CatalystX::RoleApplicator; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | <syntaxhighlight lang=perl> | ||
+ | # Insert before the line: __PACKAGE__->setup(); | ||
+ | |||
+ | # Apply additional roles to the request class | ||
+ | __PACKAGE__->apply_request_class_roles(qw/ | ||
+ | Catalyst::TraitFor::Request::ProxyBase | ||
+ | /); | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | =====Set the perl path to be used by the scripts===== | ||
+ | |||
+ | If applicable, update the scripts in the project to use the perlbrew perl instead of what env says: | ||
− | + | <syntaxhighlight lang=bash> | |
+ | find "$PROJECT_ROOT" -type f -exec perl -p -i -e "s#/usr/bin/env perl#$MYPERL#g" {} \; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | =====Set up document root===== | ||
+ | |||
+ | We create a dispatch.fcgi script (this is apparently a magical name on Dreamhost that is less subject to aggressive process killing). This script is simply a dash-based wrapper around the project's own FastCGI script. | ||
+ | |||
+ | We also create an .htaccess file. This file is set to do two things: | ||
+ | |||
+ | * It redirects any request whatsoever from the client to be handled by the Catalyst project. | ||
+ | ** <code>dispatch.fcgi</code> is set to be inaccessible except after at least one rewrite. | ||
+ | * It sets the request header <code>X-Request-Base</code> to the root of whatever is found in the <code>SCRIPT_URI</code> env variable. | ||
+ | ** This setting helps <code>Catalyst::TraitFor::Request::ProxyBase</code> change the base URL in such a way that Catalyst won't nullify our efforts to hide the script from the client. (Without it, a client's request to e.g. <code>/dispatch.fcgi/baz</code> is treated the same as <code>/baz</code>, which we don't want.) | ||
+ | |||
+ | With these settings in place, the dispatcher is so well hidden that Catalyst could theoretically be set to handle an action named <code>/dispatch.fcgi</code> without worry of misinterpretation. | ||
+ | |||
+ | These are the only files that should ever appear in the document root. Files to be served in a direct manner can be placed in the project itself (e.g. under its <code>static</code> directory). | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | ( | ||
+ | # Set conservative permissions | ||
+ | umask 0077 | ||
+ | |||
+ | # dispatch.fcgi | ||
+ | cat >"$DOCUMENT_ROOT/dispatch.fcgi" <<EOF | ||
+ | #!$DASH | ||
+ | "$PROJECT_FASTCGI_SCRIPT" "\$@" | ||
+ | EOF | ||
+ | |||
+ | # .htaccess | ||
+ | cat >"$DOCUMENT_ROOT/.htaccess" <<EOF | ||
+ | |||
+ | RewriteEngine on | ||
+ | |||
+ | # Set X-Request-Base to the URI root found in SCRIPT_URI. | ||
+ | SetEnvIf SCRIPT_URI ^([^/]+//[^/]+) fixed_request_base=$1/ | ||
+ | RequestHeader set X-Request-Base %{fixed_request_base}e env=fixed_request_base | ||
+ | |||
+ | RewriteCond %{ENV:REDIRECT_STATUS} ^$ | ||
+ | RewriteRule ^(.*)$ dispatch.fcgi/$1 | ||
+ | |||
+ | EOF | ||
+ | |||
+ | # Set actual permissions | ||
+ | chmod 755 "$DOCUMENT_ROOT/dispatch.fcgi" | ||
+ | chmod 644 "$DOCUMENT_ROOT/.htaccess" | ||
+ | ) | ||
+ | </syntaxhighlight> |
Latest revision as of 11:49, 16 January 2015
Notes on Catalyst, an MVC web framework for perl.
Installation
Other people's instructions
On Dreamhost with perlbrew
Here, I am using perlbrew instead of the typical instructions, and hope to bypass the whole local::lib
thing.
Presume that myperl
is a perlbrew environment that's already been set up and that its executable is at
/home/someuser/perl5/perlbrew/perls/myperl/bin/perl
Install modules with cpanm:
perlbrew use myperl
cpanm Catalyst::Runtime Catalyst::Devel \
Catalyst::TraitFor::Request::ProxyBase \
Catalyst::Helper::View::TT
Version check
Run the following, which always fails:
perl -M"Catalyst 999"
If the failure is about a version number, the install worked (and the error displays the version number). Otherwise, there was a problem.
Link catalyst.pl
Instructions and tutorials refer to the bootstrap script catalyst.pl. This is installed in the bin dir of the perlbrew environment:
/home/someuser/perl5/perlbrew/perls/myperl/bin/catalyst.pl
To make this less of a mouthful, make this accessible from your path. In this example, I'll qualify it with the name of the perlbrew env in case I want to set this up for multiple sites; anytime the doc says "catalyst.pl" I'll substitute "myperl-catalyst.pl".
ln -s /home/someuser/perl5/perlbrew/perls/myperl/bin/catalyst.pl ~/bin/myperl-catalyst.pl
Test on a site
Here, sub.example.com
is a domain that has been set up with FastCGI enabled.
Save the following script, modify the variables SITENAME, CATALYST, PERLENV, and PARENT as necessary, and run. This script will:
- Go to the root specified by $PARENT
- Create a new, empty site named $SITENAME at $PARENT/$SITENAME
- Create and chmod $PARENT/$SITENAME/script/dispatch.fcgi to automatically run the generated FastCGI script
- The reason for this naming is discussed in the Dreamhost wiki.
- This part is skipped if catalyst.pl has not generated the expected *_fastcgi.pl file.
- Replace all instances of "/usr/bin/env perl" with the path of the specified perlbrew perl
- Run perl Makefile.PL, as suggested by catalyst.pl to "make sure your install is complete"
#!/bin/bash
# Load perlbrew env
source ~/perl5/perlbrew/etc/bashrc
SITENAME=Foo
CATALYST=catalyst.pl
PERLENV=myperl
PARENT=~/sub.example.com
myperl="`perlbrew use "$PERLENV" && which perl`"
perlbrew use "$PERLENV" &&
cd "$PARENT" &&
"$CATALYST" "$SITENAME" &&
cd "$SITENAME" &&
(
cd script &&
for fcs in *_fastcgi.pl; do
cat > dispatch.fcgi <<EOF &&
#!/usr/bin/env perl
do '$fcs';
EOF
chmod 755 dispatch.fcgi
done
) &&
# This part corrects all the "/usr/bin/env perl" shebangs with the perlbrew perl
find -type f -exec perl -p -i -e "s!/usr/bin/env perl!$myperl!g" {} \; &&
# "make sure your install is complete"
perl Makefile.PL
After this, visiting the page
http://sub.example.com/Foo/script/dispatch.fcgi/
(note the trailing slash) results in a default welcome screen. (If the result is a 500, something wasn't set up correctly.)
With the following methodology, it is possible to:
- Store the project separately from the document root, preventing access to material in the event of a misconfiguration.
- Prevent, with caveats, the dispatch script from being requested directly by a client.
Pre-flight
- Ensure FastCGI is enabled on the domain.
- Ensure that
dash
, or some other minimalist/low-overhead shell, is installed.- We will use a wrapper script rather than a symlink to run the project dispatch script. This way, the script is able to locate itself correctly.
- Ensure the CPAN module
Catalyst::TraitFor::Request::ProxyBase
is installed.- This module is used here to alter the base URI so that requests to the dispatch script cannot be made directly by the client.
- If no Catalyst project has been created yet, create one using catalyst.pl (in particular, the copy installed under the desired perl environment).
- The project should be outside the document root for the domain. This walkthrough is designed such that none of the actual application server exists anyplace that might be directly accessible to a client.
Locations
These variables are assumed set. If they are set correctly, the shell snippets in the rest of this walkthrough should be runnable by pasting, without any changes.
# The full path to dash (or your choice of minimal sh)
export DASH="$HOME/bin/dash"
# The name of the perlbrew env to use
export MYPERLENV=myperl
# The full path to the domain document root
export DOCUMENT_ROOT="$HOME/sub.example.com"
# The name of the Catalyst project
export PROJECT_NAME=Foo
# The root dir of the Catalyst project
export PROJECT_ROOT="$HOME/catalyst-projects/$PROJECT_NAME"
# The full path to main perl module for the Catalyst project
export PROJECT_MAIN_MODULE="$PROJECT_ROOT/lib/$PROJECT_NAME.pm"
# The full path to the FastCGI script for the Catalyst project
export PROJECT_FASTCGI_SCRIPT="`find "$PROJECT_ROOT/script/" -name '*_fastcgi.pl'`"
# The full path to the perl that should be used
export MYPERL="`perlbrew use "$MYPERLENV" && which perl`"
Main project module
$PROJECT_MAIN_MODULE must be updated to apply the base-altering trait. Do this in one of two ways:
Change automatically
If the format of the generated module hasn't changed much, you can just run this patch (which is based on diff -U1
):
patch "$PROJECT_MAIN_MODULE" <<'EOF'
@@ -24,2 +24,4 @@
+use CatalystX::RoleApplicator;
+
extends 'Catalyst';
@@ -44,2 +46,7 @@
+# Apply additional roles to the request class
+__PACKAGE__->apply_request_class_roles(qw/
+ Catalyst::TraitFor::Request::ProxyBase
+/);
+
# Start the application
EOF
Change manually
If you can't run the patch as above, you can still simply edit $PROJECT_MAIN_MODULE, making these additions:
# Insert before the line: extends 'Catalyst';
use CatalystX::RoleApplicator;
# Insert before the line: __PACKAGE__->setup();
# Apply additional roles to the request class
__PACKAGE__->apply_request_class_roles(qw/
Catalyst::TraitFor::Request::ProxyBase
/);
Set the perl path to be used by the scripts
If applicable, update the scripts in the project to use the perlbrew perl instead of what env says:
find "$PROJECT_ROOT" -type f -exec perl -p -i -e "s#/usr/bin/env perl#$MYPERL#g" {} \;
Set up document root
We create a dispatch.fcgi script (this is apparently a magical name on Dreamhost that is less subject to aggressive process killing). This script is simply a dash-based wrapper around the project's own FastCGI script.
We also create an .htaccess file. This file is set to do two things:
- It redirects any request whatsoever from the client to be handled by the Catalyst project.
dispatch.fcgi
is set to be inaccessible except after at least one rewrite.
- It sets the request header
X-Request-Base
to the root of whatever is found in theSCRIPT_URI
env variable.- This setting helps
Catalyst::TraitFor::Request::ProxyBase
change the base URL in such a way that Catalyst won't nullify our efforts to hide the script from the client. (Without it, a client's request to e.g./dispatch.fcgi/baz
is treated the same as/baz
, which we don't want.)
- This setting helps
With these settings in place, the dispatcher is so well hidden that Catalyst could theoretically be set to handle an action named /dispatch.fcgi
without worry of misinterpretation.
These are the only files that should ever appear in the document root. Files to be served in a direct manner can be placed in the project itself (e.g. under its static
directory).
(
# Set conservative permissions
umask 0077
# dispatch.fcgi
cat >"$DOCUMENT_ROOT/dispatch.fcgi" <<EOF
#!$DASH
"$PROJECT_FASTCGI_SCRIPT" "\$@"
EOF
# .htaccess
cat >"$DOCUMENT_ROOT/.htaccess" <<EOF
RewriteEngine on
# Set X-Request-Base to the URI root found in SCRIPT_URI.
SetEnvIf SCRIPT_URI ^([^/]+//[^/]+) fixed_request_base=$1/
RequestHeader set X-Request-Base %{fixed_request_base}e env=fixed_request_base
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.*)$ dispatch.fcgi/$1
EOF
# Set actual permissions
chmod 755 "$DOCUMENT_ROOT/dispatch.fcgi"
chmod 644 "$DOCUMENT_ROOT/.htaccess"
)