#!/usr/bin/perl use strict; use warnings; use OpenBSD::Pledge; use OpenBSD::Unveil; use Data::Dumper; use File::Copy qw(copy); my $vmconf = "/etc/vm.conf"; my $zonedir = "/var/nsd/zones/master/"; my $hostname = "host.example.com"; my $ipv4path = "/home/username/ipv4s"; my $isopath = "/home/iso/install73.iso"; my @ipv4s; if (!(-s "$ipv4path")) { print "No IPv4 addresses in $ipv4path!\n"; die; } else { @ipv4s = readarray($ipv4path); } `doas chmod -R g+w $zonedir`; # Read from filename and return array of lines without trailing newlines sub readarray { my ($filename) = @_; open(my $fh, '<', $filename) or die "Could not read file '$filename' $!"; chomp(my @lines = <$fh>); close $fh; return @lines; } # Read from filename and return as string sub readstr { my ($filename) = @_; open my $fh, '<', $filename or die "Could not read file '$filename' $!"; my $str = do { local $/; <$fh> }; close $fh; return $str; } # Write str to filename sub writefile { my ($filename, $str) = @_; open(my $fh, '>', "$filename") or die "Could not write to $filename"; print $fh $str; close $fh; } # Append str to filename sub appendfile { my ($filename, $str) = @_; open(my $fh, '>>', "$filename") or die "Could not append to $filename"; print $fh $str; close $fh; } sub date { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); my $localtime = sprintf("%04d%02d%02d", $year+1900, $mon+1, $mday); return $localtime; } sub setdns { my ($domain, $ip) = @_; my $filename = "$zonedir/$hostname"; my $subdomain; if ($domain =~ /^([a-zA-Z][-\.a-zA-Z0-9]+)\.$hostname$/) { $subdomain = $1; } else { return 0; } my @lines = readarray($filename); foreach my $line (@lines) { # increment the zone's serial number if ($line =~ /(\d{8})(\d{2})((\s+\d+){4}\s*\))/) { my $date = date(); my $serial = 0; if ($date <= $1) { $serial = $2+1; } $line = $`.$date.sprintf("%02d",$serial).$3.$'; } } if ($ip =~ /^([0-9\.]+)$/) { # if IPv4 push(@lines, "$subdomain 3600 IN A $ip"); } elsif ($ip =~ /:/) { # if IPv6 push(@lines, "$subdomain 3600 IN AAAA $ip"); } elsif (!defined($ip)) { # delete records @lines = grep !/\b$subdomain\s*3600\s*IN/, @lines; } # trailing newline necessary writefile("$filename.bak", join("\n", @lines)."\n"); copy "$filename.bak", $filename; if (system("doas -u _nsd nsd-control reload")) { return 0; } else { return 1; } } # create A and AAAA records for subdomain, set the rDNS, # and return the new ipv6 address sub nextdns { my ($subdomain) = @_; my $ipv4 = shift(@ipv4s); my $ipv6; my $fqdn = "$subdomain.$hostname"; if ($ipv4 =~ /^[0-9]+\.[0-9]+\.[0-9]+\.([0-9]+)$/) { $ipv6 = "2602:fccf:1:1".sprintf("%03d",$1)."::"; } writefile($ipv4path, join("\n", @ipv4s)); my $success = setdns($fqdn, $ipv4) && setdns($fqdn, $ipv6) && setdns("ns1.$fqdn", $ipv4) && setdns("ns2.$fqdn", $ipv4); print "IP: $ipv4 $ipv6\n"; return $success; } sub createshell { my ($username, $password) = @_; print "Username: $username\n"; print "Password: $password\n"; system "doas groupadd $username"; system "doas adduser -batch $username $username $username `encrypt $password`"; system "doas usermod -G vmdusers $username"; system "doas chmod -R o-rwx /home/$username"; system "doas su -l $username -c \"vmctl create -s 20G $username.qcow2\""; print "VM created for $username!\n"; my @vmconf = readarray($vmconf); my $lladdr; foreach my $line (@vmconf) { if ($line =~ /lladdr (.*)/) { $lladdr = $1; } } if (defined($lladdr) && $lladdr =~ /([0-9a-fA-F]{2})$/) { $lladdr = $`.($1+1); } my $block = <<"EOF"; vm "$username" { owner $username memory 2048M cdrom "$isopath" disk /home/$username/$username.qcow2 interface { locked lladdr $lladdr switch "switch0" } } EOF appendfile($vmconf, $block); `doas vmctl reload`; } my $nargs = $#ARGV + 1; if ($nargs != 1) { print "\nUsage: install.pl username\n"; exit; } my $username = $ARGV[0]; my $password = join'', map +(0..9,'a'..'z','A'..'Z')[rand(10+26*2)], 1..12; createshell($username, $password); nextdns($username);