Initial commit from dead svn repository
+#!/usr/bin/perl
+use strict;
+
+use AwesomeGrid;
+use AwesomeGrid::Config;
+use AwesomeGrid::User;
+use AwesomeGrid::Keyring;
+
+my @peers = AwesomeGrid::Config::trusted();
+my $admins = AwesomeGrid::Config::admins();
+
+my $home = AwesomeGrid::Config::hostname()
+ || die "invalid hostname";
+
+my @exports = AwesomeGrid::Config::exports();
+foreach my $u (@exports) {
+ my $agu = AwesomeGrid::User->load($u);
+ if ($agu->{home} ne $home) {
+ print STDERR "WARNING: Exporting $u for non-home system $agu->{home}\n";
+ }
+}
+
+END {system('stty echo')}
+system('stty -echo');
+print STDERR "GPG Passphrase: ";
+my $passphrase = <STDIN>;
+system('stty echo');
+print "\n";
+
+my $pwd = `pwd`; chomp $pwd;
+chdir "$AwesomeGrid::confdir/users";
+
+my $exports = join(' ', @exports);
+system(qq!tar cfz "$pwd/export.tgz" $exports!);
+foreach my $peer (@peers) {
+ my $admin_id = $admins->{$peer};
+ print "Exporting for $peer ($admin_id)\n";
+ AwesomeGrid::Keyring::encrypt($admin_id,"$pwd/export.tgz","$pwd/$home-export-$peer.tgz.gpg",$passphrase);
+}
+unlink "$pwd/export.tgz";
+#!/usr/bin/perl
+use strict;
+
+use AwesomeGrid;
+use AwesomeGrid::User;
+
+my $host = shift;
+unless ($host) {
+ print "No host specified. Use the -all flag to flush all users.\n";
+ exit 1;
+}
+
+opendir USERS, "$AwesomeGrid::confdir/users";
+my @users = grep {/^[^.]/} readdir USERS;
+closedir USERS;
+
+foreach my $u (@users) {
+ my $agu = AwesomeGrid::User->load($u);
+ if ($host eq '-all' || $agu->{home} eq $host) {
+ print "Deleting $agu->{username}\n";
+ AwesomeGrid::User::delete($u);
+ }
+}
+#!/usr/bin/perl
+use strict;
+
+use AwesomeGrid;
+use AwesomeGrid::Config;
+use AwesomeGrid::User;
+use AwesomeGrid::Keyring;
+
+use File::Copy;
+
+my @files = @ARGV;
+my $pwd = `pwd`; chomp $pwd;
+mkdir "$AwesomeGrid::confdir/users.new";
+chdir "$AwesomeGrid::confdir/users.new";
+
+my @trusted = AwesomeGrid::Config::trusted();
+
+END {system('stty echo')}
+system('stty -echo');
+print STDERR "GPG Passphrase: ";
+my $passphrase = <STDIN>;
+system('stty echo');
+print "\n";
+
+for my $f (@files) {
+ AwesomeGrid::Keyring::gpgpass($passphrase, qq!--decrypt "$pwd/$f" | tar xfz -!);
+
+ opendir USERS, ".";
+ my @users = grep {/^[^.]/} readdir USERS;
+ closedir USERS;
+
+ for my $u (@users) {
+ my $agu = AwesomeGrid::User->loadfrom($u);
+ unless ($agu) {
+ print "Unusable user info: $u\n";
+ unlink $u;
+ next;
+ }
+ unless (grep { $_ eq $agu->{home} } @trusted) {
+ print "Refusing to import ",$agu->{username}," from untrusted system ",$agu->{home},"\n";
+ unlink $u;
+ next;
+ }
+ if (AwesomeGrid::User::exists($u)) {
+ print "$u exists. Overwrite? [y/N] ";
+ my $ans = <STDIN>; chomp $ans;
+ if ($ans !~ /^[Yy]$/) {
+ unlink $u;
+ next;
+ }
+ }
+ move $u, "$AwesomeGrid::confdir/users/"
+ || die "Could not write to $AwesomeGrid::confdir/users/";
+ print "Imported $u\n";
+ }
+}
+
+chdir $pwd;
+rmdir "$AwesomeGrid::confdir/users.new";
+#!/usr/bin/perl
+use strict;
+
+use AwesomeGrid;
+use AwesomeGrid::Config;
+use AwesomeGrid::Keyring;
+
+my $cmd = shift;
+
+if ($cmd eq 'initialize') {
+ AwesomeGrid::Keyring::initialize();
+} elsif ($cmd eq 'import') {
+ AwesomeGrid::Keyring::import_key(shift);
+} elsif ($cmd eq 'export') {
+ my $config = AwesomeGrid::Config::config();
+ print "gpg-id: ",$config->{'gpg-id'},"\n";
+ AwesomeGrid::Keyring::export_key($config->{'gpg-id'});
+} elsif ($cmd eq 'list') {
+ AwesomeGrid::Keyring::list();
+} elsif ($cmd eq 'gpg') {
+ AwesomeGrid::Keyring::gpg(@ARGV);
+}
+#!/usr/bin/perl
+
+use AwesomeGrid::User;
+
+my $hostname = shift;
+opendir USERS, "$AwesomeGrid::confdir/users";
+my @users = grep {/^[^.]/} readdir USERS;
+closedir USERS;
+printf "%-10s%-20s\n","username","home system";
+printf '-' x 30;
+printf "\n";
+foreach (@users) {
+ my $agu = AwesomeGrid::User->load($_);
+ if ($hostname) {
+ next unless $agu->{home} eq $hostname;
+ }
+ printf "%-10s%-20s\n", $agu->{username}, $agu->{home};
+}
+#!/usr/bin/perl
+
+use AwesomeGrid::User;
+
+use Crypt::PasswdMD5; # Linux, FreeBSD
+use Digest::SHA1 qw/sha1_hex/; # OS X 10.4
+
+sub getsalt {
+ my $length = shift;
+ open RAND, "/dev/urandom"; # For production, this should be
+ # /dev/random
+ my $data;
+ read(RAND, $data, $length);
+ close RAND;
+ return $data;
+}
+
+sub unix_MD5 {
+ my $passwd = shift;
+ my $salt = unpack("h8",getsalt(8));
+ return unix_md5_crypt($passwd, $salt);
+}
+
+sub apache_MD5 {
+ my $passwd = shift;
+ my $salt = unpack("h8",getsalt(8));
+ return apache_md5_crypt($passwd, $salt);
+}
+
+sub OSX_SHA1 {
+ my $passwd = shift;
+ my $v = getsalt(4);
+ my $hash = sha1_hex("$v$passwd");
+ my $salt = unpack("H8",$v);
+ return uc("$salt$hash");
+}
+
+my $user = shift;
+if (!$user) {
+ print "usage: ag-passwd user\n";
+ exit 1;
+}
+
+if (!AwesomeGrid::User::exists($user)) {
+ print "User does not exist\n";
+ exit 1;
+}
+
+my $aguser = AwesomeGrid::User->load($user) || exit 1;
+
+# Restore sanity in case of emergency
+END { system("stty echo") }
+
+system("stty -echo");
+print STDERR "Password: ";
+my $passwd1 = <STDIN>;
+chomp $passwd1;
+print STDERR "\n";
+
+print STDERR "Repeat password: ";
+my $passwd2 = <STDIN>;
+chomp $passwd2;
+print STDERR "\n";
+
+system("stty echo");
+
+if ($passwd1 != $passwd1) {
+ print "Passwords don't match.\n";
+ exit 1;
+}
+
+$aguser->{'passwd-unix-md5'} = unix_MD5($passwd1);
+$aguser->{'passwd-apache-md5'} = apache_MD5($passwd1);
+$aguser->{'passwd-osx-sha1'} = OSX_SHA1($passwd1);
+
+$aguser->save();
+print "Password updated.\n";
+#!/usr/bin/perl
+use strict;
+
+use AwesomeGrid;
+use AwesomeGrid::Config;
+use AwesomeGrid::User;
+
+my $etc = '/etc'; # In production, this should be '/etc'
+
+my $config = AwesomeGrid::Config::config();
+
+# Get a list of awesomegrid users
+opendir USERS, "$AwesomeGrid::confdir/users";
+my @agusers = grep {/^[^.]/} readdir USERS;
+close USERS;
+
+# Get a list of system users
+my @sysusers;
+my @allsysusers;
+
+open PASSWD, "$etc/passwd";
+while (<PASSWD>) {
+ my ($username, undef, $uid, $gid, $gecos, $home, $shell) = split(/:/);
+ push(@allsysusers, $username);
+ next unless $gecos =~ /^\[AG\]/; # Skip non-AG-managed users
+ push(@sysusers, $username);
+}
+close PASSWD;
+
+# Check to see what there is in /etc/passwd that should be removed.
+my @deletes;
+for my $sysuser (@sysusers) {
+ unless (grep { $_ eq $sysuser } @agusers) {
+ push(@deletes, $sysuser);
+ }
+}
+
+# Check to see which users are going to be added
+my @adds;
+for my $aguser (@agusers) {
+ unless (grep { $_ eq $aguser } @allsysusers) {
+ push(@adds, $aguser);
+ }
+}
+
+# Check to see which users should be updated
+my @updates;
+for my $user (@allsysusers) {
+ if (grep { $_ eq $user } @agusers) {
+ push(@updates, $user);
+ }
+}
+
+unless (@adds or @deletes or @updates) {
+ print "Nothing to do.\n";
+ exit 0;
+}
+
+print "Adding: ",join(' ',@adds),"\n" if @adds;
+print "Deleting: ",join(' ',@deletes),"\n" if @deletes;
+print "Updating: ",join(' ',@updates),"\n" if @updates;
+
+print "Proceed? [y/N] ";
+my $ans = <STDIN>;
+unless ($ans =~ /^[Yy]$/) {
+ exit(0);
+}
+
+# Before proceeding, make a backup copy of /etc/passwd and /etc/shadow
+system("cp $etc/passwd $etc/passwd.ag_backup");
+system("cp $etc/shadow $etc/shadow.ag_backup");
+
+# Note that parsing /etc/passwd and /etc/shadow like this is pretty bad
+# form, but it's the only way I can test it without being root. It's
+# also the only way I can dump the hash directly into /etc/shadow.
+
+# Deletes first
+if (@deletes or @updates) {
+ open PASSWD, "$etc/passwd";
+ open PASSWDOUT, ">$etc/passwd.out";
+ while (<PASSWD>) {
+ my ($username) = split(/:/);
+ next if grep { $username eq $_ } (@deletes,@updates);
+ print PASSWDOUT;
+ }
+ close PASSWDOUT;
+ close PASSWD;
+
+ open SHADOW, "$etc/shadow";
+ open SHADOWOUT, ">$etc/shadow.out";
+ while (<SHADOW>) {
+ my ($username) = split(/:/);
+ next if grep { $username eq $_ } (@deletes,@updates);
+ print SHADOWOUT;
+ }
+ close SHADOWOUT;
+ close SHADOW;
+
+ rename "$etc/passwd.out","$etc/passwd";
+ chmod 0644, "$etc/passwd";
+ rename "$etc/shadow.out","$etc/shadow";
+ chmod 0640, "$etc/shadow";
+ system(qq{chown root.shadow "$etc/shadow"});
+}
+
+# Then add new ones.
+if (@adds or @updates) {
+ # Perhaps appending directly is a bad idea...
+ open PASSWD, ">>$etc/passwd";
+ open SHADOW, ">>$etc/shadow";
+
+ for my $add (@updates,@adds) {
+ my $agu = AwesomeGrid::User->load($add);
+ printf PASSWD "%s:x:%d:%d:[AG]:%s:%s\n", $agu->{username},
+ $agu->{uid}, $agu->{uid},
+ "$config->{homedir}/$agu->{username}",
+ $config->{'default-shell'};
+ printf SHADOW "%s:%s:%d:%d:%d:%d:::\n",
+ $agu->{username},
+ $agu->{'passwd-unix-md5'},
+ time / 86400, 0, 99999, 7;
+ unless (-d "$config->{homedir}/$agu->{username}") {
+ print "Creating homedir for $agu->{username}\n";
+ mkdir "$config->{homedir}/$agu->{username}";
+ chown $agu->{uid}, $agu->{uid},
+ "$config->{homedir}/$agu->{username}";
+ }
+ }
+ close PASSWD;
+ close SHADOW;
+}
+#!/usr/bin/perl
+use strict;
+
+use AwesomeGrid::User;
+
+my $user = shift;
+if (!$user) {
+ print "usage: ag-useradd user\n";
+ exit 1;
+}
+
+if (AwesomeGrid::User::exists($user)) {
+ print "User exists! I refuse to clobber them (even though I may want to).\n";
+ exit 1;
+}
+
+my $aguser = AwesomeGrid::User->new($user);
+
+$aguser->save();
+print "User $user created.\n";
+#!/usr/bin/perl
+use strict;
+
+use AwesomeGrid::User;
+
+my $user = shift;
+if (!$user) {
+ print "usage: ag-userdel user\n";
+ exit 1;
+}
+
+if (!AwesomeGrid::User::exists($user)) {
+ print "User $user doesn't exist!\n";
+ exit 1;
+}
+
+AwesomeGrid::User::delete($user);
+
+print "User $user deleted.\n";
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Config;
+use Sys::Hostname;
+
+my $etc = '/etc';
+my $bin = '/usr/bin';
+my $sbin = '/usr/sbin';
+
+# This should work better.
+my $perllib = $Config{sitelib};
+
+my $hostname = hostname();
+if (($hostname =~ /^([a-z0-9][a-z0-9-]*)(\.|$)/) && $1 !~ /-$/) {
+ $hostname = $1;
+} else {
+ print <<EOD;
+
+Your hostname ($hostname) is weird, and is probably
+not a valid internet hostname. A valid hostname uses only alphanumeric
+characters and hyphens. The hostname cannot begin or end with a hyphen.
+
+EOD
+ exit 1;
+}
+
+if ($> != 0) {
+ print <<EOD
+
+You are not running with root privs. This could make installation problematic
+if you're using the default paths.
+EOD
+}
+
+print <<EOD;
+
+This script will install the AwesomeGrid perl libraries, utilities, and
+configuration. A short summary is printed below. If this doesn't look
+right to you, please edit this script.
+
+Configuration will be installed to:
+ $etc/awesomegrid
+
+User binaries will be installed to:
+ $bin
+
+Superuser binaries will be installed to:
+ $sbin
+
+Perl libraries will be installed to:
+ $perllib
+
+EOD
+
+# Make sure that $perllib is actually in the @INC path, and warn if not.
+unless (grep $perllib, @INC) {
+ print <<EOD;
+Caution! $perllib not found in your \@INC path.
+AwesomeGrid may not run correctly during and/or after installation!
+
+EOD
+}
+
+print "Continue? [Y/n] ";
+my $ans = <STDIN>;
+unless ($ans =~ /^([Yy]|)$/) {
+ print "Aborting.\n";
+ exit 0;
+}
+
+print "Installing user binaries...\n";
+# No user binaries, LOL!
+
+print "Installing superuser binaries...\n";
+system("cp -fv ag-export ag-flush ag-import ag-keyring ag-listusers ag-passwd ag-update-shadow ag-useradd ag-userdel $sbin/");
+
+print "Installing perl libraries...\n";
+system("mkdir -p $perllib/AwesomeGrid") unless -d "$perllib/AwesomeGrid";
+system("cp -rfv lib/AwesomeGrid.pm $perllib/");
+system("cp -rfv lib/AwesomeGrid/{Config,Keyring,User}.pm $perllib/AwesomeGrid/");
+
+# Be extra careful for configuration bits
+umask 0077;
+
+print "Installing configuration...\n";
+mkdir "$etc/awesomegrid" unless -d "$etc/awesomegrid";
+unless (-f "$etc/awesomegrid/admins") {
+ open ADMINS, ">$etc/awesomegrid/admins";
+ print ADMINS <<EOD;
+# hostname admin GPG id
+$hostname foo\@bar.com
+EOD
+ close ADMINS;
+}
+unless (-f "$etc/awesomegrid/exports") {
+ open EXPORTS, ">$etc/awesomegrid/exports";
+ print EXPORTS <<EOD;
+# List here, one per line, users to export to other trusted systems (as
+# defined in the 'trusted' file)
+EOD
+ close EXPORTS;
+}
+unless (-f "$etc/awesomegrid/trusted") {
+ open TRUSTED, ">$etc/awesomegrid/trusted";
+ print TRUSTED <<EOD;
+# List here, one per line, trusted systems (short hostname)
+EOD
+ close TRUSTED;
+}
+unless (-f "$etc/awesomegrid/awesomegrid.conf") {
+ open CONF, ">$etc/awesomegrid/awesomegrid.conf";
+ print CONF <<EOD;
+# The GPG id of the admin of this machine
+gpg-id: foo\@bar.com
+# The place to install new home directories for imported users
+homedir: /home
+# The shell to give imported users if they don't specify one
+default-shell: /bin/bash
+EOD
+ close CONF;
+}
+mkdir "$etc/awesomegrid/users" unless -d "$etc/awesomegrid/users";
+unless (-d "$etc/awesomegrid/keyring") {
+ print <<EOD;
+It appears that you don't have an AwesomeGrid keyring. You will need one
+to export users to other systems (and have other admins import from
+you).
+
+EOD
+ print "Shall I run 'ag-keyring initialize' to generate a new keyring? [Y/n] ";
+ my $ans = <STDIN>;
+ if ($ans =~ /^([Yy]|)$/) {
+ system("$sbin/ag-keyring initialize");
+ }
+}
+
+print <<EOD;
+
+Installation finished. Please look over the configuration in
+$etc/awesomegrid and customize it to suit your system.
+EOD
+__END__
+
+=head1 NAME
+
+install.pl - Install AwesomeGrid
+
+=head1 SYNOPSIS
+
+install.pl
+
+=head1 DESCRIPTION
+
+Installs AwesomeGrid on the current system, with some basic sanity checking.
+Currently, it is pretty simple, and verified only on Slackware, Debian (and
+variants), and OpenSolaris.
+
+=head1 OPTIONS
+
+None.
+
+=head1 BUGS
+
+Too many. Far, far too many.
+
+=head1 AUTHOR
+
+Chip Black (bytex64@bytex64.net), Matt Erickson (peawee@peawee.net)
+
+=cut
+package AwesomeGrid;
+
+# For paranoia's sake
+umask 0077;
+
+our $version = 0.1;
+
+our $confdir;
+if ($ENV{AWESOMEGRID_CONFDIR}) {
+ $confdir = $ENV{AWESOMEGRID_CONFDIR};
+} else {
+ $confdir = '/etc/awesomegrid';
+}
+
+1;
+package AwesomeGrid::Config;
+use strict;
+
+use AwesomeGrid;
+
+use Sys::Hostname;
+
+# Return a list of trusted machines
+sub trusted {
+ open PEERS, "$AwesomeGrid::confdir/trusted";
+ my @peers;
+ while (<PEERS>) {
+ chomp;
+ s/#.*$//;
+ next if /^$/;
+ push @peers, $_;
+ }
+ close PEERS;
+ return @peers;
+}
+
+# Return a list of exported users
+sub exports {
+ open EXPORTS, "$AwesomeGrid::confdir/exports";
+ my @exports;
+ while (<EXPORTS>) {
+ chomp;
+ s/#.*$//;
+ next if /^$/;
+ push @exports, $_;
+ }
+ close EXPORTS;
+ return @exports;
+}
+
+# Return a hashref of admin IDs for each machine.
+sub admins {
+ my %admins;
+ open ADMINS, "$AwesomeGrid::confdir/admins";
+ while (<ADMINS>) {
+ chomp;
+ my ($machine, $admin) = split(/\s+/);
+ $admins{$machine} = $admin;
+ }
+ close ADMINS;
+ return \%admins;
+}
+
+# Return a hashref of values from awesomegrid.conf
+sub config {
+ my %config;
+ open CONFIG, "$AwesomeGrid::confdir/awesomegrid.conf";
+ while (<CONFIG>) {
+ s/#.*$//;
+ next if /^\s+$/;
+ if (/^([A-Za-z0-9_-]+):\s+(.*)$/) {
+ $config{$1} = $2;
+ } else {
+ print "Config error: line $.\n";
+ }
+ }
+ close CONFIG;
+ return \%config;
+}
+
+# Return the short hostname for this host, or undef if the hostname is
+# weird
+sub hostname {
+ my $hostname = Sys::Hostname::hostname();
+ if (($hostname =~ /^([a-z0-9][a-z0-9-]*)(\.|$)/) && $1 !~ /-$/) {
+ return $1;
+ } else {
+ return undef;
+ }
+}
+
+1;
+package AwesomeGrid::Keyring;
+use strict;
+
+use AwesomeGrid;
+use AwesomeGrid::Config;
+
+my $config = AwesomeGrid::Config::config();
+my $gpg = $config->{gpg} || '/usr/bin/gpg';
+
+sub gpg {
+ system(qq!$gpg --no-default-keyring --keyring "$AwesomeGrid::confdir/keyring/pubring.gpg" --secret-keyring "$AwesomeGrid::confdir/keyring/secring.gpg" --trustdb-name "$AwesomeGrid::confdir/keyring/trustdb.gpg" ! . join(' ',@_));
+}
+
+sub gpgpass {
+ my $passphrase = shift;
+ open GPG, qq!|$gpg --batch --passphrase-fd 0 --no-default-keyring --keyring "$AwesomeGrid::confdir/keyring/pubring.gpg" --secret-keyring "$AwesomeGrid::confdir/keyring/secring.gpg" --trustdb-name "$AwesomeGrid::confdir/keyring/trustdb.gpg" ! . join(' ',@_);
+ print GPG $passphrase;
+ close GPG;
+}
+
+sub initialize {
+ if (-e "$AwesomeGrid::confdir/keyring/secring.gpg") {
+ print "Refusing to clobber existing keyring.\n";
+ return;
+ }
+ if (!-d "$AwesomeGrid::confdir/keyring") {
+ mkdir "$AwesomeGrid::confdir/keyring";
+ }
+ gpg('--gen-key');
+}
+
+sub import_key {
+ my $file = shift;
+ gpg(qq{--import "$file"});
+}
+
+sub export_key {
+ my $who = shift;
+ gpg(qq{--output $who-pubkey.asc --armor --export $who});
+ print "Wrote $who-pubkey.asc\n";
+}
+
+sub list {
+ gpg('--list-keys');
+}
+
+sub encrypt {
+ my $who = shift;
+ my $in = shift;
+ my $out = shift;
+ my $passphrase = shift;
+
+ gpgpass($passphrase,qq{--recipient $who --sign --encrypt --output "$out" "$in"});
+}
+
+1;
+package AwesomeGrid::User;
+use strict;
+
+use AwesomeGrid;
+use AwesomeGrid::Config;
+
+# Class methods
+
+sub exists {
+ my $user = shift;
+ return -f "$AwesomeGrid::confdir/users/$user";
+}
+
+sub delete {
+ my $user = shift;
+ unlink "$AwesomeGrid::confdir/users/$user";
+}
+
+# Constructors
+
+sub new {
+ my $class = shift;
+ my $user = shift;
+ my $obj = {};
+ $obj->{username} = $user;
+ # Pick a random UID >= 10000 and < 65535
+ $obj->{uid} = int(rand(55534)) + 10000;
+ $obj->{home} = AwesomeGrid::Config::hostname();
+ warn "invalid hostname" unless $obj->{home};
+ chomp $obj->{home};
+ return bless($obj, $class);
+}
+
+sub load {
+ my $class = shift;
+ my $user = shift;
+
+ return $class->loadfrom("$AwesomeGrid::confdir/users/$user");
+}
+
+sub loadfrom {
+ my $class = shift;
+ my $file = shift;
+ local $_;
+
+ open USER, $file || return undef;
+ $_ = <USER>;
+ unless (/^AWESOMEGRID USER (\d+\.\d+)$/) {
+ print STDERR "Invalid AwesomeGrid User format\n";
+ return undef;
+ }
+ if ($1 > $AwesomeGrid::version) {
+ print STDERR "Cowardly refusing to parse future AwesomeGrid format.\n";
+ return undef;
+ }
+
+ my $obj = {};
+ while (<USER>) {
+ if (/([A-Za-z0-9_-]+):\s+(.*)$/) {
+ $obj->{$1} = $2;
+ }
+ }
+ close USER;
+
+ return bless($obj, $class);
+}
+
+# Object methods (isn't all this confusion WONDERFUL?)
+
+sub save {
+ my $class = shift;
+ open USER, ">$AwesomeGrid::confdir/users/$class->{username}";
+ print USER "AWESOMEGRID USER $AwesomeGrid::version\n";
+ for (keys %$class) {
+ print USER "$_: $class->{$_}\n";
+ }
+ close USER;
+}
+
+1;