Preliminary server chroot installation

Configures a chroot with Perl and bin/pgen-server to run a shell.

ssh to 'pgen@localhost' successful shows pgen-server test message then
logs out.

'add_key' and 'remove_server' functions also work. Recursive path
builder function 'dig_dirs' also works well within the latter.
This commit is contained in:
John Mertz 2022-06-30 06:49:11 +00:00
parent aedd59b759
commit e3bdcdb5ae
2 changed files with 221 additions and 0 deletions

3
bin/pgen-server Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/perl
print("Testing server\n");

218
install.pl Executable file
View File

@ -0,0 +1,218 @@
#!/usr/bin/perl
use strict;
use warnings;
our $server_dir = '/var/pgen';
use File::Which;
if (scalar(@ARGV) != 1) {
usage("Requires exactly 1 argument:");
} elsif ($ARGV[0] eq '-h' || $ARGV[0] eq '--help') {
usage("Usage: $0 <option>");
} elsif ($ARGV[0] eq 'server') {
if (-d $server_dir) {
print("Installation appears to already exist. Would you like it to be removed and
replaced? All existing password information will be lost. ");
my $selection;
do {
print("[y/N]? ");
$selection = <STDIN>;
chomp($selection);
} while (!defined($selection) || $selection !~ m/^[yYnN]?$/);
unless ($selection eq 'y' || $selection eq 'Y') {
die("Abandoning installation\n");
}
remove_server();
}
install_server();
} elsif ($ARGV[0] eq 'client') {
install_client();
} elsif ($ARGV[0] eq 'remove') {
my %removed = (
'server' => undef,
'client' => undef
);
if (detect_server()) {
$removed{'server'} = remove_server();
}
if (detect_client()) {
$removed{'client'} = remove_client();
}
foreach (keys(%removed)) {
if (defined($removed{$_})) {
if ($removed{$_}) {
print("Received error '$removed{$_}' while removing $_\n");
} else {
print("Successfully removed $_\n");
}
}
}
} elsif ($ARGV[0] eq 'add') {
add_key();
} else {
usage("Invalid argument '$ARGV[0]'. Requires one of:");
}
exit(0);
sub detect_server
{
my $dir = (getpwnam('pgen'))[7] || return undef;
if (-d $dir) {
return($dir);
}
return(undef);
}
sub detect_client
{
return(which('pgen'));
}
sub install_server
{
my $perl = which('perl') || die "Missing dependency: perl\n";
chomp($perl);
my ($perl_dir) = $perl =~ m/^((?:\/[^\/]*)*)\/perl$/;
my @perl_path = ( $server_dir );
foreach (split('/',$perl_dir)) {
unless($_ eq '') {
if (scalar(@perl_path)) {
$_ = $perl_path[scalar(@perl_path)-1].'/'.$_;
}
push(@perl_path,$_);
}
}
foreach (@perl_path, $server_dir."/.ssh", $server_dir."/bin", $server_dir."/lib", $server_dir."/lib64") {
print "Creating directory '$_'\n";
mkdir($_) || die "Failed to mkdir '$_': $!\n";
}
use File::Copy;
print("Installing Perl...\n");
copy($perl,"${server_dir}${perl}");
chmod(0755, "${server_dir}${perl}");
my $libs = `ldd ${server_dir}${perl}`;
foreach (split("\n",$libs)) {
if ($_ =~ m#^\s+([^\ ]+)\ =>\ (\/[^\/]+)((?:\/[^\/]*)*)\/([^\/]+) \(.*#) {
print "Copying ${2}${3}/${4} to ${server_dir}${2}/${4}\n";
copy("${2}${3}/${4}","${server_dir}${2}/${4}");
} elsif ($_ =~ m#^\s+(\/[^\/]+)((?:\/[^\/]*)*)\/([^\/]+) \(.*#) {
print "Copying ${1}${2}/${3} to ${server_dir}${1}/${3}\n";
copy("${1}${2}/${3}","${server_dir}${1}/${3}");
} else {
print "skipping $_\n";
}
}
my $src = $0;
$src =~ s/(.*)\/([^\/]*)/$1\/bin\/pgen-server/;
print("Installing $src to ${server_dir}/bin/pgen\n");
copy($src,"${server_dir}/bin/pgen") || die("Failed to install: $!\n");
chmod(0755, "${server_dir}/bin/pgen");
print("Configuring user 'pgen'\n");
system("useradd -d $server_dir pgen");
system("usermod -s $server_dir/bin/pgen pgen");
add_key();
print("Server installation complete\n");
}
sub install_client
{
if (detect_client()) {
die("Client is already installed. If necessary, uninstall it with 'remove'\n");
}
print("TODO: Here I would install the client\n");
}
sub remove_server
{
if (detect_server()) {
system("userdel pgen");
}
my @paths;
my $paths_ref = \@paths;
dig_dirs($paths_ref, $server_dir);
unless (scalar(@paths)) {
die("No chroot installation present at $server_dir\n");
}
while (scalar(@paths)) {
my $target = pop(@paths);
print "Removing $target...\n";
if (-d $target) {
rmdir($target) || die("Failed to remove directory '$target': $!\n");
} else {
unlink($target) || die("Failed to delete '$target': $!\n");
}
}
}
sub dig_dirs
{
my $paths_ref = shift;
my $path = shift;
unless (-e $path) {
return(undef);
}
if ($path =~ m/\/\.\.?$/) {
return;
}
push(@$paths_ref, $path);
foreach (glob("$path/*"), glob("$path/.*")) {
if ($path =~ m/\/\.\.?$/) {
next();
}
if (-d $_) {
dig_dirs($paths_ref,$_);
} else {
push(@$paths_ref, $_);
}
}
}
sub remove_client
{
unless (detect_client()) {
return "Client is not installed.\n";
}
print("TODO: Here I would remove the client\n");
}
sub add_key
{
unless (detect_server()) {
die("This must be run on a host with the server installed");
}
my $file = "$server_dir/.ssh/authorized_keys";
my $msg = "Paste the SSH public key of the user. 'Q' to";
if (-e $file) {
print("Adding new user. $msg abort ");
} else {
print("Initializing with first user. $msg skip ");
}
my $key;
do {
print("[<key>/Q]: ");
$key = <STDIN>;
chomp($key);
} while (!defined($key) || ($key ne 'Q' && $key ne 'q' && $key !~ m/^ssh\-(dsa|ecdsa|ecdsa\sk|ed25519|ed25519-sk|rsa)\ [a-zA-Z0-9\+\/]{50,}/));
open(my $fh, '>>', $file) || die("Failed to open '$file': $!\n");
unless ($key eq 'Q' && $key ne 'q') {
print $fh $key."\n";
}
close($fh);
}
sub usage
{
my $error = shift;
if (!defined($error)) {
$error = "Unknown failure. Try running with one of these options:";
}
die($error . "
server - install server application
client - install client application
remove - uninstall client and server (as necessary)
add - add a user key (SSH public key)\n");
exit(1);
}