Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • alex_orange/emulab-devel
  • hakasapl/emulab-devel
  • cecchet/emulab-devel
  • srirams/emulab-devel
  • chuck/emulab-devel
  • crd/emulab-devel
  • kwebb/emulab-devel
  • moate/emulab-devel
  • grubb/emulab-devel
  • nasir/emulab-devel
  • asydney/emulab-devel
  • kdownie/emulab-devel
  • wvdemeer/emulab-devel
  • anilmr/emulab-devel
  • bvermeul/emulab-devel
  • emulab/emulab-devel
16 results
Show changes
Commits on Source (10043)
Showing with 2196 additions and 700 deletions
.merge-build
clientside/tmcc/cygwinseven/unattend-7pro-x86.xml.in
extendCMV
\ No newline at end of file
......@@ -10,6 +10,7 @@ clientside/os/imagezip/fat
clientside/os/imagezip/ntfs/liblocale
clientside/lib/event/event_wrap*
clientside/tmcc/cygwinxp/site-lisp
clientside/tmcc/freebsd/init
clientside/tmcc/plab
os/shd
www/cvsweb
......@@ -49,3 +50,8 @@ LGPL-COPYING
AGPL-COPYING
TODO.plab
MOVED-TO-WIKI
VERSION
protogeni/flack/js/forge
protogeni/flack/src/com/hurlant
protogeni/flack/src/com/mattism
protogeni/protogeniflash/src/com/mattism
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2021 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -32,6 +32,7 @@ PELABSUPPORT = @PELABSUPPORT@
PGENISUPPORT = @PROTOGENI_SUPPORT@
ISMAINSITE = @TBMAINSITE@
SPEWFROMOPS = @SPEWFROMOPS@
MOBILESUPPORT = @MOBILESUPPORT@
SYSTEM := $(shell uname -s)
include Makeconf
......@@ -51,14 +52,14 @@ ifeq ($(STANDALONE_CLEARINGHOUSE),0)
SUBDIRS = \
clientside/lib \
db assign www @optional_subdirs@ clientside ipod security sensors \
pxe tbsetup account tmcd utils backend tip ipod vis \
sensors os xmlrpc install/newnode_sshkeys mote tools/whol \
tools/svn wiki bugdb collab node_usage install
pxe tbsetup account tmcd utils wbstore backend ipod vis \
sensors os xmlrpc autofs install/newnode_sshkeys \
tools/svn collab/exp-vis node_usage install
ifeq ($(ISMAINSITE),1)
SUBDIRS += tools/rmanage
SUBDIRS += tools/whol
endif
ifeq ($(PGENISUPPORT),1)
SUBDIRS += protogeni apt
SUBDIRS += protogeni apt powder
endif
else
SUBDIRS = db tbsetup account protogeni
......@@ -107,7 +108,8 @@ boss-install-noupdatecheck: install-schemacheck \
install-setbuildinfo
# Only the checks:
install-checks: install-updatecheck \
install-checks: \
install-updatecheck \
install-schemacheck \
install-sitevarscheck \
install-dbfillcheck install-genischemacheck
......@@ -127,10 +129,10 @@ post-install:
ifeq ($(EVENTSYS),1)
@$(MAKE) -C event post-install
endif
@$(MAKE) -C mote post-install
ifeq ($(MOBILESUPPORT),1)
@$(MAKE) -C mobile
endif
@$(MAKE) -C tools post-install
@$(MAKE) -C wiki post-install
@$(MAKE) -C bugdb post-install
@$(MAKE) -C collab post-install
@$(MAKE) -C utils post-install
ifeq ($(NODE_USAGE_SUPPORT),1)
......@@ -151,14 +153,12 @@ ops-install:
@$(MAKE) -C rc.d control-install
@$(MAKE) -C tbsetup control-install
@$(MAKE) -C security control-install
@$(MAKE) -C tip control-install
@$(MAKE) -C db control-install
@$(MAKE) -C utils control-install
@$(MAKE) -C clientside control-install
ifeq ($(EVENTSYS),1)
@$(MAKE) -C event control-install
endif
@$(MAKE) -C xmlrpc control-install
@$(MAKE) -C account control-install
ifeq ($(PELABSUPPORT),1)
@$(MAKE) -C pelab control-install
......@@ -174,6 +174,7 @@ opsfs-install: ops-install fs-install
@echo "Combined ops/fs install done."
install-mkdirs:
-mkdir -p $(INSTALL_TOPDIR)/db
-mkdir -p $(INSTALL_TOPDIR)/locks
-mkdir -p $(INSTALL_TOPDIR)/log
-mkdir -p $(INSTALL_TOPDIR)/log/mysql
......@@ -208,7 +209,7 @@ just-builddirs:
tipserv-install:
-mkdir -p $(INSTALL_TOPDIR)/log/tiplogs
-mkdir -p $(INSTALL_TOPDIR)/etc
@$(MAKE) -C tip tipserv-install
@$(MAKE) -C clientside/tip tipserv-install
@$(MAKE) -C clientside/os/capture tipserv-install
@$(MAKE) -C tbsetup tipserv-install
......@@ -219,31 +220,22 @@ client-mkdirs:
client:
@$(MAKE) -C clientside client
@$(MAKE) -C os client
ifneq ($(SYSTEM),CYGWIN_NT-5.1)
@$(MAKE) -C tip client
endif
client-install: client client-mkdirs
@$(MAKE) -C clientside client-install
@$(MAKE) -C os client-install
ifneq ($(SYSTEM),CYGWIN_NT-5.1)
@$(MAKE) -C tip client-install
endif
subboss:
@$(MAKE) -C clientside subboss
@$(MAKE) -C tbsetup subboss
@$(MAKE) -C db subboss
@$(MAKE) -C os subboss
ifneq ($(SYSTEM),CYGWIN_NT-5.1)
@$(MAKE) -C tip client
endif
@$(MAKE) -C utils subboss
subboss-install: subboss
@$(MAKE) -C clientside subboss-install
@$(MAKE) -C tbsetup subboss-install
@$(MAKE) -C os subboss-install
ifneq ($(SYSTEM),CYGWIN_NT-5.1)
@$(MAKE) -C tip client-install
endif
@$(MAKE) -C utils subboss-install
@$(MAKE) -C db subboss-install
@$(MAKE) -C rc.d subboss-install
......@@ -286,39 +278,27 @@ mfsoscheck:
fi
mfs: mfsoscheck
@$(MAKE) -C os mfs
@$(MAKE) -C clientside mfs
mfs-nostatic: mfsoscheck
@NOSTATIC=1 $(MAKE) -C os mfs
@NOSTATIC=1 $(MAKE) -C clientside mfs
mfs-install: destdircheck mfs client-mkdirs
@$(MAKE) -C os mfs-install
@$(MAKE) -C clientside mfs-install
mfs-nostatic-install: destdircheck mfs-nostatic client-mkdirs
@$(MAKE) -C os mfs-install
@$(MAKE) -C clientside mfs-install
frisbee-mfs: mfsoscheck
@$(MAKE) -C cdrom/groklilo client
@$(MAKE) -C os frisbee-mfs
@$(MAKE) -C clientside frisbee-mfs
frisbee-mfs-nostatic: mfsoscheck
@NOSTATIC=1 $(MAKE) -C cdrom/groklilo client
@NOSTATIC=1 $(MAKE) -C os frisbee-mfs
@NOSTATIC=1 $(MAKE) -C clientside frisbee-mfs
frisbee-mfs-install: destdircheck frisbee-mfs
@CLIENT_BINDIR=/etc/testbed $(MAKE) -e -C cdrom/groklilo client-install
@$(MAKE) -C os frisbee-mfs-install
@$(MAKE) -C clientside frisbee-mfs-install
frisbee-mfs-nostatic-install: destdircheck frisbee-mfs-nostatic
@CLIENT_BINDIR=/etc/testbed $(MAKE) -e -C cdrom/groklilo client-install
@$(MAKE) -C os frisbee-mfs-install
@$(MAKE) -C clientside frisbee-mfs-install
newnode-mfs: mfsoscheck
......@@ -431,6 +411,19 @@ ifeq ($(PGENISUPPORT),1)
endif
@echo "Done"
BRANCHCHECK=
BRANCHECHO= @echo "Skipping branch check since not the Mothership"
ifeq ($(ISMAINSITE),1)
ifeq ($(TBROOT),/usr/testbed)
BRANCHCHECK= cd $(SRCDIR) && \
git status --porcelain -s -b | head -1 | grep -q -s current
BRANCHECHO= @echo "Checking to make sure you are on the mothership branch"
endif
endif
install-branchcheck:
$(BRANCHECHO)
$(BRANCHCHECK)
# We use separate src and obj trees in Emulab, so the traditional distclean to
# clean "made" files from a mingled source-and-obj tree is unnecessary.
# However, this may be useful if you mistakenly configure and make a src tree.
......
#
# Copyright (c) 2000-2012 University of Utah and the Flux Group.
# Copyright (c) 2000-2012, 2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -108,6 +108,21 @@ distclean: default-clean
default-clean:
rm -f GNUmakefile
# This is to avoid warnings about duplicate targets.
default-install-notusing:
ifeq ($(ISMAINSITE),1)
ifeq ($(TBROOT),/usr/testbed)
(cd $(SRCDIR) ; \
git status --porcelain -s -b | head -1 | grep -q -s current)
else
/usr/bin/true
endif
else
/usr/bin/true
endif
#install: default-install
#
# Where to find source files.
# Using specific patterns instead of the catch-all VPATH variable
......
#
# Copyright (c) 2000-2012 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -44,6 +44,7 @@ export JAR = @JAR@
prefix = @prefix@
exec_prefix = @exec_prefix@
ISMAINSITE = @TBMAINSITE@
TBROOT = @prefix@
TBDEFS = @TBDEFS@
TBDBNAME = @TBDBNAME@
......@@ -120,4 +121,4 @@ MERGE_BUILD_SANDBOX = @MERGE_BUILD_SANDBOX@
EXP_VIS_SUPPORT = @EXP_VIS_SUPPORT@
TESTBED_LIBSRCDIR = ${TESTBED_SRCDIR}/clientside/lib
TESTBED_LIBOBJDIR = ${OBJDIR}/clientside/lib
TESTBED_IMAGEZIPSRCDIR = ${OBJDIR}/clientside/os/imagezip
TESTBED_IMAGEZIPSRCDIR = ${TESTBED_SRCDIR}/clientside/os/imagezip
#
# Copyright (c) 2000-2011, 2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2024 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -31,7 +31,7 @@ include $(OBJDIR)/Makeconf
SBIN_STUFF = tbacct addsfskey addpubkey mkusercert quotamail genpubkeys \
newuser newproj mksyscert spewcert dumpuser dumpproject \
manageremote
manageremote regencerts
LIBEXEC_STUFF = webtbacct webaddsfskey webaddpubkey webmkusercert \
webnewuser webnewproj webspewcert webmanageremote
CTRLSBIN_STUFF = adduserhook accountsetup
......
This diff is collapsed.
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2012 University of Utah and the Flux Group.
# Copyright (c) 2000-2020 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -24,6 +24,7 @@
use English;
use Getopt::Std;
use XML::Simple;
use File::Temp qw(tempfile :POSIX );
#
# Parse ssh public keys and enter into the DB. The default format is
......@@ -48,7 +49,7 @@ sub usage()
print " -r Force a regenerate of initial key for user\n";
exit(-1);
}
my $optlist = "dkniwfu:rX:sRNC:S:";
my $optlist = "dkniwfu:rX:sRNC:S:Ia";
my $iskey = 0;
my $verify = 0;
my $initmode = 0;
......@@ -58,6 +59,8 @@ my $nobody = 0;
my $noemail = 0;
my $remove = 0;
my $nodelete = 0;
my $internal = 0;
my $isaptkey = 0;
my $Comment;
my $xmlfile;
......@@ -68,7 +71,11 @@ my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $OURDOMAIN = "@OURDOMAIN@";
my $CONTROL = "@USERNODE@";
my $KEYGEN = "/usr/bin/ssh-keygen";
my $ACCOUNTPROXY= "$TB/sbin/accountsetup";
my $SSH = "$TB/bin/sshtb";
my $SAVEUID = $UID;
my $USERUID;
# Locals
......@@ -83,6 +90,7 @@ my $user_name;
my $user_email;
my $user_dbid;
my $user_uid;
my $user_gid;
my $debug = 0;
#
......@@ -93,6 +101,9 @@ use libaudit;
use libdb;
use libtestbed;
use User;
if (@PROTOGENI_SUPPORT@) {
require APT_Utility;
}
#
# Function prototypes
......@@ -155,6 +166,9 @@ if (! getopts($optlist, \%options)) {
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"a"})) {
$isaptkey = 1;
}
if (defined($options{"k"})) {
$iskey = 1;
}
......@@ -170,6 +184,9 @@ if (defined($options{"i"})) {
if (defined($options{"N"})) {
$nodelete = 1;
}
if (defined($options{"I"})) {
$internal = 1;
}
if (defined($options{"r"})) {
$force = 1;
}
......@@ -248,6 +265,18 @@ if (defined($user)) {
$user_dbid = $target_user->dbid();
$user_uid = $target_user->uid();
$USERUID = $target_user->unix_uid();
my $firstproject;
if ($target_user->FirstApprovedProject(\$firstproject) < 0) {
fatal("Could not determine first approved project");
}
if (defined($firstproject)) {
$user_gid = $firstproject->unix_gid();
}
else {
$user_gid = "guest";
}
}
#
......@@ -273,18 +302,16 @@ else {
}
#
# Initmode or genmode, do it and exit. Eventually get rid of the switch
# to the target user.
# Initmode or genmode, do it and exit.
#
if ($initmode) {
# Drop root privs, switch to target user.
$EUID = $UID = $USERUID;
exit InitUser();
}
if ($genmode) {
# Drop root privs, switch to target user.
$EUID = $UID = $USERUID;
exit GenerateKeyFile();
if ($initmode || $genmode) {
if ($initmode) {
exit(InitUser());
}
if ($genmode) {
exit(GenerateKeyFile());
}
exit(1);
}
# Else, key parse mode ...
......@@ -307,7 +334,23 @@ else {
if (! -e $keyfile) {
fatal("No such file: $keyfile\n");
}
$keyline = `head -1 $keyfile`;
open(KEY, "$keyfile")
or fatal("Could not open $keyfile to read");
#
# Ick, look at first line to see if it looks like an openssh key,
# and if so we want to remove the embedded newlines.
#
$keyline = <KEY>;
my $chompit = 0;
if ($keyline =~ /^ssh/ || $keyline =~ /^\d+\s+\d+/) {
$chompit = 1;
}
while (<KEY>) {
chomp($_) if ($chompit);
$keyline .= $_;
}
close(KEY);
}
#
......@@ -320,18 +363,11 @@ if (!$verify) {
!$nodelete) {
fatal("You are not allowed to set pubkeys for $target_user\n");
}
if (-d "$HOMEDIR/$user_uid/.ssh") {
if ($target_user->SameUser($this_user) && $this_user->active()) {
# Drop root privs, switch to target user.
$EUID = $UID = $USERUID;
$genmode = 1;
}
#
# This script is audited when not in verify mode. Since all keys are first
# checked with verify mode, this should not cause any extra email from bad
# keys.
#
AuditStart(0);
}
#
......@@ -374,7 +410,7 @@ sub ParseKey($) {
return 0;
}
if ($keyline =~ /^(\d*\s\d*\s[0-9a-zA-Z]*) ([-\w\@\.:\ ]*)\s*$/) {
if ($keyline =~ /^(\d*\s\d*\s[0-9a-zA-Z]*) ([-\\\w\@\.:\ ]*)\s*$/) {
# Protocol 1
$type = "ssh-rsa1";
$key = $1;
......@@ -386,21 +422,20 @@ sub ParseKey($) {
$key = $1;
}
elsif ($keyline =~
/^(ssh-rsa|ssh-dss) ([-\w\.\@\+\/\=]*) ([-\w\@\.:\ ]*)$/) {
/^(ssh-rsa|ssh-ed25519) ([-\w\.\@\+\/\=]*) ([-\\\w\@\.:\ ]*)$/) {
# Protocol 2
$type = $1;
$key = "$1 $2";
$comment = $3;
}
elsif ($keyline =~ /^(ssh-rsa|ssh-dss) ([-\w\.\@\+\/\=:]*)$/) {
elsif ($keyline =~ /^(ssh-rsa|ssh-ed25519) ([-\w\.\@\+\/\=:]*)$/) {
# Protocol 2 but no comment field
$type = $1;
$key = "$1 $2";
}
if (!defined($key)) {
print "Key cannot be parsed!\n";
print "Key: $keyline\n";
print STDERR "Key cannot be parsed, we accept rsa and ed25519 only!\n";
return 0;
}
......@@ -424,17 +459,24 @@ sub ParseKey($) {
DBQueryFatal("delete from user_pubkeys ".
"where uid_idx='$user_dbid' and comment=$safe_comment");
}
# Only one APT key allowed
if ($isaptkey) {
DBQueryFatal("delete from user_pubkeys ".
"where uid_idx='$user_dbid' and isaptkey=1");
}
DBQueryFatal("replace into user_pubkeys set ".
" uid='$user_uid', uid_idx='$user_dbid', ".
" internal='0', nodelete='$nodelete', ".
" idx=NULL, stamp=now(), ".
" internal='$internal', nodelete='$nodelete', ".
" isaptkey='$isaptkey',idx=NULL, stamp=now(), ".
" pubkey=$safe_key, comment=$safe_comment");
#
# Mark user record as modified so nodes are updated.
#
TBNodeUpdateAccountsByUID($user_uid);
if (@PROTOGENI_SUPPORT@) {
APT_Utility::UpdateInstancesByUser($target_user);
}
my $chunked = "";
while (length($key)) {
......@@ -457,7 +499,7 @@ sub ParseKey($) {
"SSH Public Key for '$user_uid' added:\n".
"\n".
"$chunked\n",
"$TBOPS");
"$TBOPS", "Bcc: $TBAUDIT");
}
return 1;
}
......@@ -469,98 +511,47 @@ sub ParseKey($) {
#
sub InitUser()
{
my $sshdir = "$HOMEDIR/$user_uid/.ssh";
#
# Set up the ssh key, but only if not done so already.
#
if (! -e "$sshdir") {
mkdir("$sshdir", 0700) or
fatal("Could not mkdir $sshdir: $!");
}
if (! -e "$sshdir/identity" || $force) {
print "Creating ssh protocol 1 key for $user.\n";
#
# Want to delete existing key from DB.
#
if (-e "$sshdir/identity") {
my $ident = `cat $sshdir/identity.pub`;
if ($ident =~ /(\d*\s\d*\s[0-9a-zA-Z]*)\s([-\w\@\.]*)/) {
DBQueryFatal("delete from user_pubkeys ".
"where uid_idx='$user_dbid' and pubkey='$1 $2'");
}
unlink("$sshdir/identity");
}
# Hmm, need to use -C option so comment field makes sense.
if (system("$KEYGEN -t rsa1 -P '' ".
"-C '${user}" . "\@" . ${OURDOMAIN} . "' ".
"-f $sshdir/identity")) {
fatal("Failure in ssh-keygen!");
}
#
# Grab a copy for the DB.
#
my $ident = `cat $sshdir/identity.pub`;
if ($ident =~ /(\d*\s\d*\s[0-9a-zA-Z]*)\s([-\w\@\.]*)/) {
DBQueryFatal("replace into user_pubkeys set ".
" uid='$user_uid', uid_idx='$user_dbid', ".
" internal='1', nodelete='1', idx=NULL, stamp=now(), ".
" pubkey='$1 $2', comment='$2'");
}
else {
fatal("Bad protocol 1 public key: $ident\n");
}
}
# Want to delete existing keys from DB, but not the sslcert key.
#
# Moving to V2 keys ...
#
if (! -e "$sshdir/id_rsa" || $force) {
print "Creating ssh protocol 2 key for $user.\n";
#
# Want to delete existing key from DB.
#
if (-e "$sshdir/id_rsa") {
my $ident = `cat $sshdir/id_rsa.pub`;
if ($ident =~
/^(ssh-rsa [-\w\.\@\+\/\=]*) ([-\w\@\.\ ]*)$/) {
DBQueryFatal("delete from user_pubkeys ".
"where uid_idx='$user_dbid' and pubkey='$1 $2'");
}
unlink("$sshdir/id_rsa");
}
# Hmm, need to use -C option so comment field makes sense.
DBQueryFatal("delete from user_pubkeys ".
"where uid_idx='$user_dbid' and ".
" (internal=1 or comment like '%\@${OURDOMAIN}' or ".
" comment like '%\@boss.${OURDOMAIN}') and ".
" isaptkey=0 and comment not like 'sslcert:%'");
# Redirect pub key to file, redirect STDERR to STDIN for display.
my $outfile = tmpnam();
my $command = "$ACCOUNTPROXY createsshkey $user_uid $user_gid ";
$UID = 0;
open ERR, "$SSH -host $CONTROL '$command rsa' 2>&1 > $outfile |";
$UID = $SAVEUID;
$errs = "";
while (<ERR>) {
$errs .= $_;
}
close(ERR);
print STDERR $errs;
if ($?) {
unlink($outfile);
fatal("Could not create rsa key");
}
$pubkey = `cat $outfile`;
chomp($pubkey);
$safe_pubkey = DBQuoteSpecial($pubkey);
$comment = "rsa\@${OURDOMAIN}";
if (system("$KEYGEN -t rsa -P '' ".
"-C '${user}" . "\@" . ${OURDOMAIN} . "' ".
"-f $sshdir/id_rsa")) {
fatal("Failure in ssh-keygen!");
}
#
# Grab a copy for the DB.
#
my $ident = `cat $sshdir/id_rsa.pub`;
if ($ident =~
/^(ssh-rsa [-\w\.\@\+\/\=]*) ([-\w\@\.\ ]*)$/) {
DBQueryFatal("replace into user_pubkeys set ".
" uid='$user_uid', uid_idx='$user_dbid', ".
" internal='1', nodelete='1', idx=NULL, stamp=now(), ".
" pubkey='$1 $2', comment='$2'");
}
else {
fatal("Bad protocol 2 public key: $ident\n");
}
}
if (! DBQueryWarn("replace into user_pubkeys set ".
" uid='$user_uid', uid_idx='$user_dbid', ".
" internal='1', nodelete='1', idx=NULL, stamp=now(), ".
" pubkey=$safe_pubkey, comment='$comment'")) {
unlink($outfile);
fatal("Could not add rsa key to database");
}
unlink($outfile);
return GenerateKeyFile();
}
......@@ -571,15 +562,9 @@ sub InitUser()
sub GenerateKeyFile()
{
my @pkeys = ();
my $outfile = tmpnam();
my $sshdir = "$HOMEDIR/$user_uid/.ssh";
my $keyfile = "$sshdir/authorized_keys";
if (! -e $sshdir) {
if (! mkdir($sshdir, 0700)) {
warn("*** WARNING: Could not mkdir $sshdir: $!\n");
return -1;
}
}
my $query_result =
DBQueryFatal("select pubkey from user_pubkeys ".
"where uid_idx='$user_dbid'");
......@@ -588,15 +573,14 @@ sub GenerateKeyFile()
push(@pkeys, $key);
}
print "Generating $keyfile ...\n";
if (!open(AUTHKEYS, "> ${keyfile}.new")) {
warn("*** WARNING: Could not open ${keyfile}.new: $!\n");
print "Generating authorized_keys ...\n";
if (!open(AUTHKEYS, "> $outfile")) {
warn("*** WARNING: Could not open $outfile: $!\n");
return -1;
}
print AUTHKEYS "#\n";
print AUTHKEYS "# DO NOT EDIT! This file auto generated by ".
"Emulab.Net account software.\n";
"Emulab account software.\n";
print AUTHKEYS "#\n";
print AUTHKEYS "# Please use the web interface to edit your ".
"public key list.\n";
......@@ -607,28 +591,17 @@ sub GenerateKeyFile()
}
close(AUTHKEYS);
if (!chmod(0600, "${keyfile}.new")) {
warn("*** WARNING: Could not chmod ${keyfile}.new: $!\n");
return -1;
}
if (-e "${keyfile}") {
if (system("cp -p -f ${keyfile} ${keyfile}.old")) {
warn("*** Could not save off ${keyfile}: $!\n");
return -1;
}
if (!chmod(0600, "${keyfile}.old")) {
warn("*** Could not chmod ${keyfile}.old: $!\n");
}
}
if (system("mv -f ${keyfile}.new ${keyfile}")) {
warn("*** Could not mv ${keyfile} to ${keyfile}.new: $!\n");
}
elsif (-e "$sshdir/authorized_keys2") {
#
# Save to remove deprecated authorized_keys2 file at this point.
#
unlink("$sshdir/authorized_keys2");
$EUID = $UID = 0;
system("$SSH -host $CONTROL ".
"'$ACCOUNTPROXY dropfile $user_uid $user_gid 0600 $sshdir ".
"authorized_keys' < $outfile");
$EUID = $UID = $SAVEUID;
if ($?) {
unlink($outfile);
fatal("Could not copy authorized_keys file to $CONTROL");
}
unlink($outfile);
return 0;
}
......
#!/usr/bin/perl -w
#
# Copyright (c) 2010-2013 University of Utah and the Flux Group.
# Copyright (c) 2010-2020 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -128,7 +128,7 @@ sub DumpUser($)
"URL" => {"tag" => "URL",
"optional" => 1 },
"addr" => {"tag" => "address",
"optional" => 0 },
"optional" => 1 },
"addr2" => {"tag" => "address2",
"optional" => 1 },
"city" => {"tag" => "city",
......@@ -136,13 +136,13 @@ sub DumpUser($)
"state" => {"tag" => "state",
"optional" => 0 },
"zip" => {"tag" => "zip",
"optional" => 0 },
"optional" => 1 },
"country" => {"tag" => "country",
"optional" => 0 },
"phone" => {"tag" => "phone",
"optional" => 0 },
"optional" => 1 },
"title" => {"tag" => "title",
"optional" => 0 },
"optional" => 1 },
"affil" => {"tag" => "affiliation",
"optional" => 0 },
"shell" => {"tag" => "shell",
......@@ -178,6 +178,9 @@ sub DumpUser($)
# Pubkeys are special.
if (@keys) {
foreach my $key (@keys) {
next
if ($key =~ /^ssh-dss/);
print "<pubkeys>$key</pubkeys>\n";
}
}
......
#!/usr/bin/perl -w
#
# Copyright (c) 2005-2022 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
use English;
use strict;
use Getopt::Std;
use Data::Dumper;
use File::Temp qw(tempfile);
use JSON;
#
# Ask https://ipinfo.io/ for IP info
#
# select cc.country,sum(t.count) as count from
# (select country,count(distinct(uid)) as count from login_history
# where IP is not null and location is not null and portal='cloudlab'
# group by country,uid) as t
# left join ccodes.ccodes as cc on cc.code=t.country
# group by cc.country order by count desc;
#
#select region,sum(t.count) as count from
# (select region,count(distinct(uid)) as count from login_history
# where IP is not null and location is not null and country='US' and
# portal='cloudlab'
# group by region,uid) as t
#group by region order by count desc;
#
#
sub usage()
{
print "Usage: getipinfo [-n]\n";
print " getipinfo [-n] -p portal\n";
exit(1);
}
my $optlist = "ndp:";
my $impotent = 0;
my $debug = 0;
my $limit = 200;
#
# Configure variables
#
my $TB = "@prefix@";
my $token = "850749cc3b77dc";
my $URL = "http://ipinfo.io/batch?token=${token}";
my $CURL = "/usr/local/bin/curl";
# Load the Testbed support stuff.
use lib "@prefix@/lib";
use emdb;
use User;
use emutil;
# Protos
sub fatal($);
sub WriteResults($);
#
# Turn off line buffering on output
#
$| = 1;
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"n"})) {
$impotent++;
}
if (defined($options{"d"})) {
$debug++;
}
if (defined($options{"p"})) {
my $portal = $options{"p"};
if ($portal ne "cloudlab" && $portal ne "powder") {
fatal("Only cloudlab or powder portal please");
}
exit(WriteResults($portal));
}
#
# Find unmatched IPs in the login_history table and batch them up
# for the request.
#
my $count = 0;
while ($limit) {
$limit--;
my $query_result =
DBQueryFatal("select distinct IP from login_history ".
"where location is null and IP is not null ".
"limit 100");
last
if ($query_result->numrows == 0);
my %IPs = ();
while (my ($IP) = $query_result->fetchrow_array()) {
$IPs{$IP} = $IP;
}
if (keys(%IPs)) {
# Create a temporary files for curl
my ($fpIn, $fnameIn) = tempfile("/tmp/iplistInXXXXX", UNLINK => 0);
if (!defined($fpIn)) {
fatal("Could not create temp file for IPs");
}
my ($fpOut, $fnameOut) = tempfile("/tmp/iplistOutXXXXX", UNLINK => 0);
if (!defined($fpOut)) {
fatal("Could not create temp file for IPs");
}
foreach my $IP (keys(%IPs)) {
print $fpIn "$IP\n";
}
my $command =
"$CURL -s -o $fnameOut -XPOST --data-binary \@${fnameIn} $URL";
if ($debug) {
print "$command\n";
}
system($command);
if ($?) {
fatal("curl failure: '$command'\n");
}
my $json = emutil::ReadFile($fnameOut);
if (!$json || $json eq "") {
fatal("No json received");
}
my $results = eval { decode_json($json) };
if ($@) {
fatal("Could not decode json data");
}
if ($debug) {
print Dumper($results);
}
foreach my $IP (keys(%IPs)) {
my $ref = $results->{$IP};
if (!defined($ref)) {
print STDERR "No data for $IP\n";
next;
}
my $loc = $ref->{'loc'};
my $country = $ref->{'country'};
my $region = $ref->{'region'};
if (!defined($loc)) {
print STDERR "No data for $IP\n";
DBQueryFatal("update login_history set ".
" location='' ".
"where IP='$IP'");
next;
}
$count++;
if ($impotent) {
print "Would set $IP: $loc,$country,$region\n";
next;
}
else {
print "$IP: $loc,$country,$region\n";
DBQueryFatal("update login_history set ".
" location=" . DBQuoteSpecial($loc) . ", ".
" country=" . DBQuoteSpecial($country) . ", ".
" region=" . DBQuoteSpecial($region) . " ".
"where IP='$IP'");
}
}
unlink($fnameIn);
unlink($fnameOut);
}
print "$count IPs completed\n";
last
if (!$count);
sleep(10);
}
exit(0);
#
# Write per portal results files. Queries take a while.
#
sub WriteResults($)
{
my ($portal) = @_;
print "These queries take time, get a cup of coffee.\n";
my $query_result =
DBQueryFatal("select cc.country,sum(t.count) as count from ".
" (select country,count(distinct(uid)) as count ".
" from login_history ".
" where IP is not null and location is not null and ".
" portal='$portal' ".
" group by country,uid) as t ".
"left join ccodes.ccodes as cc on cc.code=t.country ".
"group by cc.country order by count desc");
my $fname = "world-counts-${portal}.csv";
print "Writing $fname ... \n";
if (open(WORLD, ">$fname")) {
print WORLD "name,count\n";
while (my ($country,$count) = $query_result->fetchrow_array()) {
next
if (!defined($country));
$country = "USA"
if ($country eq "United States");
print WORLD "$country,$count\n";
}
close(WORLD);
}
else {
fatal("Could not open $fname for writing: $!\n");
}
$query_result =
DBQueryFatal("select region,sum(t.count) as count from ".
" (select region,count(distinct(uid)) as count ".
" from login_history ".
" where IP is not null and location is not null and ".
" country='US' and portal='$portal' ".
" group by region,uid) as t ".
"group by region order by count desc");
$fname = "us-counts-${portal}.csv";
print "Writing $fname ... \n";
if (open(STATES, ">$fname")) {
print STATES "name,count\n";
while (my ($region,$count) = $query_result->fetchrow_array()) {
next
if (!defined($region));
print STATES "$region,$count\n";
}
close(STATES);
}
else {
fatal("Could not open $fname for writing: $!\n");
}
exit(0);
}
sub fatal($) {
my($mesg) = $_[0];
die("*** $0:\n".
" $mesg\n");
}
#!/usr/bin/perl -w
#
# Copyright (c) 2010-2011, 2013 University of Utah and the Flux Group.
# Copyright (c) 2010-2016, 2019 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
......@@ -78,8 +78,8 @@ my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
my $PGENISUPPORT = @PROTOGENI_SUPPORT@;
my $PORTAL_ENABLE = @PORTAL_ENABLE@;
my $PORTAL_PRIMARY= @PORTAL_ISPRIMARY@;
my $PEER_ENABLE = @PEER_ENABLE@;
my $PEER_PRIMARY = @PEER_ISPRIMARY@;
my $OURDOMAIN = "@OURDOMAIN@";
my $DUMPUSER = "$TB/sbin/dumpuser";
my $DUMPPROJ = "$TB/sbin/dumpproject";
......@@ -133,10 +133,10 @@ my $cmd = shift(@ARGV);
my $peername = shift(@ARGV);
my $peerurn;
if (! $PORTAL_ENABLE) {
fatal("Portal mode is not enabled");
if (! $PEER_ENABLE) {
fatal("Peer mode is not enabled");
}
if (! ($PORTAL_PRIMARY || $cmd eq "addpeer")) {
if (! ($PEER_PRIMARY || $cmd eq "addpeer")) {
fatal("You can only run addpeer on this boss");
}
......@@ -197,7 +197,7 @@ if ($cmd ne "addpeer") {
#
# All operations other then xlogin require locking to avoid a
# race with the portal_daemon.
# race with the peer_daemon.
#
if ($cmd ne "xlogin" && !$fromdaemon) {
while (TBScriptLock("portal_op", 0, 5) != TBSCRIPTLOCK_OKAY()) {
......@@ -340,6 +340,7 @@ sub AddUser(;$)
my $xmlgoo = emutil::ExecQuiet("$DUMPUSER $uid");
if ($?) {
print STDERR "$xmlgoo";
fatal("$DUMPUSER failed");
}
my $args = {"xmlstring" => $xmlgoo,
......
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2024 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -25,6 +25,7 @@ use strict;
use English;
use Getopt::Long qw(:config no_ignore_case);
use POSIX qw(strftime);
use Date::Parse;
#
# Load the Testbed support stuff.
......@@ -54,6 +55,7 @@ my $urn;
my $oldkeyfile;
my $authority;
my $notca = 0;
my $days = 2000;
my $include_uuid = 0;
my %optlist = ( "debug" => \$debug,
"password=s" => \$password,
......@@ -85,6 +87,7 @@ my $EMULAB_CERT = "$TB/etc/emulab.pem";
my $EMULAB_KEY = "$TB/etc/emulab.key";
my $OPENSSL = "/usr/bin/openssl";
my $WORKDIR = "$TB/ssl";
my $RANDFILE = "./.rnd";
my $SAVEUID = $UID;
my $certfile = $EMULAB_CERT;
my $keyfile = $EMULAB_KEY;
......@@ -205,6 +208,22 @@ if (!defined($email)) {
chdir("$WORKDIR") or
fatal("Could not chdir to $WORKDIR: $!");
#
# Some sillyness to deal with changes to .rnd file handling across
# versions of openssl.
#
if (! -e $RANDFILE) {
system("/bin/dd if=/dev/urandom of=${RANDFILE} bs=256 count=4");
if ($?) {
fatal("Could not generate $RANDFILE");
}
}
#
# Older versions of openssl ignore -rand option, but use this environment
# variable. New versions ignore the environment variable but use -rand.
#
$ENV{"RANDFILE"} = $RANDFILE;
#
# Need an index file, which is the openssl version of the DB.
#
......@@ -249,7 +268,9 @@ if (@urls) {
# unregistered OID 2.25.305821105408246119474742976030998643995
# (corresponding to UUID e61300a0-c4c5-11de-b14e-0002a5d5c51b)
# is used to indicate generic ProtoGENI XMLRPC servers.
print TEMP "authorityInfoAccess=2.25.305821105408246119474742976030998643995;URI:$_\n";
# print TEMP "authorityInfoAccess=2.25.305821105408246119474742976030998643995;URI:$_\n";
print TEMP
"authorityInfoAccess=2.25.305821105.408246119.47474297.603099864.3995;URI:$_\n";
}
}
......@@ -293,16 +314,23 @@ if( defined( $oldkeyfile ) ) {
#
# Create a client side private key and certificate request.
#
my $genopts =
my $genopts = " -rand $RANDFILE " .
($encrypted ? " -passout 'pass:${sh_password}' -des3 " : "");
system("$OPENSSL genrsa $genopts -out syscert_key.pem 1024")
== 0 or fatal("Could generate new key");
system("$OPENSSL req -text -new -config syscert.cnf ".
($encrypted ? " -passin 'pass:${sh_password}' " : "") .
" -key syscert_key.pem -out syscert_req.pem $outline") == 0
or fatal("Could not create certificate request");
my $output =
emutil::ExecQuiet("$OPENSSL genrsa $genopts -out syscert_key.pem 2048");
if ($?) {
print STDERR $output;
fatal("Could generate new key");
}
$output =
emutil::ExecQuiet("$OPENSSL req -text -new -config syscert.cnf ".
($encrypted ? " -passin 'pass:${sh_password}' " : "") .
" -key syscert_key.pem -out syscert_req.pem $outline");
if ($?) {
print STDERR $output;
fatal("Could not create certificate request");
}
}
#
......@@ -312,8 +340,45 @@ if( defined( $oldkeyfile ) ) {
#
my $startdate = POSIX::strftime("%y%m%d%H%M%SZ", gmtime(time() - 3600));
#
# Check the expiration on the CA cert, we do not want the new
# certificate to expire after the CA (signer) cert expires.
#
$UID = 0;
my $expires = `$OPENSSL x509 -enddate -noout -in $certfile`;
if ($?) {
fatal("Could not get expiration from $certfile");
}
if ($expires =~ /^notAfter=(.*)$/i) {
my $tmp = str2time($1);
if (!defined($tmp)) {
fatal("Could not convert $certfile expiration to time: $1");
}
$expires = $tmp;
}
else {
fatal("Could not parse $certfile expiration: $expires");
}
if ($expires < time()) {
fatal("$certfile certificate has expired!");
}
# If the CA expires in less then 30 days, grind to a halt.
my $daystoexpire = int(($expires - time()) / (3600 * 24));
if ($daystoexpire <= 30) {
fatal("Refusing to sign new certificate; the $certfile expires in less ".
"then 30 days!");
}
if ($debug) {
print "CA certificate expires in $daystoexpire days.\n";
}
if ($days > $daystoexpire) {
$days = $daystoexpire - 1;
print "Shortening certificate expiration to $days days\n";
}
system("$OPENSSL ca -batch -policy policy_sslxmlrpc -startdate $startdate ".
" -days $days ".
" -name CA_syscerts -config $CACONFIG ".
" -out syscert_cert.pem -cert $certfile -keyfile $keyfile ".
" -infiles syscert_req.pem $outline") == 0
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2024 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -24,6 +24,7 @@
use strict;
use English;
use Getopt::Std;
use Date::Parse;
#
# Load the Testbed support stuff.
......@@ -43,7 +44,7 @@ sub usage()
print("Usage: mkusercert [-d] [-o] [-r] [-g] [-p password] <user>\n");
exit(-1);
}
my $optlist = "dp:ogrc:";
my $optlist = "dp:ogrc:CPG";
my $debug = 0;
my $output = 0;
my $password = "";
......@@ -63,6 +64,7 @@ my $PGENISUPPORT= @PROTOGENI_SUPPORT@;
my $CONTROL = "@USERNODE@";
my $BOSSNODE = "@BOSSNODE@";
my $OU = "sslxmlrpc"; # orgunit
my $RANDFILE = "./.rnd";
# Locals
my $USERDIR = USERROOT();
......@@ -74,6 +76,8 @@ my $EMULAB_KEY = "$TB/etc/emulab.key";
my $OPENSSL = "/usr/bin/openssl";
my $KEYGEN = "/usr/bin/ssh-keygen";
my $ADDKEY = "$TB/sbin/addpubkey";
my $SSH = "$TB/bin/sshtb";
my $ACCOUNTPROXY= "$TB/sbin/accountsetup";
my $WORKDIR = "$TB/ssl";
my $SAVEUID = $UID;
......@@ -148,38 +152,8 @@ if (defined($options{"r"})) {
if (defined($options{"g"})) {
$geniflag = 1;
}
if (defined($options{"p"})) {
$password = $options{"p"};
#
# Make sure its all escaped since any printable char is allowed.
#
if ($password =~ /^([\040-\176]*)$/) {
$password = $1;
}
else {
die("Tainted argument: $password\n");
}
$db_password = DBQuoteSpecial($password);
$sh_password = $password;
$sh_password =~ s/\'/\'\\\'\'/g;
$sh_password = "$sh_password";
if (defined($options{"p"}) || defined($options{"P"})) {
$encrypted = 1;
$days = 365;
}
if (defined($options{"c"})) {
$old_password = $options{"c"};
#
# Make sure its all escaped since any printable char is allowed.
#
if ($old_password =~ /^([\040-\176]*)$/) {
$old_password = $1;
}
else {
die("Tainted argument: $old_password\n");
}
$old_password =~ s/\'/\'\\\'\'/g;
}
if (@ARGV != 1) {
usage();
......@@ -202,6 +176,15 @@ else {
die("Tainted argument: $user\n");
}
# Figure out what version of OpenSSL
my $sslversion = `$OPENSSL version`;
if ($sslversion =~ /^OpenSSL\s+(\d+)\.(\d+)\./) {
$sslversion = "$1.$2";
} else {
print STDERR "Cannot parse OpenSSL version, assuming 1.0\n";
$sslversion = "1.0";
}
# Map target user to object.
my $target_user = User->Lookup($user);
if (! defined($target_user)) {
......@@ -214,6 +197,61 @@ if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
if (defined($options{"p"}) || defined($options{"P"})) {
if (defined($options{"p"})) {
$password = $options{"p"};
}
elsif ($target_user->SSLPassPhrase(1, \$password)) {
if (defined($options{"G"})) {
$password = substr(TBGenSecretKey(), 0, 12);
if (!defined($password) || $password eq "") {
fatal("Could not generate a random passphrase for -P -G");
}
}
else {
fatal("No stored passphrase for -P option");
}
}
#
# Make sure its all escaped since any printable char is allowed.
#
if ($password =~ /^([\040-\176]*)$/) {
$password = $1;
}
else {
die("Tainted argument: $password\n");
}
$db_password = DBQuoteSpecial($password);
$sh_password = $password;
$sh_password =~ s/\'/\'\\\'\'/g;
$sh_password = "$sh_password";
}
# This option is for changing the passphrase on existing key.
# Might need the target user (-C options).
if (defined($options{"c"}) || defined($options{"C"})) {
if (defined($options{"c"})) {
$old_password = $options{"c"};
}
elsif ($target_user->SSLPassPhrase(1, \$old_password)) {
$old_password = undef;
}
if (defined($old_password)) {
#
# Make sure its all escaped since any printable char is allowed.
#
if ($old_password =~ /^([\040-\176]*)$/) {
$old_password = $1;
}
else {
fatal("Tainted password: $old_password");
}
$old_password =~ s/\'/\'\\\'\'/g;
}
}
#
# CD to the workdir, and then serialize on the lock file since there is
# some shared goop that the ssl tools muck with (serial number, index, etc.).
......@@ -224,6 +262,22 @@ chdir("$WORKDIR") or
TBScriptLock("mkusercert") == 0 or
fatal("Could not get the lock!");
#
# Some sillyness to deal with changes to .rnd file handling across
# versions of openssl.
#
if (! -e $RANDFILE) {
system("/bin/dd if=/dev/urandom of=${RANDFILE} bs=256 count=4");
if ($?) {
fatal("Could not generate $RANDFILE");
}
}
#
# Older versions of openssl ignore -rand option, but use this environment
# variable. New versions ignore the environment variable but use -rand.
#
$ENV{"RANDFILE"} = $RANDFILE;
#
# Create a client side cert. Reuse the original key if are told to,
# and it actually exists, and the password is valid.
......@@ -339,7 +393,7 @@ sub CreateNewCert() {
# (corresponding to UUID e61300a0-c4c5-11de-b14e-0002a5d5c51b)
# is used to indicate generic ProtoGENI XMLRPC servers.
print TEMP
"authorityInfoAccess=2.25.305821105408246119474742976030998643995;URI:$url\n";
"authorityInfoAccess=2.25.305821105.408246119.47474297.603099864.3995;URI:$url\n";
}
print TEMP "\n";
......@@ -370,22 +424,58 @@ sub CreateNewCert() {
# Create a client side private key and certificate request.
#
if (!$reusekey) {
my $genopts =
my $genopts = " -rand $RANDFILE " .
($encrypted ? " -passout 'pass:${sh_password}' -des3 " : "");
system("$OPENSSL genrsa $genopts -out usercert_key.pem 1024")
== 0 or fatal("Could generate new key");
system("$OPENSSL genrsa $genopts -out usercert_key.pem 2048")
== 0 or fatal("Could not generate new key");
}
my $reqopts = ($encrypted ? "-passin 'pass:${sh_password}' " : "");
system("$OPENSSL req $reqopts -new -config usercert.cnf ".
"-key usercert_key.pem -out usercert_req.pem")
== 0 or fatal("Could not create certificate request");
#
# Check the expiration on the CA cert, we do not want the new
# certificate to expire after the CA (signer) cert expires.
#
$UID = 0;
my $expires = `$OPENSSL x509 -enddate -noout -in $EMULAB_CERT`;
if ($?) {
fatal("Could not get expiration from $EMULAB_CERT");
}
if ($expires =~ /^notAfter=(.*)$/i) {
my $tmp = str2time($1);
if (!defined($tmp)) {
fatal("Could not convert CA expiration to time: $1");
}
$expires = $tmp;
}
else {
fatal("Could not parse CA expiration: $expires");
}
if ($expires < time()) {
fatal("CA certificate has expired!");
}
# If the CA expires in less then 30 days, grind to a halt.
my $daystoexpire = int(($expires - time()) / (3600 * 24));
if ($daystoexpire <= 30) {
fatal("Refusing to sign new certificate; the CA expires in less ".
"then 30 days!");
}
if ($debug) {
print "CA certificate expires in $daystoexpire days.\n";
}
if ($days > $daystoexpire) {
$days = $daystoexpire - 1;
print "Shortening certificate expiration to $days days\n";
}
#
# Sign the client cert request, creating a client certificate.
#
$UID = 0;
system("$OPENSSL ca -batch -policy policy_sslxmlrpc -days $days ".
" -name CA_usercerts -config $CACONFIG ".
" -out usercert_cert.pem -cert $EMULAB_CERT -keyfile $EMULAB_KEY ".
......@@ -397,7 +487,11 @@ sub CreateNewCert() {
# We store the DN in the DB too, for creating the crl index file without
# having to reparse all the certs.
#
my $DN = `$OPENSSL x509 -subject -noout -in usercert_cert.pem`;
my $args = "-subject -noout";
if ($sslversion > 1.0) {
$args .= " -nameopt=compat";
}
my $DN = `$OPENSSL x509 $args -in usercert_cert.pem`;
chomp($DN);
if ($DN =~ /^subject=\s*(\/[-\/\=\w\@\.,\s]+)$/) {
$DN = $1;
......@@ -571,32 +665,15 @@ else {
CreateNewCert();
}
#
# Copy the certificate to the users .ssl directory.
#
# Drop the file into the user .ssl directory.
my $ssldir = "$USERDIR/$user_uid/.ssl";
if (! -d $ssldir) {
mkdir($ssldir, 0700) or
fatal("Could not mkdir $ssldir: $!");
chown($user_number, $default_groupgid, $ssldir)
or fatal("Could not chown $ssldir: $!");
}
my $target;
if ($encrypted) {
$target = "$ssldir/encrypted.pem";
}
else {
$target = "$ssldir/emulab.pem";
}
system("cp -f usercert.pem $target") == 0
or fatal("Could not copy usercert.pem to $target");
chown($user_number, $default_groupgid, "$target")
or fatal("Could not chown $target: $!");
$UID = $EUID;
system("$SSH -host $CONTROL ".
"'$ACCOUNTPROXY dropfile $user_uid $default_groupgid 0600 $ssldir ".
($encrypted ? "encrypted.pem" : "emulab.pem") . "' < usercert.pem") == 0
or fatal("Could not copy certificate file to $CONTROL");
$UID = $SAVEUID;
if ($encrypted) {
#
......@@ -605,19 +682,16 @@ if ($encrypted) {
#
system("$OPENSSL pkcs12 -export -in usercert.pem -des3 ".
"-passin 'pass:${sh_password}' -passout 'pass:${sh_password}' ".
"-out usercert.p12 -rand ./.rnd")
"-out usercert.p12 -rand $RANDFILE")
== 0 or fatal("Could not create usercert.p12");
$target = "$ssldir/encrypted.p12";
system("cp -f usercert.p12 $target") == 0
or fatal("Could not copy usercert.p12 to $target");
chown($user_number, $default_groupgid, "$target")
or fatal("Could not chown $target: $!");
chmod(0600, $target)
or fatal("Could not chmod $target: $!");
# Drop the file into the user .ssl directory.
$UID = $EUID;
system("$SSH -host $CONTROL ".
"'$ACCOUNTPROXY dropfile $user_uid $default_groupgid 0600 $ssldir ".
"encrypted.p12' < usercert.p12")
== 0 or fatal("Could not copy .p12 file to $CONTROL");
$UID = $SAVEUID;
goto skipssh
if ($target_user->IsNonLocal());
......@@ -626,7 +700,7 @@ if ($encrypted) {
# Create an SSH key from the private key. Mostly for geni users,
# who tend not to know how to do such things.
#
my $pemfile = "$ssldir/encrypted.pem";
my $pemfile = "usercert.pem";
my $sshdir = "$USERDIR/$user_uid/.ssh";
my $pphrase = User::escapeshellarg($password);
# This comment is special. It functions as a cross table reference
......@@ -640,10 +714,12 @@ if ($encrypted) {
#
# The key format is identical to openssh, so just copy it over.
#
system("/bin/cp usercert_key.pem $sshdir/encrypted.key") == 0
or fatal("Could not copy private key to $sshdir/encrypted.key: $!");
chmod(0600, "$sshdir/encrypted.key")
or fatal("Could not chmod $sshdir/encrypted.key: $!");
$UID = $EUID;
system("$SSH -host $CONTROL '$ACCOUNTPROXY ".
" dropfile $user_uid $default_groupgid 0600 $sshdir ".
" encrypted.key' < usercert_key.pem")
== 0 or fatal("Could not copy ssh key file to $CONTROL");
$UID = $SAVEUID;
#
# No need to do this when just changing the passphrase.
......@@ -652,18 +728,33 @@ if ($encrypted) {
#
# Extract a public key.
#
system("$KEYGEN -P $pphrase -y -f $pemfile > $sshdir/encrypted.pub")
system("$KEYGEN -P $pphrase -y -f $pemfile > encrypted.pub")
== 0
or fatal("Could not extract ssh pubkey from $pemfile");
$UID = $EUID;
system("$SSH -host $CONTROL '$ACCOUNTPROXY ".
" dropfile $user_uid $default_groupgid 0644 $sshdir ".
" encrypted.pub' < encrypted.pub")
== 0 or fatal("Could not copy ssh pub key file to $CONTROL");
$UID = $SAVEUID;
#
# Need to remove the current ssh pubkey from the database, but we just
# updated the new serial number so the comment is no longer valid for
# lookup.
#
$target_user->DeleteSSLCertSSHKey();
#
# And add the pubkey to the DB. Mark it as nodelete and that it should
# remove existing key with same comment.
# And add the pubkey to the DB. Mark it as nodelete and
# as internal since we do not want to delete these except when
# creating a new certificate.
#
$EUID = $UID;
system("$ADDKEY -s -N -R -C $comment -u $user_uid ".
" -f $sshdir/encrypted.pub")
== 0 or fatal("Could not add pubkey $sshdir/encrypted.pub");
system("$ADDKEY -s -N -I -C $comment -u $user_uid ".
" -f encrypted.pub")
== 0 or fatal("Could not add ssh pubkey");
}
skipssh:
}
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2011, 2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2021, 2024 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -42,8 +42,10 @@ my $debug = 0;
my $nonlocal= 0;
my $impotent= 0;
my $silent = 0;
my $genesis;
my $portal;
my $resend;
my %licenses = ();
my %nsf_awards = ();
#
# Configure variables
......@@ -53,6 +55,7 @@ my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $TBBASE = "@TBBASE@";
my $TBWWW = "@TBWWW@";
my $LICENSES = "$TB/sbin/manage_licenses";
#
# This script is setuid, so please do not run it as root. Hard to track
......@@ -82,6 +85,7 @@ use libdb;
use libtestbed;
use Project;
use User;
use Brand;
# Protos
sub fatal($);
......@@ -204,8 +208,13 @@ my %optional = ("newuser_xml" => "newuser_xml",
"members" => "num_members",
"ron" => "num_ron",
"plab" => "num_pcplab",
"class" => "forClass",
"whynotpublic" => "public_whynot",
"user_interface" => "default_user_interface");
"user_interface" => "default_user_interface",
"nsf_funded" => "nsf_funded",
"nsf_awards" => "nsf_awards",
"nsf_supplement" => "nsf_supplement",
);
#
# This script is not audited cause we want the output to be sent back
......@@ -224,11 +233,29 @@ fatal($@)
if ($@);
# APT flag. Notice and delete.
if (exists($xmlparse->{'attribute'}->{"genesis"})) {
$genesis = $xmlparse->{'attribute'}->{"genesis"}->{'value'};
delete($xmlparse->{'attribute'}->{"genesis"});
if (! ($genesis eq "aptlab.net" || $genesis eq "cloudlab")) {
fatal("Bad genesis: $genesis");
if (exists($xmlparse->{'attribute'}->{"portal"})) {
$portal = $xmlparse->{'attribute'}->{"portal"}->{'value'};
delete($xmlparse->{'attribute'}->{"portal"});
my $brand = Brand->Create($portal);
if (!defined($brand)) {
fatal("Bad portal: $portal");
}
}
# Licenses. Save for later, but need to delete.
foreach my $key (keys(%{ $xmlparse->{'attribute'} })) {
if ($key =~ /^license_([-\w]+)$/) {
my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'};
my $name = $1;
if (lc($value) eq "yes") {
system("$LICENSES show $name");
if ($?) {
fatal("Invalid license name: $name");
}
$licenses{$name} = $name;
print "requested license $name\n";
}
delete($xmlparse->{'attribute'}->{"$key"});
}
}
......@@ -236,7 +263,6 @@ if (exists($xmlparse->{'attribute'}->{"genesis"})) {
# Make sure all the required arguments were provided.
#
foreach my $key (keys(%required)) {
fatal("Missing required attribute '$key'")
if (! exists($xmlparse->{'attribute'}->{"$key"}));
}
......@@ -353,16 +379,45 @@ if (exists($newproj_args{'newuser_xml'})) {
$newproj_args{'head_uid'} = $2;
}
# NSF award. Only one allowed. Change later.
if (exists($newproj_args{'nsf_funded'})) {
my $funded = $newproj_args{'nsf_funded'};
if ($funded == 1) {
my $award = $newproj_args{'nsf_awards'};
my $sup = $newproj_args{'nsf_supplement'};
$nsf_awards{$award} = $sup;
}
delete($newproj_args{'nsf_funded'});
delete($newproj_args{'nsf_awards'});
delete($newproj_args{'nsf_supplement'});
}
if (exists($newproj_args{'forClass'}) && $newproj_args{'forClass'} == 1) {
$newproj_args{'shared_reservations'} = 1;
}
else {
$newproj_args{'shared_reservations'} = 0;
}
#
# Now do special checks.
#
UserError("Project already exists; pick another name!")
if (Project->Lookup($newproj_args{'pid'}));
my $leader = User->Lookup($newproj_args{'head_uid'});
UserError("Project leader does not exist!")
if (!defined($leader));
#
# Need a big lock to avoid double click errors, too much stuff going on in
# Project->Create() to lock tables. This lock is automatically dropped.
#
my $lock_result = DBQueryWarn("select GET_LOCK('NewProjectLock', 60)");
if (!$lock_result ||
!$lock_result->numrows) {
fatal("Could not get the new project lock for a long time");
}
UserError("Project already exists; pick another name!")
if (Project->Lookup($newproj_args{'pid'}));
exit(0)
if ($impotent);
......@@ -374,15 +429,35 @@ exit(0)
my $new_pid = $newproj_args{'pid'};
delete($newproj_args{'pid'});
delete($newproj_args{'head_uid'});
# Genesis (APT or Cloud)
$newproj_args{'genesis'} = $genesis
if (defined($genesis));
# Portal (Emulab, APT, CloudLab, or PhantomNet)
$newproj_args{'portal'} = $portal
if (defined($portal));
my $newproj = Project->Create($new_pid, $leader, \%newproj_args);
if (!defined($newproj)) {
fatal("Could not create new project!");
}
my $new_idx = $newproj->pid_idx();
DBQueryWarn("select RELEASE_LOCK('NewProjectLock')");
#
# Add any licenses.
#
if (keys(%licenses)) {
foreach my $name (keys(%licenses)) {
system("$LICENSES require $name $new_pid");
if ($?) {
fatal("Invalid license name: $name");
}
}
}
# And NSF awards
if (keys(%nsf_awards)) {
foreach my $award (keys(%nsf_awards)) {
my $sup = $nsf_awards{$award};
$newproj->AddNSFAward($award, $sup);
}
}
#
# See if we are in an initial Emulab setup. If so, no email sent.
......
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2022 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -36,13 +36,17 @@ sub usage()
print("Usage: newuser [-s] -t <type> <xmlfile>\n");
exit(-1);
}
my $optlist = "dt:nsp";
my $optlist = "dt:nsprGP";
my $debug = 0;
my $impotent= 0;
my $type = "";
my $silent = 0;
my $portal = 0;
my $genesis;
my $relaxed = 0;
my $dopass = 0;
my $genuser = 0;
my $portal;
my $passhash;
my @keyfiles = ();
#
......@@ -87,6 +91,8 @@ use libdb;
use libtestbed;
use User;
use EmulabConstants();
use emutil;
use Brand;
# Protos
sub fatal($);
......@@ -116,6 +122,15 @@ if (defined($options{"s"})) {
if (defined($options{"t"})) {
$type = $options{"t"};
}
if (defined($options{"r"})) {
$relaxed = 1;
}
if (defined($options{"P"})) {
$dopass = 1;
}
if (defined($options{"G"})) {
$genuser = 1;
}
if (@ARGV != 1) {
usage();
}
......@@ -175,6 +190,13 @@ my %optional = ("uid" => "uid",
"pubkey" => undef,
"pubkeys" => undef);
my %relaxed_fields = ("affiliation_abbreviation" => 1,
"phone" => 1,
"title" => 1,
"address" => 1,
"zip" => 1,
"wikiname" => 1);
#
# These are required for most users, but are optional for wiki-only users
#
......@@ -211,34 +233,36 @@ print STDERR Dumper($xmlparse)
if ($debug);
# APT flag. Notice and delete.
if (exists($xmlparse->{'attribute'}->{"genesis"})) {
$genesis = $xmlparse->{'attribute'}->{"genesis"}->{'value'};
delete($xmlparse->{'attribute'}->{"genesis"});
if (! ($genesis eq "aptlab.net" || $genesis eq "cloudlab")) {
fatal("Bad genesis: $genesis");
if (exists($xmlparse->{'attribute'}->{"portal"})) {
$portal = $xmlparse->{'attribute'}->{"portal"}->{'value'};
delete($xmlparse->{'attribute'}->{"portal"});
my $brand = Brand->Create($portal);
if (!defined($brand)) {
fatal("Bad portal: $portal");
}
# Remove these, we do not require them on the APT path.
delete($required{"affiliation_abbreviation"});
delete($required{"phone"});
delete($required{"title"});
delete($required{"address"});
delete($required{"zip"});
delete($required{"wikiname"});
$relaxed = 1;
}
#
# Make sure all the required arguments were provided.
#
foreach my $key (keys(%required)) {
next
if ($relaxed && exists($relaxed_fields{$key}));
fatal("Missing required attribute '$key'")
if (! exists($xmlparse->{'attribute'}->{"$key"}));
}
#
# Always delete this. Used by the portal code but we ignore it.
# Used by the portal code but we ignore it unless explicitly told
# not to.
#
delete($xmlparse->{'attribute'}->{"passhash"})
if (exists($xmlparse->{'attribute'}->{"passhash"}));
if (exists($xmlparse->{'attribute'}->{"passhash"})) {
if ($dopass) {
$passhash = $xmlparse->{'attribute'}->{"passhash"}->{'value'};
}
delete($xmlparse->{'attribute'}->{"passhash"});
}
#
# We build up an array of arguments to pass to User->Create() as we check
......@@ -326,7 +350,8 @@ if ($WIKISUPPORT) {
# And the email address has to be unique.
#
UserError("Email address already in use; please pick another!")
if ($newuser_args{'usr_email'} ne $TBOPS &&
if (!$genuser &&
$newuser_args{'usr_email'} ne $TBOPS &&
User->LookupByEmail($newuser_args{'usr_email'}));
#
......@@ -360,7 +385,7 @@ else {
}
fatal("Checkpass failed with $?");
}
$newuser_args{'usr_pswd'} = crypt($pswd, "\$1\$" . substr(time(), 0, 8));
$newuser_args{'usr_pswd'} = PassWordHash($pswd);
}
#
......@@ -433,9 +458,9 @@ if (exists($newuser_args{'uid'})) {
$new_uid = $newuser_args{'uid'};
delete($newuser_args{'uid'});
}
# Genesis (APT or Cloud)
$newuser_args{'genesis'} = $genesis
if (defined($genesis));
# Portal (Emulab, APT, CloudLab or PhantomNet)
$newuser_args{'portal'} = $portal
if (defined($portal));
#
# The type modifier comes in on the command line since this is available
......@@ -459,9 +484,12 @@ my $newuser = User->Create($new_uid, $flags, \%newuser_args);
if (!defined($newuser)) {
fatal("Could not create new user!");
}
if (defined($genesis)) {
if (defined($portal)) {
$newuser->SetStatus(USERSTATUS_UNAPPROVED());
}
if (defined($passhash)) {
$newuser->SetPassword($passhash);
}
my $key = $newuser->verify_key();
my $usr_uid = $newuser->uid();
my $usr_idx = $newuser->uid_idx();
......@@ -474,9 +502,10 @@ my $usr_email = $newuser->email();
my $firstinitstate;
if (TBGetSiteVar("general/firstinit/state", \$firstinitstate)) {
#
# The first user gets admin status and some extra groups, etc.
# These initial users gets admin status and some extra groups, etc.
#
if ($firstinitstate eq "createproject") {
if ($firstinitstate eq "createproject" &&
$new_uid ne EmulabConstants::GENIUSER()) {
DBQueryFatal("update users set ".
" admin=1,status='". $User::USERSTATUS_UNAPPROVED . "' " .
"where uid_idx='$usr_idx'");
......@@ -530,7 +559,7 @@ SENDMAIL("$usr_name '$usr_uid' <$usr_email>",
"Testbed Operations\n",
"$TBAPPROVAL",
"Bcc: $TBAUDIT")
if (!($silent || defined($genesis)));
if (!($silent || defined($portal)));
#
# Do we have a keyfile? If so, rerun addpubkey for real now that the
......
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2024 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
use English;
use Getopt::Std;
#
# This script will generate new Emulab keys for all users who do not
# already have them or if the cert is expired.
#
sub usage()
{
print("Usage: regencerts [-n]\n");
exit(-1);
}
my $optlist = "n";
my $impotent = 0;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $OURDOMAIN = "@OURDOMAIN@";
my $MKCERT = "$TB/sbin/mkusercert";
my $CHECKQUOTA = "$TB/sbin/checkquota";
my $SUDO = "/usr/local/bin/sudo";
my $PROTOUSER = "elabman";
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libaudit;
use libdb;
use libtestbed;
use emutil;
use libEmulab;
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"n"})) {
$impotent = 1;
}
#
# Grab all active/frozen users.
#
my $query_result =
DBQueryFatal("select u.uid,u.uid_idx,encrypted,c.idx,s.last_activity ".
" from user_sslcerts as c ".
"left join users as u on u.uid_idx=c.uid_idx ".
"left join user_stats as s on s.uid_idx=u.uid_idx ".
"where webonly=0 and wikionly=0 and ".
" (u.status='active' or u.status='frozen') and ".
" revoked is null ".
"order by u.uid");
while (my ($uid,$uid_idx,$encrypted) = $query_result->fetchrow_array()) {
system("$CHECKQUOTA $uid");
if ($?) {
print STDERR "User $uid over quota, not generating certificate!\n";
next;
}
if (!$encrypted) {
print "Regenerating unencrypted certificate for $uid.\n";
if (!$impotent) {
system("$SUDO -u $PROTOUSER $MKCERT $uid_idx");
if ($?) {
print STDERR "Could not regenerate unencrypted cert ".
"for $uid ($uid_idx)\n";
}
}
}
else {
print "Regenerating encrypted certificate for $uid.\n";
if (!$impotent) {
system("$SUDO -u $PROTOUSER $MKCERT -P $uid_idx");
if ($?) {
print STDERR "Could not regenerate encrypted cert ".
"for $uid ($uid_idx)\n";
}
}
}
}
exit(0);
This diff is collapsed.
#
# Copyright (c) 2002-2012 University of Utah and the Flux Group.
# Copyright (c) 2002-2017, 2020 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -27,6 +27,7 @@ SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
OBJDIR = ..
SUBDIR = apache
PGENISUPPORT = @PROTOGENI_SUPPORT@
SYSTEM := $(shell uname -s)
ifeq ($(SYSTEM),FreeBSD)
......@@ -41,10 +42,16 @@ include $(OBJDIR)/Makeconf
OPS_FILES = httpd.conf-ops php.ini
CONFIG_FILES = httpd.conf $(OPS_FILES)
ifeq ($(PGENISUPPORT),1)
CONFIG_GENI = httpd-geni.conf
CONFIG_FILES += $(CONFIG_GENI)
endif
#
# Move to Apache 22 ...
#
ifeq ($(APACHE_VERSION),24)
# For VPATH.
MOSTLY_SRCDIRS = ${SRCDIR}/v24
SCRIPT_HACK = 0
else
ifeq ($(APACHE_VERSION),22)
# For VPATH.
MOSTLY_SRCDIRS = ${SRCDIR}/v2
......@@ -52,6 +59,7 @@ SCRIPT_HACK = 0
else
MOSTLY_SRCDIRS = ${SRCDIR}/v1
endif
endif
INSTALL_PHP_CONFIG = /usr/local/etc
#
......@@ -135,20 +143,29 @@ ifeq ($(SCRIPT_HACK),1)
$(INSTALL) -m 755 $(SRCDIR)/apache-emulab /usr/local/etc/rc.d/apache.sh
endif
ifeq ($(APACHE_VERSION),22)
install: install-dirs install-scripts httpd.conf
ifeq ($(APACHE_VERSION),1.3)
install: install-dirs install-scripts httpd.conf.fixed
$(INSTALL_DATA) httpd.conf.fixed $(INSTALL_APACHE_CONFIG)/httpd.conf
control-install: install-dirs install-scripts httpd.conf-ops.fixed
$(INSTALL_DATA) httpd.conf-ops.fixed $(INSTALL_APACHE_CONFIG)/httpd.conf
else
install: install-dirs install-scripts httpd.conf pgeni-install
$(INSTALL_DATA) httpd.conf $(INSTALL_APACHE_CONFIG)/httpd.conf
install-utah-nets:
$(INSTALL_DATA) $(SRCDIR)/utah-nets.conf \
$(INSTALL_APACHE_CONFIG)/utah-nets.conf
control-install: install-dirs install-scripts httpd.conf-ops
$(INSTALL_DATA) httpd.conf-ops $(INSTALL_APACHE_CONFIG)/httpd.conf
utah: httpd.conf.utah httpd.conf-ops.utah
else
install: install-dirs install-scripts httpd.conf.fixed
$(INSTALL_DATA) httpd.conf.fixed $(INSTALL_APACHE_CONFIG)/httpd.conf
pgeni-install: $(CONFIG_GENI)
ifeq ($(PGENISUPPORT),1)
$(INSTALL_DATA) httpd-geni.conf $(INSTALL_APACHE_CONFIG)/httpd-geni.conf
endif
control-install: install-dirs install-scripts httpd.conf-ops.fixed
$(INSTALL_DATA) httpd.conf-ops.fixed $(INSTALL_APACHE_CONFIG)/httpd.conf
utah: httpd.conf.utah httpd.conf-ops.utah
endif
install-php-ini: php.ini
......
......@@ -32,3 +32,8 @@ error_log = "@prefix@/log/php-errors.log"
; How stupid is this?
;
date.timezone = @OURTIMEZONE@
;
; This is needed now to avoid SSL verify errors when talking to ourself.
[openssl]
openssl.cafile = @prefix@/etc/emulab.pem
# Flux
allow from 155.98.60.
# Flux VPN
allow from 155.98.63.
# Emulab
allow from 155.98.32.
# APT boss
allow from 128.110.100.4
# Wisc boss
allow from 128.104.222.9
# Cloudlab boss
allow from 128.110.156.4
# Clemson boss
allow from 130.127.132.51
# Utah University VPN
allow from 155.101.240.
allow from 155.101.241.
allow from 155.101.242.
# Leigh Comcast
allow from 24.21.143.27