From 75ecd4d7f4ef9934a2fb6fb08bee47a7567e945e Mon Sep 17 00:00:00 2001 From: JT Smith Date: Wed, 8 Feb 2006 20:18:00 +0000 Subject: [PATCH] added data serialization, encryption, and ip subnet checking to spectre --- docs/gotcha.txt | 2 ++ etc/WebGUI.conf.original | 14 +++++++++- etc/spectre.conf.original | 6 ++++ lib/Spectre/Admin.pm | 2 +- lib/Spectre/Workflow.pm | 23 ++++++++++++++-- lib/WebGUI/Operation.pm | 1 + lib/WebGUI/Operation/Spectre.pm | 49 +++++++++++++++++++++++++++++++++ lib/WebGUI/Utility.pm | 46 ++++++++++++++++++++++++++++++- sbin/testEnvironment.pl | 2 ++ 9 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 lib/WebGUI/Operation/Spectre.pm diff --git a/docs/gotcha.txt b/docs/gotcha.txt index 6358b6f01..2c4a0dcc5 100644 --- a/docs/gotcha.txt +++ b/docs/gotcha.txt @@ -35,6 +35,8 @@ save you many hours of grief. POE POE::Component::IKC::Server POE::Component::Client::UserAgent + Crypt::Blowfish + Net::Subnets * The upgrade script is going to convert your WebGUI config files from the current PlainConfig format to the new JSON format. diff --git a/etc/WebGUI.conf.original b/etc/WebGUI.conf.original index cc83b5471..0df0d8c03 100644 --- a/etc/WebGUI.conf.original +++ b/etc/WebGUI.conf.original @@ -320,7 +320,19 @@ # the LDAP server. Note that this will only happen for users # that have LDAP as their authentication. -"SyncProfilesToLDAP_hour" : 2 +"SyncProfilesToLDAP_hour" : 2, + +# Define the subnets that WebGUI should expect Spectre communication +# to come from. All other subnets will be ignored. The subnet +# should be listed in CIDR notation. + +"spectreSubnets" : [ "127.0.0.1/32" ], + +# Define the key that will be used to encrypt communcation +# between Spectre and WebGUI. Note that this must match the +# cryptoKey in the Spectre config file. + +"spectreCryptoKey" : "123qwe" } diff --git a/etc/spectre.conf.original b/etc/spectre.conf.original index db9b11bf1..760bcad65 100644 --- a/etc/spectre.conf.original +++ b/etc/spectre.conf.original @@ -2,6 +2,12 @@ { +# Define a key that will be used between Spectre and WebGUI to encrypt +# communication. Note that this key must match the "spectreCryptoKey" +# directive in your WebGUI config files. + +"cryptoKey" : "123qwe", + # Define a port for Spectre to run on between 1024 and 65000. "port" : 32133, diff --git a/lib/Spectre/Admin.pm b/lib/Spectre/Admin.pm index 01f1c6304..a55bb2cf7 100644 --- a/lib/Spectre/Admin.pm +++ b/lib/Spectre/Admin.pm @@ -48,7 +48,7 @@ Gracefully shuts down the admin interface. sub _stop { my ($kernel, $self) = @_[KERNEL, OBJECT]; - print "Stopping WebGUI Admin..."; + print "Stopping Spectre..."; undef $self; $kernel->stop; print "OK\n"; diff --git a/lib/Spectre/Workflow.pm b/lib/Spectre/Workflow.pm index 79ba94a30..6c180ca68 100644 --- a/lib/Spectre/Workflow.pm +++ b/lib/Spectre/Workflow.pm @@ -210,7 +210,17 @@ sub runWorker { my $url = $job->{sitename}.'/'.$job->{gateway}; $url =~ s/\/\//\//g; $url = "http://".$url."?op=spectre;instanceId=".$job->{instanceId}; - $kernel->post( useragent => 'request', { request => HTTP::Request->new(GET => $url), response => $session->postback('workerResponse') }); + my $payload = { + 'do'=>'runWorkflow', + instanceId=>$job->{instanceId}, + }; + my $cipher = Crypt::Blowfish->new($self->{_config}->get("cryptoKey")); + my $request = HTTP::Request->new(POST => $url, Content => { op=>"spectre", payload=>$cipher->encrypt(objToJson($payload)) }); + my $cookie = $self->{_cookies}{$job->{sitename}}; + $request->header("Cookie","wgSession=".$cookie) if (defined $cookie); + $request->header("User-Agent","Spectre"); + $request->header("X-JobId",$job->{instanceId}); + $kernel->post( useragent => 'request', { request => $request, response => $session->postback('workerResponse') }); } #------------------------------------------------------------------- @@ -247,9 +257,16 @@ This method is called when the response from the runWorker() method is received. sub workerResponse { my $self = $_[OBJECT]; my ($request, $response, $entry) = @{$_[ARG1]}; - my $jobId = ""; # got to figure out how to get this from the request, cuz the response may die + my $jobId = $request->header("X-JobId"); # got to figure out how to get this from the request, cuz the response may die if ($response->is_success) { - my $state = ""; # get the response + if ($response->header("Cookie") ne "") { + my $cookie = $response->header("Set-Cookie"); + $cookie =~ s/wgSession=([a-zA-Z0-9\_\-]{22})/$1/; + $self->{_cookies}{$self->{_jobs}{$jobId}{sitename}} = $cookie; + } + my $cipher = Crypt::Blowfish->new($self->{_config}->get("cryptoKey")); + my $payload = jsonToObj($cipher->decrypt($response->content)); + my $state = $payload->{state}; if ($state eq "continue") { $self->suspendJob($jobId); } elsif ($state eq "done") { diff --git a/lib/WebGUI/Operation.pm b/lib/WebGUI/Operation.pm index e05c40aeb..074255061 100644 --- a/lib/WebGUI/Operation.pm +++ b/lib/WebGUI/Operation.pm @@ -77,6 +77,7 @@ Returns a hash reference containing operation and package names. sub getOperations { return { + 'spectre' => 'WebGUI::Operation::Spectre', 'adminConsole' => 'WebGUI::Operation::Admin', 'switchOffAdmin' => 'WebGUI::Operation::Admin', 'switchOnAdmin' => 'WebGUI::Operation::Admin', diff --git a/lib/WebGUI/Operation/Spectre.pm b/lib/WebGUI/Operation/Spectre.pm new file mode 100644 index 000000000..6d5fb6001 --- /dev/null +++ b/lib/WebGUI/Operation/Spectre.pm @@ -0,0 +1,49 @@ +package WebGUI::Operation::Spectre; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2006 Plain Black Corporation. +#------------------------------------------------------------------- +# Please read the legal notices (docs/legal.txt) and the license +# (docs/license.txt) that came with this distribution before using +# this software. +#------------------------------------------------------------------- +# http://www.plainblack.com info@plainblack.com +#------------------------------------------------------------------- + +use strict; +use Crypt::Blowfish; +use JSON; + +=head1 NAME + +Package WebGUI::Operation::Spectre + +=head1 DESCRIPTION + +Operation handler for Spectre functions. + +=cut + +#------------------------------------------------------------------- + +=head2 www_spectre ( ) + +Checks to ensure the requestor is who we think it is, and then executes a spectre function, and returns a data packet. + +=cut + +sub www_spectre { + my $session = shift; + return $session->privilege->insufficient unless(isInSubnet($session->env->get("REMOTE_ADDR"), $session->config->get("spectreSubnets")); + my $cipher = Crypt::Blowfish->new($session->config->get("spectreCryptoKey")); + my $payload = jsonToObj($cipher->decrypt($session->form->get("payload"))); + my $out = {}; + if ($payload->{do} eq "runWorkflow") { + # do workflow stuff + } + return $cipher->encrypt(objToJson($out)); +} + + + +1; diff --git a/lib/WebGUI/Utility.pm b/lib/WebGUI/Utility.pm index 699ec71fe..25158e810 100644 --- a/lib/WebGUI/Utility.pm +++ b/lib/WebGUI/Utility.pm @@ -19,9 +19,10 @@ package WebGUI::Utility; use Exporter; use strict; use Tie::IxHash; +use Net::Subnets; our @ISA = qw(Exporter); -our @EXPORT = qw(&isBetween &makeTabSafe &makeArrayTabSafe &randomizeHash &commify &randomizeArray +our @EXPORT = qw(&isBetween &makeTabSafe &makeArrayTabSafe &randomizeHash &commify &randomizeArray &isInSubnet &formatBytes &sortHashDescending &sortHash &isIn &makeCommaSafe &makeArrayCommaSafe &randint &round ); @@ -40,6 +41,7 @@ This package provides miscellaneous but useful utilities to the WebGUI programme $string = commify($integer); $size = formatBytes($integer); $boolean = isIn($value, @array); + $boolean = isInSubnet($ip, \@subnets); makeArrayCommaSafe(\@array); makeArrayTabSafe(\@array); $string = makeCommaSafe($string); @@ -155,6 +157,48 @@ sub isIn { return 0; } + +#------------------------------------------------------------------- + +=head2 isInSubnet ( ipAddress, subnets ) + +Verifies whether an IP address is in a given subnet. Returns a 1 if it is, undef if there's a formatting error, or 0 if the IP is not in the list of subnets. + +=head3 ipAddress + +A scalar containing an IP address. + +=head3 subnets + +An array reference containing subnets in CIDR format. Example: 127.0.0.1/32 + +=cut + +sub isInSubnet { + my $ip = shift; + my $subnets = shift; + # some validation + for my $cidr ( @{ $subnets } ) { + my @parts = $cidr =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/; + unless ( 5 == @parts ) { # cidr has 5 parts + return undef; + } + unless ( 4 == grep { $_ <= 255 } @parts[0..3] ) { # each octet needs to be between 0 and 255 + return undef; + } + unless ( $parts[4] <= 32 ) { # the subnet needs to be less than or equal to 32, as 32 represents only 1 ip address + return undef; + } + } + my $net = Net::Subnets->new; + $net->subnets($subnets); + if ($net->check($ip)) { + return 1; + } else { + return 0; + } +} + #------------------------------------------------------------------- =head2 makeArrayCommaSafe ( array ) diff --git a/sbin/testEnvironment.pl b/sbin/testEnvironment.pl index fe9a45299..987537906 100644 --- a/sbin/testEnvironment.pl +++ b/sbin/testEnvironment.pl @@ -78,6 +78,8 @@ checkModule("Template",2.14,2); checkModule("Parse::PlainConfig",1.1); checkModule("XML::RSSLite",0.11); checkModule("JSON",0.991); +checkModule("Net::Subnets",0.21); +checkModule("Crypt::Blowfish",2.10); checkModule("Finance::Quote",1.08); checkModule("POE",0.3202); checkModule("POE::Component::IKC::Server",0.18);