From a84d45498bd861c9225080232948a99c2e317bb8 Mon Sep 17 00:00:00 2001 From: Reinhard Tartler Date: Thu, 2 Apr 2009 13:58:11 +0200 Subject: Import upstream version 0.11~rc3~r2491 --- contrib/bbadmin/accounts.cgi | 580 +++++++++++++++++++++++++++++++++++++++++++ contrib/bbadmin/apache.conf | 14 ++ contrib/bbadmin/bb.css | 70 ++++++ 3 files changed, 664 insertions(+) create mode 100755 contrib/bbadmin/accounts.cgi create mode 100644 contrib/bbadmin/apache.conf create mode 100644 contrib/bbadmin/bb.css (limited to 'contrib/bbadmin') diff --git a/contrib/bbadmin/accounts.cgi b/contrib/bbadmin/accounts.cgi new file mode 100755 index 00000000..d68b82c6 --- /dev/null +++ b/contrib/bbadmin/accounts.cgi @@ -0,0 +1,580 @@ +#!/usr/bin/perl + +# Box Backup web management interface (c) Chris Wilson, 2008 +# +# LICENSE: The Box Backup license applies to this code, with the following +# additional conditions: +# +# If you make any changes to this code, except for changes to existing +# variables in the Configuration section below, you must publish the changes +# under the same license, whether or not you distribute copies of the +# changed version. +# +# If you use any of this code in a derivative work, you must publish the +# source code of the derivative work under the same or compatible license, +# whether or not you distribute copies of the derivative work. +# +# The terms of the Box Backup license may be viewed here: +# https://www.boxbackup.org/license.html +# +# If you require access to the code under a different license, this may +# be negotiated with the copyright holder. + +use strict; +use warnings; + +# Variables which you may need to change to match your installation +# Changes to existing variables are NOT required to be published. + +my $box_dir = "/etc/box"; +my $bbstored_dir = "$box_dir/bbstored"; +my $ca_dir = "/mnt/backup/boxbackup/ca"; + +# You should not need to change these unless you have a non-standard installation + +my $bbstored_conf_file = "$box_dir/bbstored.conf"; +my $bbstoreaccounts = "/usr/local/sbin/bbstoreaccounts"; +my $accounts_db_file = undef; +# my $accounts_db_file = "/etc/box/bbstored/accounts.txt"; +my $raidfile_conf_file = undef; +# my $raidfile_conf_file = "/etc/box/raidfile.conf"; +my $sign_period = '5000'; + +# install Perl module with: +# perl -MCPAN -e 'install Config::Scoped' +# perl -MCPAN -e 'force install P/PT/PTHOMSEN/BoxBackup/BBConfig-0.03.tar.gz' +# perl -MCPAN -e 'install Convert::ASN1' +# download http://search.cpan.org/CPAN/authors/id/L/LE/LEO/Convert-X509-0.1.tar.gz, +# unpack, and move the Convert folder to /usr/lib/perl5/site_perl/X.X.X + +# Check that SSL is being used. +# DO NOT DISABLE THIS unless you really know what you're doing! +die "This script requires an SSL web server" unless $ENV{HTTPS}; + +# Check that the script is protected by basic authentication. +# DO NOT DISABLE THIS unless you really know what you're doing! +die "This script requires HTTP Authentication" unless $ENV{REMOTE_USER}; + +# You should not need to change anything below this line. +# If you do, you must publish your changes to comply with the license. + +use BoxBackup::Config::Accounts; +use BoxBackup::Config::DiskSets; +use CGI::Carp qw(fatalsToBrowser); +use CGI::Pretty; +use Config::Scoped; +use Convert::X509::Request; +use English; +use Fcntl; +use File::Temp; +use URI; +use URI::QueryParam; + +sub check_access($$) +{ + my ($file,$desc) = @_; + unless (-r $file) + { + open FILE, "< $file" and die "should have failed"; + die "Failed to access $desc ($file): $!"; + } +} + +sub check_executable($$) +{ + my ($file,$desc) = @_; + unless (-x $file) + { + open FILE, "< $file" and die "should have failed"; + die "$desc is not executable ($file): $!"; + } +} + + +my $cgi = new CGI; + +if (my $download = $cgi->param("download")) +{ + my ($filename, $acct_no); + + if ($download eq "cert") + { + $acct_no = $cgi->param("account"); + $acct_no =~ tr/0-9a-fA-F//cd; + $filename = "$acct_no-cert.pem"; + } + elsif ($download eq "cacert") + { + $filename = "serverCA.pem"; + } + else + { + die "No such download method $download"; + } + + print $cgi->header(-type => "text/plain", + -"content-disposition" => "attachment; filename=$filename"); + + my $send_file; + + if ($download eq "cert") + { + $send_file = "$ca_dir/clients/$filename"; + } + elsif ($download eq "cacert") + { + $send_file = "$ca_dir/roots/serverCA.pem"; + } + + die "File does not exist: $send_file" + unless -f $send_file; + die "File is not readable by user " . getpwuid($UID) . + ": $send_file" unless -r $send_file; + + open SENDFILE, "< $send_file" or die "Failed to open file " . + "$send_file: $!"; + while (my $line = ) + { + print $line; + } + close SENDFILE; + exit 0; +} + +print $cgi->header(), $cgi->start_html(-title=>"Box Backup Certificates", + -style=>'bb.css'); +print $cgi->h1("Box Backup Certificates"); + +check_access($bbstored_conf_file, "BBStoreD configuration file"); + +my $bbstored_conf = Config::Scoped->new(file => $bbstored_conf_file)->parse(); + +$accounts_db_file ||= $bbstored_conf->{'Server'}{'AccountDatabase'}; +die "Missing AccountDatabase in $bbstored_conf_file" unless $accounts_db_file; +check_access($accounts_db_file, "Accounts Database"); + +$raidfile_conf_file ||= $bbstored_conf->{'Server'}{'RaidFileConf'}; +die "Missing RaidFileConf in $bbstored_conf_file" unless $raidfile_conf_file; +check_access($raidfile_conf_file, "RaidFile configuration file"); + +my $accounts_db = BoxBackup::Config::Accounts->new($accounts_db_file); + +check_executable($bbstoreaccounts, "bbstoreaccounts program"); + +sub error($) +{ + my ($message) = @_; + unless ($message =~ /^p($message); + } + print $cgi->div({-class=>"error"}, $message); + return 0; +} + +sub url +{ + my $cgi = shift @_; + my %params = @_; + my $uri = URI->new($cgi->url(-absolute=>1)); + foreach my $param (keys %params) + { + $uri->query_param($param, $params{$param}); + } + return $uri; +} + +sub create_account($) +{ + my ($cgi) = @_; + + my $upload = $cgi->upload('req'); + unless ($upload) + { + return error("Please attach a certificate request file."); + } + + my $tempfile = File::Temp->new("bbaccount-certreq-XXXXXX.pem"); + my $csr_data = ""; + + while (my $line = <$upload>) + { + print $tempfile $line; + $csr_data .= $line; + } + + my @accounts = $accounts_db->getAccountIDs(); + my $new_acc_no = $cgi->param('account'); + if (not $new_acc_no) + { + return error("Please enter an account number."); + } + + foreach my $account_no (@accounts) + { + if ($account_no == $new_acc_no) + { + return error("The account number $new_acc_no " . + "already exists, please use one which " . + "does not."); + } + } + + my $req = Convert::X509::Request->new($csr_data); + my $cn; + foreach my $part ($req->subject) + { + if ($part =~ /^cn=(.*)/i) + { + $cn = $1; + last; + } + } + + unless ($cn) + { + return error("The certificate request does not include a " . + "common name, which should be BACKUP-$new_acc_no."); + } + + unless ($cn eq "BACKUP-$new_acc_no") + { + return error("The certificate request includes the wrong " . + "common name. Expected " . + "BACKUP-$new_acc_no but found " . + "$cn."); + } + + my $out_cert_dir = "$ca_dir/clients"; + unless (-w $out_cert_dir) + { + return error("Cannot write to certificate directory " . + "$out_cert_dir as user " . + "" . getpwuid($UID) . "."); + } + + my $out_cert = "$out_cert_dir/$new_acc_no-cert.pem"; + if (-f $out_cert and not -w $out_cert) + { + return error("The certificate file $out_cert " . + "exists and is not writable as user " . + "$out_cert_dir as user " . + "" . getpwuid($UID) . "."); + } + + my $client_ca_cert_file = "$ca_dir/roots/clientCA.pem"; + unless (-r $client_ca_cert_file) + { + return error("The client CA certificate file " . + "$client_ca_cert_file " . + "is not readable by user " . + "" . getpwuid($UID) . "."); + } + + my $client_ca_key_file = "$ca_dir/keys/clientRootKey.pem"; + unless (-r $client_ca_key_file) + { + return error("The client CA key file " . + "$client_ca_key_file " . + "is not readable by user " . + "" . getpwuid($UID) . "."); + } + + my $serial_file = "$ca_dir/roots/clientCA.srl"; + unless (-w $serial_file) + { + return error("The certificate serial number file " . + "$serial_file " . + "is not writable by user " . + "" . getpwuid($UID) . "."); + } + + my $outputfile = File::Temp->new("bbaccounts-openssl-output-XXXXXX"); + + if (system("openssl x509 -req -in $tempfile -sha1 " . + "-extensions usr_crt " . + "-CA $client_ca_cert_file " . + "-CAkey $client_ca_key_file " . + "-out $out_cert -days $sign_period " . + ">$outputfile 2>&1") != 0) + { + open ERR, "< $outputfile" or die "$outputfile: $!"; + my $errors = join("", ); + close ERR; + return error($cgi->p("Failed to sign certificate:") . + $cgi->pre($errors)); + } + + my $cert_uri = url($cgi, download => "cert", account => $new_acc_no); + my $ca_uri = url($cgi, download => "cacert"); + + print $cgi->div({-class=>"success"}, + $cgi->p("Account created. Please download the following " . + "files:") . + $cgi->ul( + $cgi->li($cgi->a({href=>$cert_uri}, + "Client Certificate")), + $cgi->li($cgi->a({href=>$ca_uri}, + "CA Certificate")) + ) + ); + + return 1; +} + +if ($cgi->param("create")) +{ + print $cgi->h2("Account Creation"); + create_account($cgi); +} + +print $cgi->h2("Accounts"); +print $cgi->start_table({-border=>0, -class=>"numbers"}); + +print $cgi->Tr( + $cgi->th("Account"), + $cgi->th('Used'), $cgi->th('%'), + $cgi->th('Old files'), $cgi->th('%'), + $cgi->th('Deleted files'), $cgi->th('%'), + $cgi->th('Directories'), $cgi->th('%'), + $cgi->th('Soft limit'), $cgi->th('%'), + $cgi->th('Hard limit'), + $cgi->th('Actions') + ); + +sub human_format($) +{ + my ($kb) = @_; + die "bad format in value: expected number followed by kB, " . + "found '$kb'" unless $kb =~ /^(\d+) (kB)$/; + + my $value = $1; + my $units = $2; + + if ($value > 1024) + { + $value /= 1024; + $units = "MB"; + } + + if ($value > 1024) + { + $value /= 1024; + $units = "GB"; + } + + $value = sprintf("%.1f", $value); + return "$value $units"; +} + +sub bbstoreaccounts_format($) +{ + my ($kb) = @_; + die unless $kb =~ /^(\d+) (kB)$/; + + my $value = $1; + my $units = "K"; + + unless ($value % 1024) + { + $value /= 1024; + $units = "M"; + } + + unless ($value % 1024) + { + $value /= 1024; + $units = "G"; + } + + return "$value$units"; +} + +sub get_account_info($) +{ + my ($account) = @_; + + open BBSA, "$bbstoreaccounts -c $bbstored_conf_file -m info $account |" + or die "Failed to get account info for $account: $!"; + + my $account_info = {}; + + while (my $line = ) + { + unless ($line =~ m/([^:]*): (.*)/) + { + die "Bad format in bbstoreaccounts info output " . + "for account $account: '$line'"; + } + + my $name = $1; + my $value = $2; + + if ($value =~ /(.*), (.*)/) + { + $account_info->{$name} = [$1, $2]; + } + else + { + $account_info->{$name} = $value; + } + } + + return $account_info; +} + +sub format_account_info($) +{ + my ($values) = @_; + my $kb = $values->[0]; + my $pc = $values->[1]; + return $cgi->td(human_format($kb)), $cgi->td($values->[1]); +} + +my %account_numbers; + +my @accounts = $accounts_db->getAccountIDs(); +foreach my $i (@accounts) +{ + die "Duplicate account number $i" if $account_numbers{hex($i)}; + $account_numbers{hex($i)} = 1; + + # Find out what account is on what diskset. + my $disk = $accounts_db->getDisk($i); + + # store limits + my $account_info = get_account_info($i); + + print $cgi->Tr( + $cgi->td($i), + format_account_info($account_info->{'Used'}), + format_account_info($account_info->{'Old files'}), + format_account_info($account_info->{'Deleted files'}), + format_account_info($account_info->{'Directories'}), + format_account_info($account_info->{'Soft limit'}), + $cgi->td(human_format($account_info->{'Hard limit'}[0])), + $cgi->td($cgi->a({-href=>url($cgi, account=>$i)}, + "Edit")) + ); +} + +print $cgi->end_table(); + +my $account_no = $cgi->param("account"); +$account_no =~ tr/0-9a-fA-F//cd; + +if (not $cgi->param("showcreate")) +{ + print $cgi->start_form, + $cgi->submit(-name=>"showcreate", + -value=>"Create Account"), + $cgi->end_form(); +} + +if ($account_no) +{ + print $cgi->h2("Edit Account"); + my $account_info = get_account_info($account_no); + $cgi->param("account", $account_no); + $cgi->param("soft_limit", + bbstoreaccounts_format($account_info->{'Soft limit'}[0])); + $cgi->param("hard_limit", + bbstoreaccounts_format($account_info->{'Hard limit'}[0])); +} +elsif ($cgi->param("showcreate")) +{ + print $cgi->h2("Create Account"); +} + +if ($account_no or $cgi->param("showcreate")) +{ + my $new_account_no = 1; + while ($account_numbers{$new_account_no}) + { + $new_account_no++; + } + + my $disksets_conf = BoxBackup::Config::DiskSets->new($raidfile_conf_file); + my @disk_names = $disksets_conf->getListofDisks(); + my @disk_numbers; + my %disk_labels; + + foreach my $name (@disk_names) + { + my $num = $disksets_conf->getParamVal($name, "SetNumber"); + push @disk_numbers, $num; + $disk_labels{$num} = $name; + } + + print $cgi->start_multipart_form(), + $cgi->start_table(); + + if ($account_no) + { + print $cgi->Tr( + $cgi->th("Account Number"), + $cgi->td($account_no . + $cgi->hidden("account", $account_no)) + ); + } + else + { + print $cgi->Tr( + $cgi->th("Account Number"), + $account_no ? $account_no : + $cgi->td($cgi->textfield(-name => "account", + -default => sprintf("%x", $new_account_no))), + ); + } + + if (not $account_no) + { + print $cgi->Tr( + $cgi->th("Disk Set"), + $cgi->td($cgi->popup_menu(-name => "disk_set", + -values => \@disk_numbers, + -labels => \%disk_labels)) + ); + } + + print $cgi->Tr( + $cgi->th("Soft Limit"), + $cgi->td($cgi->textfield(-name => "soft_limit", + -default => "10G")) + ), + $cgi->Tr( + $cgi->th("Hard Limit"), + $cgi->td($cgi->textfield(-name => "hard_limit", + -default => "20G")) + ), + $cgi->Tr( + $cgi->th("Certificate Request"), + $cgi->td($cgi->filefield({ + -name => "req", + -default => "*.crt" + })) + ); + + if ($account_no) + { + print $cgi->Tr( + $cgi->th(), + $cgi->td($cgi->submit(-name => "update", + -value => "Update Account")) + ); + } + else + { + print $cgi->Tr( + $cgi->th(), + $cgi->td($cgi->submit(-name => "create", + -value => "Create Account")) + ); + } + + print $cgi->end_table(), $cgi->end_form(); +} + +print $cgi->end_html; + +exit 0; diff --git a/contrib/bbadmin/apache.conf b/contrib/bbadmin/apache.conf new file mode 100644 index 00000000..e22668ab --- /dev/null +++ b/contrib/bbadmin/apache.conf @@ -0,0 +1,14 @@ +Alias /bbadmin /var/www/localhost/bbadmin + + + AuthType basic + AuthName "Box Backup Web Management Interface" + AuthUserFile /etc/apache2/bbadmin.cgi.htpasswd + Require valid-user + + Allow from all + + Options ExecCGI + AddHandler cgi-script .cgi + DirectoryIndex accounts.cgi + diff --git a/contrib/bbadmin/bb.css b/contrib/bbadmin/bb.css new file mode 100644 index 00000000..76d48e93 --- /dev/null +++ b/contrib/bbadmin/bb.css @@ -0,0 +1,70 @@ +body +{ + background: #edeef3; +} + +table +{ + border-spacing: 0px; +} + +h1, th +{ + background: #e4e6ed; +} + +h1 +{ + border-top: 1px solid #c4c4d5; + border-bottom: 1px solid #fff; +} + +td, th +{ + border-top: 1px solid #fff; + border-bottom: 1px solid #c4c4d5; + margin: 0px; + padding: 0.2em 0.5em; +} + +th +{ + text-align: left; +} + +table.numbers td +{ + text-align: right; +} + +div.error, div.success +{ + margin: 1em; +} + +div.error>*, div.success>* +{ + margin: 0.5em; +} + +div.error +{ + background: #fdd; + border: 2px solid #c00; +} + +div.success +{ + background: #dfd; + border: 2px solid #0c0; +} + +h2, table, p +{ + margin: 0.5em; +} + +form +{ + margin: 0.5em 0; +} -- cgit v1.2.3