pricecharts

track prices of consumer electronics
Log | Files | Refs | README

commit d0007f09ffdb74a0584d0f3a38c3a983aea1b189
parent 5d6620e98e6ef1fb8cfc76c58b8052dc31d2386a
Author: Kyle Milz <kyle@getaddrinfo.net>
Date:   Sun,  1 Feb 2015 19:10:36 -0700

search: chroot, simplifications

- chroot when started, drop privileges
- move some hard coded variables to the config file
- add verbose debugging print outs
- open all resources after privilege drop

Diffstat:
MPriceChart.pm | 17+++++++++++++----
Msearch.pl | 111+++++++++++++++++++++++++++++++++++++++++++------------------------------------
2 files changed, 74 insertions(+), 54 deletions(-)

diff --git a/PriceChart.pm b/PriceChart.pm @@ -10,12 +10,21 @@ use Exporter; sub get_config { my $parser = Config::Grammar->new({ - _sections => ["general", "vendors"], + _sections => ["general", "http", "vendors"], general => { _vars => [ 'user_agent', 'email', - 'smtp' + 'smtp', + ], + }, + http => { + _vars => [ + "socket_file", + "uid", + "gid", + "chroot", + "db_dir", ], }, vendors => { @@ -41,8 +50,8 @@ sub get_config sub get_dbh { # XXX: needs to be changed in production! - # my $db_dir = "/var/www/db"; - my $db_dir = "./"; + my $db_dir = shift || "/var/www/db"; + # my $db_dir = "./"; mkdir $db_dir; my $dbh = DBI->connect( diff --git a/search.pl b/search.pl @@ -3,84 +3,82 @@ use strict; use warnings; +# because we chroot all dependencies must be explicitly listed +use Config::Grammar; +use DBD::SQLite; +use Encode; use FCGI; use Getopt::Std; use Template; +use Template::Context; +use Template::Filters; +use Template::Iterator; +use Template::Parser; +use Template::Plugins; +use Template::Stash::XS; use PriceChart; use Unix::Syslog qw(:macros :subs); use URI::Escape; my %args; -getopts("d", \%args); +getopts("v", \%args); -my $socket_file = "/var/www/run/search.sock"; -if (-e $socket_file) { - print "Not starting, socket $socket_file exists\n"; - exit; -} - -if (!$args{d} && fork()) { - exit; +# fork into background unless verbose +unless ($args{v}) { + if (fork()) { + exit; + } } -openlog("pricechart_search", LOG_PID, LOG_DAEMON); +my $cfg = get_config(); +my $db_dir = $cfg->{"http"}{"db_dir"}; +my $socket_file = $cfg->{"http"}{"socket_file"}; +my $uid_name = $cfg->{"http"}{"uid"}; +my $gid_name = $cfg->{"http"}{"gid"}; -my $socket = FCGI::OpenSocket($socket_file, 1024); -syslog(LOG_DEBUG, "$socket_file created"); +# this looks up information in /etc +my $uid = getpwnam($uid_name) or die "error: uid does not exist"; +my $gid = getgrnam($gid_name) or die "error: gid does not exist"; +print "info: $uid_name:$gid_name -> $uid:$gid\n" if ($args{v});; -if (my $child_pid = fork()) { - # keep the parent around to clean up the socket after we're done +chroot($cfg->{"http"}{"chroot"}); +chdir("/"); +print "info: chroot done\n" if ($args{v}); - $SIG{INT} = \&parent_sig; - $SIG{TERM} = \&parent_sig; - sub parent_sig - { - my $signal = shift; - kill $signal, $child_pid; - } +# XXX: verify we have indeed dropped privileges? +$< = $> = $uid; +$( = $) = "$gid $gid"; +print "info: uid:gid set to $<:$(\n" if ($args{v}); - # wait for the child to finish - waitpid($child_pid, 0); +openlog("pricechart_fcgi", LOG_PID, LOG_DAEMON); +print "info: open syslog ok\n" if ($args{v}); - FCGI::CloseSocket($socket); - unlink($socket_file) or - syslog(LOG_WARNING, "could not unlink $socket_file: $!"); - closelog(); +if (-e $socket_file) { + my $msg = "socket file $socket_file exists\n"; + print "error: $msg\n" if ($args{v}); + syslog(LOG_ERR, $msg); exit; } -my $uid = getpwnam("www"); -my $gid = getgrnam("daemon"); - -# change ownership on socket otherwise httpd can't talk to us -chown $uid, $gid, $socket_file; +# XXX: i need to be sudo for this to work? after we've dropped privileges? +my $socket = FCGI::OpenSocket($socket_file, 1024); +print "info: open $socket_file ok\n" if ($args{v}); -# drop privileges -$< = $> = $uid; -$( = $) = "$gid $gid"; +my $dbh = get_dbh($db_dir); +print "info: open $db_dir/pricechart.db ok\n" if ($args{v}); my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV, $socket, FCGI::FAIL_ACCEPT_ON_INTR); $SIG{INT} = \&child_sig; $SIG{TERM} = \&child_sig; -sub child_sig -{ - my $signame = shift; - - $request->LastCall(); - syslog(LOG_DEBUG, "caught SIG$signame"); -} my $config = { - # XXX: this needs to be fixed - INCLUDE_PATH => "/home/kyle/src/pricechart/html" + INCLUDE_PATH => "/htdocs/pricechart/templates" }; -my $template = Template->new($config); - -my $dbh = get_dbh(); -syslog(LOG_DEBUG, "database opened"); +my $template = Template->new($config) || die $Template::ERROR . "\n"; +print "info: template config ok\n" if ($args{v}); my $sql = "select part_num, manufacturer, description from products " . "where description like ? or part_num like ? or manufacturer like ?"; @@ -103,7 +101,20 @@ while ($request->Accept() >= 0) { results => $products }; - $template->process("search.html", $vars) || print $template->error(); + $template->process("search.html", $vars) or print $template->error(); } -syslog(LOG_INFO, "shut down"); +syslog(LOG_INFO, "shutdown"); +closelog(); + $dbh->disconnect(); + +FCGI::CloseSocket($socket); +unlink($socket_file) or print "error: could not unlink $socket_file: $!"; + +sub child_sig +{ + my $signame = shift; + + $request->LastCall(); + print "info: caught SIG$signame\n" if ($args{v}); +}