diff --git a/account/addpubkey.in b/account/addpubkey.in
index 750b3a0e7db495382f581a6aea0db46a19e0e6aa..bb9763e3ee9599f52fe146b0030b44422899a2c5 100644
--- a/account/addpubkey.in
+++ b/account/addpubkey.in
@@ -591,11 +591,11 @@ sub GenerateKeyFile()
     }
     close(AUTHKEYS);
 
-    $UID = 0;
+    $EUID = $UID = 0;
     system("$SSH -host $CONTROL ".
 	   "'$ACCOUNTPROXY dropfile $user_uid $user_gid 0600 $sshdir ".
 	   "authorized_keys' < $outfile");
-    $UID = $SAVEUID;
+    $EUID = $UID = $SAVEUID;
 
     if ($?) {
 	unlink($outfile);
diff --git a/account/mkusercert.in b/account/mkusercert.in
index acd871998bed3bde729a3819644de904a3a207b0..42e8e75c2d19a8eee7e0c566b57029fd23015e77 100755
--- a/account/mkusercert.in
+++ b/account/mkusercert.in
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -wT
 #
-# Copyright (c) 2000-2018 University of Utah and the Flux Group.
+# Copyright (c) 2000-2020 University of Utah and the Flux Group.
 # 
 # {{{EMULAB-LICENSE
 # 
@@ -175,6 +175,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)) {
@@ -461,7 +470,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;
diff --git a/clientside/configure b/clientside/configure
index 7e966ac14e77f8704f98573065e149c872588c9d..8b1771c2ef8c1827c3cd94dbe1623a05cd7517fa 100755
--- a/clientside/configure
+++ b/clientside/configure
@@ -4251,6 +4251,9 @@ if test ! -d "$PYTHON_PATH/include"; then
     PYTHON_PATH=`dirname ${PYTHON_PATH}`
 fi
 PYTHON_INCLUDE="${PYTHON_PATH}/include/python${PYTHON_VERSION}"
+if test ! -d "$PYTHON_INCLUDE" -a -d "${PYTHON_INCLUDE}m"; then
+      PYTHON_INCLUDE="${PYTHON_INCLUDE}m"
+fi
 CPPFLAGS="${CPPFLAGS} -I${PYTHON_INCLUDE}"
 
 
diff --git a/clientside/configure.ac b/clientside/configure.ac
index 210a3e173be1ec329a0b20c3099fb76a3e83340b..7bbaab3c5fe41f88a4cc679b970db6e849af2883 100644
--- a/clientside/configure.ac
+++ b/clientside/configure.ac
@@ -217,6 +217,9 @@ if test ! -d "$PYTHON_PATH/include"; then
     PYTHON_PATH=`dirname ${PYTHON_PATH}`
 fi
 PYTHON_INCLUDE="${PYTHON_PATH}/include/python${PYTHON_VERSION}"
+if test ! -d "$PYTHON_INCLUDE" -a -d "${PYTHON_INCLUDE}m"; then
+      PYTHON_INCLUDE="${PYTHON_INCLUDE}m"
+fi
 CPPFLAGS="${CPPFLAGS} -I${PYTHON_INCLUDE}"
 AC_CHECK_HEADERS([Python.h],
 		 [],
diff --git a/clientside/lib/event/GNUmakefile.in b/clientside/lib/event/GNUmakefile.in
index 3ca7f8bdf7c7a614df47333e9faa6361873ea29b..28cd6b798add958a8e8d565c54274683ecffc01a 100644
--- a/clientside/lib/event/GNUmakefile.in
+++ b/clientside/lib/event/GNUmakefile.in
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2000-2016, 2018 University of Utah and the Flux Group.
+# Copyright (c) 2000-2020 University of Utah and the Flux Group.
 # 
 # {{{EMULAB-LICENSE
 # 
@@ -39,8 +39,8 @@ PUBSUBLIBS_R    = -L/usr/local/lib -lpubsub_r
 #			/home/stoller/pubsub/.libs/libpubsub.so
 #PUBSUBLIBS_R    = -rpath /home/stoller/pubsub/.libs/ \
 #			/home/stoller/pubsub/.libs/libpubsub_r.so
-PUBSUBLIBS     += -lssl -lcrypto -lcrypt
-PUBSUBLIBS_R   += -lssl -lcrypto -lcrypt
+PUBSUBLIBS     += -L/usr/lib -lssl -lcrypto -lcrypt
+PUBSUBLIBS_R   += -L/usr/lib -lssl -lcrypto -lcrypt
 CFLAGS         += -DWITHSSL
 else
 PUBSUBLIBS     += -L/usr/local/lib -lpubsub -lcrypt
diff --git a/clientside/tip/tiptunnel.c b/clientside/tip/tiptunnel.c
index b27e716aed38a91ea1afd27ff1033fe01669ec16..0de15beb9d3da621c673171d845fa9bc95b0f234 100644
--- a/clientside/tip/tiptunnel.c
+++ b/clientside/tip/tiptunnel.c
@@ -485,7 +485,7 @@ void loadAcl( const char * filename )
   
   bzero( &key, sizeof( key ) );
 
-  while (fscanf(aclFile, "%s %s\n", &b1, &b2) != EOF) {
+  while (fscanf(aclFile, "%255s %255s\n", b1, b2) != EOF) {
     if ( strcmp(b1, "host:") == 0 || strcmp(b1, "server:") == 0 ) {
       if (!uploadmode)
 	hostname = strdup( b2 );
@@ -594,7 +594,7 @@ void doAuthenticate()
 
 void doCreateTunnel()
 {
-  int i;
+  unsigned int i;
   struct sockaddr_in name;
 
   tunnelSock = socket(AF_INET, SOCK_STREAM, 0);
@@ -889,13 +889,15 @@ void sslConnect()
   // X509_digest( &peer, EVP_sha() , digest, &len );
 
   //X509_digest( peer, EVP_md5(), digest, &len );
-  
-  X509_digest( peer, EVP_sha(), digest, &len );
+  //X509_digest( peer, EVP_sha(), digest, &len );
+
+  X509_digest( peer, EVP_sha1(), digest, &len );
 
   X509_free( peer );
 
   for (i = 0; i < len; i++) {
-    sprintf( digestHex + (i * 2), "%02x", (unsigned int) digest[i] );
+	  sprintf( (char *)digestHex + (i * 2), "%02x",
+		   (unsigned int) digest[i] );
   }
 
   if (debug) {
@@ -905,7 +907,7 @@ void sslConnect()
 	   digestHex );
   }
 
-  if (/*!localmode && */0 != strcmp( certString, digestHex )) {
+  if (/*!localmode && */0 != strcmp( certString, (char *)digestHex )) {
     fprintf(stderr, 
 	    "Server does not have certificate described in ACL:\n"
 	    "ACL's  cert digest: %s\n" 
diff --git a/clientside/tmcc/common/config/rc.mkelab b/clientside/tmcc/common/config/rc.mkelab
index 4b5ef7cc23e9da78c77508c09e029d5b9b61525c..d0df50de5da59656c7865a59529676621f669b5a 100755
--- a/clientside/tmcc/common/config/rc.mkelab
+++ b/clientside/tmcc/common/config/rc.mkelab
@@ -154,6 +154,10 @@ my $ZPOOLCMD = "/sbin/zpool";
 my $ZFSCMD =   "/sbin/zfs";
 my $ZFSPOOL =  "emulab";
 
+# Ick to deal with python differences
+my $PYBIN2 = "/usr/local/bin/python2";
+my $PYBIN3 = "/usr/local/bin/python3.7";
+
 #
 # Support for Windows nodes.
 # XXX here for compat; replaced by emulabconfig option.
@@ -708,7 +712,7 @@ sub doboot()
 	$emulabconfig{EXTRA_PKG}    = "emulab-extras-7.2";
 	$emulabconfig{PACKAGE_TARBALL} = "FreeBSD-11.2-packages${suf}.tar.gz";
 	$emulabconfig{PGENI_PKG}    = "emulab-protogeni-7.2";
-    } elsif ($FBSD_VERSION >= 11.3) {
+    } elsif ($FBSD_VERSION == 11.3) {
 	my $suf = "-64";
 	$emulabconfig{FS_PKG_DIR} = "/share/freebsd/11.3/packages${suf}";
 	$emulabconfig{OPS_PKG_DIR}  = $emulabconfig{FS_PKG_DIR};
@@ -720,6 +724,18 @@ sub doboot()
 	$emulabconfig{EXTRA_PKG}    = "emulab-extras-7.3";
 	$emulabconfig{PACKAGE_TARBALL} = "FreeBSD-11.3-packages${suf}.tar.gz";
 	$emulabconfig{PGENI_PKG}    = "emulab-protogeni-7.3";
+    } elsif ($FBSD_VERSION > 11.3) {
+	my $suf = "-64";
+	$emulabconfig{FS_PKG_DIR} = "/share/freebsd/12.1/packages${suf}";
+	$emulabconfig{OPS_PKG_DIR}  = $emulabconfig{FS_PKG_DIR};
+	$emulabconfig{BOSS_PKG_DIR} = $emulabconfig{FS_PKG_DIR};
+	$emulabconfig{EXTRA_PKG_DIR}= $emulabconfig{FS_PKG_DIR};
+	$emulabconfig{FS_PKG}       = "emulab-fs-8.1";
+	$emulabconfig{OPS_PKG}      = "emulab-ops-8.1";
+	$emulabconfig{BOSS_PKG}     = "emulab-boss-8.1";
+	$emulabconfig{EXTRA_PKG}    = "emulab-extras-8.1";
+	$emulabconfig{PACKAGE_TARBALL} = "FreeBSD-12.1-packages${suf}.tar.gz";
+	$emulabconfig{PGENI_PKG}    = "emulab-protogeni-8.1";
     }
     #
     # If there is a package tarball, prefer that and grab it now.
@@ -1096,11 +1112,15 @@ sub SetupFsNode()
 	if ($FBSD_VERSION >= 10.1) {
 	    # XXX FreeBSD got rid of the /usr/bin/perl symlink; we need it.
 	    if (! -x "/usr/bin/perl") {
-		system("ln -sf /usr/local/bin/perl5 /usr/bin/perl");
+		system("ln -sfn /usr/local/bin/perl5 /usr/bin/perl");
 	    }
 	    # hmm...python too?
 	    if (! -x "/usr/local/bin/python") {
-		system("ln -sf /usr/local/bin/python2 /usr/local/bin/python");
+		if (-x $PYBIN2) {
+		    system("ln -sfn $PYBIN2 /usr/local/bin/python");
+		} elsif (-x $PYBIN3) {
+		    system("ln -sfn $PYBIN3 /usr/local/bin/python");
+		}
 	    }
 
 	    #
@@ -1715,11 +1735,15 @@ sub SetupOpsNode($)
 	if ($FBSD_VERSION >= 10.1) {
 	    # XXX FreeBSD got rid of the /usr/bin/perl symlink; we need it.
 	    if (! -x "/usr/bin/perl") {
-		system("ln -sf /usr/local/bin/perl5 /usr/bin/perl");
+		system("ln -sfn /usr/local/bin/perl5 /usr/bin/perl");
 	    }
 	    # hmm...python too?
 	    if (! -x "/usr/local/bin/python") {
-		system("ln -sf /usr/local/bin/python2 /usr/local/bin/python");
+		if (-x $PYBIN2) {
+		    system("ln -sfn $PYBIN2 /usr/local/bin/python");
+		} elsif (-x $PYBIN3) {
+		    system("ln -sfn $PYBIN3 /usr/local/bin/python");
+		}
 	    }
 
 	    #
@@ -2490,11 +2514,15 @@ sub SetupBossNode($)
 	if ($FBSD_VERSION >= 10.1) {
 	    # XXX FreeBSD got rid of the /usr/bin/perl symlink; we need it.
 	    if (! -x "/usr/bin/perl") {
-		system("ln -sf /usr/local/bin/perl5 /usr/bin/perl");
+		system("ln -sfn /usr/local/bin/perl5 /usr/bin/perl");
 	    }
 	    # hmm...python too?
 	    if (! -x "/usr/local/bin/python") {
-		system("ln -sf /usr/local/bin/python2 /usr/local/bin/python");
+		if (-x $PYBIN2) {
+		    system("ln -sfn $PYBIN2 /usr/local/bin/python");
+		} elsif (-x $PYBIN3) {
+		    system("ln -sfn $PYBIN3 /usr/local/bin/python");
+		}
 	    }
 
 	    #
@@ -3305,11 +3333,15 @@ sub SetupOpsJail()
 
 	if ($FBSD_VERSION >= 10.1) {
 	    if (! -x "/usr/bin/perl") {
-		system("ln -sf /usr/local/bin/perl5 /usr/bin/perl");
+		system("ln -sfn /usr/local/bin/perl5 /usr/bin/perl");
 	    }
 	    # hmm...python too?
 	    if (! -x "/usr/local/bin/python") {
-		system("ln -sf /usr/local/bin/python2 /usr/local/bin/python");
+		if (-x $PYBIN2) {
+		    system("ln -sfn $PYBIN2 /usr/local/bin/python");
+		} elsif (-x $PYBIN3) {
+		    system("ln -sfn $PYBIN3 /usr/local/bin/python");
+		}
 	    }
 
 	    #
@@ -4078,6 +4110,16 @@ sub FindExtraFSConfig($)
 	foreach my $disk (@disks) {
 	    my ($dev,$path) = split(":", $disk);
 	    if (defined($path) && $path eq $mountpoint) {
+		#
+		# XXX attempt to map "ada" to the real disk type.
+		# Over time we have used "da", "ada", and now "xbd" depending
+		# on the version of Xen and FreeBSD. We only care about "xbd"
+		# going forward.
+		#
+		if ($dev =~ /^ada(.*)$/ && ! -e "/dev/$dev" &&
+		    -e "/dev/xbd$1") {
+		    $dev = "xbd$1";
+		}
 		return $dev;
 	    }
 	}
diff --git a/configure b/configure
index 710d8d9ee7f0f4184d441c7d50f7ec39acbff67b..65c32f176107c2251fdda9041ecd465d6f033ee4 100755
--- a/configure
+++ b/configure
@@ -676,15 +676,15 @@ TBROBOCOPSEMAIL_NOSLASH
 TBROBOCOPSEMAIL
 TBOPSEMAIL_NOSLASH
 TBOPSEMAIL
-POWDER_RFMONITOR_HOST
-POWDER_RFMONITOR
+MAILERNODE
+DISABLE_RESERVATION_EMAIL
 POWDER_WBSTORE
 POWDER_NICKNAME
+POWDER_RFMONITOR_HOST
+POWDER_RFMONITOR
 UI_EXTERNAL_ACCOUNTS
 UI_DISABLE_RESERVATIONS
 UI_DISABLE_DATASETS
-DISABLE_RESERVATION_EMAIL
-MAILERNODE
 REPORTBOOT_FLAGS
 REPORTBOOT_ENABLED
 BOOTINFO_EVENTS
@@ -718,8 +718,8 @@ IMAGEPROVENANCE
 NFSMAPTOUSER
 IPV6_SUBNET_PREFIX
 IPV6_ENABLED
-BROWSER_CONSOLE_ENABLE
 BROWSER_CONSOLE_PROXIED
+BROWSER_CONSOLE_ENABLE
 EC2META_ENABLE
 NOSITECHECKIN
 SPEWFROMOPS
@@ -759,11 +759,11 @@ PROTOGENI_GENIRACK
 PROTOGENI_URL
 GENI_PUBRPCPORT
 PROTOGENI_NONFSMOUNTS
-PROTOGENI_GENIWEBLOGIN
 PROTOGENI_HOLDINGPROJECT
+PROTOGENI_GENIWEBLOGIN
 PROTOGENI_MAXSERVERLOAD
-PROTOGENI_LOCALUSER
 PROTOGENI_PGERRORS
+PROTOGENI_LOCALUSER
 PROTOGENI_RPCNAME
 PROTOGENI_RPCPORT
 PROTOGENI_DOMAIN
@@ -846,8 +846,8 @@ SUBBOSS_SSLCERTNAME
 OUTERBOSS_SSLCERTNAME
 OUTERBOSS_XMLRPCPORT
 OUTERBOSS_NODENAME
-ELABINELAB
 ELABINELAB_MAILTARGET
+ELABINELAB
 SFSSUPPORT
 FANCYBANNER
 TBMAINSITE
@@ -2798,6 +2798,9 @@ if test ! -d "$PYTHON_PATH/include"; then
     PYTHON_PATH=`dirname ${PYTHON_PATH}`
 fi
 PYTHON_INCLUDE="${PYTHON_PATH}/include/python${PYTHON_VERSION}"
+if test ! -d "$PYTHON_INCLUDE" -a -d "${PYTHON_INCLUDE}m"; then
+      PYTHON_INCLUDE="${PYTHON_INCLUDE}m"
+fi
 CPPFLAGS="${CPPFLAGS} -I${PYTHON_INCLUDE}"
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
@@ -5170,6 +5173,15 @@ done
 
 
 
+
+
+
+
+
+
+
+
+
 
 
 
@@ -5954,8 +5966,8 @@ fi
 if test -n "$PROTOGENI_EMAIL"; then
     PROTOGENI_EMAIL="`echo $PROTOGENI_EMAIL | sed -e 's/@/\\\@/'`"
 fi
-if test -n "$ELABINELAB_MAILTARGET"; then
-    ELABINELAB_MAILTARGET="`echo $ELABINELAB_MAILTARGET | sed -e 's/@/\\\@/'`"
+if test -n "ELABINELAB_MAILTARGET"; then
+    ELABINELAB_MAILTARGET="`echo $ELABINELAB | sed -e 's/@/\\\@/'`"
 fi
 PROTOGENI_PGERRORS="`echo $PROTOGENI_PGERRORS | sed -e 's/@/\\\@/'`"
 
diff --git a/configure.ac b/configure.ac
index dff5b6d84fc19fa225f1bf08010acc6bd8e6abf8..276036d4dc58a2d034c45b16ae33d1f546237763 100644
--- a/configure.ac
+++ b/configure.ac
@@ -89,6 +89,9 @@ if test ! -d "$PYTHON_PATH/include"; then
     PYTHON_PATH=`dirname ${PYTHON_PATH}`
 fi
 PYTHON_INCLUDE="${PYTHON_PATH}/include/python${PYTHON_VERSION}"
+if test ! -d "$PYTHON_INCLUDE" -a -d "${PYTHON_INCLUDE}m"; then
+      PYTHON_INCLUDE="${PYTHON_INCLUDE}m"
+fi
 CPPFLAGS="${CPPFLAGS} -I${PYTHON_INCLUDE}"
 AC_CHECK_HEADERS([Python.h],
 		 [],
diff --git a/event/monitoring/GNUmakefile.in b/event/monitoring/GNUmakefile.in
index 49847e50d403772653d2089a76e32f13496dfc57..a42f1e3b16b0b580f4e307100cb2d2196e4695f4 100644
--- a/event/monitoring/GNUmakefile.in
+++ b/event/monitoring/GNUmakefile.in
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014 University of Utah and the Flux Group.
+# Copyright (c) 2014-2020 University of Utah and the Flux Group.
 # 
 # {{{EMULAB-LICENSE
 # 
@@ -52,7 +52,7 @@ CFLAGS   += -I/usr/local/include
 LDFLAGS  += -L$(TESTBED_LIBOBJDIR)
 LDFLAGS  += -L$(TESTBED_LIBOBJDIR)/event
 LIBS      = -levent -lcrypto
-LIBS     += -L/usr/local/lib -lpubsub -lm
+LIBS     += -L/usr/local/lib -lpubsub -lssl -lm
 LIBS     += -L/usr/local/lib/mysql -lmysqlclient
 LDFLAGS  += $(LDSTATIC)
 
diff --git a/install/installvars.pm.in b/install/installvars.pm.in
index edd2666cf55afa83e1bb38bca654d02333e4bf60..018cdc719bb7e2e4d4beb1f7a8c22a54f3e9e026 100644
--- a/install/installvars.pm.in
+++ b/install/installvars.pm.in
@@ -64,13 +64,16 @@ $BOSS_PORT = "emulab-boss-1.8";
 $OPS_PORT  = "emulab-ops-1.4"; 
 $FS_PORT   = "emulab-fs-1.4";
 if ($FBSD_MAJOR > 4) {
-    if ($FBSD_MAJOR == 11) {
+    if ($FBSD_MAJOR == 12) {
+	    $BOSS_PORT = "emulab-boss-8.1";
+	    $OPS_PORT = "emulab-ops-8.1";
+	    $FS_PORT = "emulab-fs-8.1";
+    } elsif ($FBSD_MAJOR == 11) {
 	if ($FBSD_MINOR <= 1) {
 	    $BOSS_PORT = "emulab-boss-7.1";
 	    $OPS_PORT = "emulab-ops-7.1";
 	    $FS_PORT = "emulab-fs-7.1";
-	}
-        else {
+	} else {
 	    my $ver = "7.$FBSD_MINOR";
 	    $BOSS_PORT = "emulab-boss-$ver";
 	    $OPS_PORT = "emulab-ops-$ver";
@@ -163,7 +166,12 @@ $SELFLOAD_PATCH		= "$main::TOP_SRCDIR/patches/SelfLoader.patch";
 #
 $PYM2_PKG = "py25-m2crypto-0.19.1";
 $PY_VER   = "python2.5";
-if ($FBSD_MAJOR > 10 || ($FBSD_MAJOR == 10 && $FBSD_MINOR > 0)) {
+$PY_PKGPREFIX = "py27";
+if ($FBSD_MAJOR > 11 || ($FBSD_MAJOR == 11 && $FBSD_MINOR > 3)) {
+    $PYM2_PKG = "";
+    $PY_VER = "python3.7";
+    $PY_PKGPREFIX = "py37";
+} elsif ($FBSD_MAJOR > 10 || ($FBSD_MAJOR == 10 && $FBSD_MINOR > 0)) {
     $PYM2_PKG = "py27-m2crypto-0.22.3";
     $PY_VER = "python2.7";
 } elsif ($FBSD_MAJOR > 9) {
@@ -262,6 +270,10 @@ $EMULAB_PYTHON_PATH	= "/usr/local/bin/python";
 $PORT_PERL_PATH		= "/usr/local/bin/perl5";
 $PORT_PYTHON_PATH	= "/usr/local/bin/python2";
 $PORT_PYTHON_PATH2	= "/usr/local/bin/python2.7";
+if ($FBSD_MAJOR > 11) {
+    $PORT_PYTHON_PATH	= "/usr/local/bin/python3";
+    $PORT_PYTHON_PATH2	= "/usr/local/bin/python3.7";
+}
 
 #
 # Some programs we use
diff --git a/install/phases/boss/genirack b/install/phases/boss/genirack
index bbbb6cce9d3fe53f3850ed455372ed06d5fc8886..178f87951c25183615a133cecb24fa85d1a4108f 100755
--- a/install/phases/boss/genirack
+++ b/install/phases/boss/genirack
@@ -35,8 +35,8 @@ my $ELABPASSWORD = "$PREFIX/etc/elabman.pswd";
 my $HPPASSWORD   = "$PREFIX/etc/switch.pswd";
 my $PORTSRC      = "http://www.emulab.net/downloads/FreeBSD-9.0-ports.tar.gz";
 if ($FBSD_MAJOR >= 10) {
-    $PORTSRC     = "http://www.emulab.net/downloads/FreeBSD-10.".
-	$FBSD_MINOR . "-ports.tar.gz";
+    $PORTSRC     = "http://www.emulab.net/downloads/FreeBSD-" .
+	$FBSD_MAJOR . "." . $FBSD_MINOR . "-ports.tar.gz";
 }
 my $ZZZ		 = "/usr/local/etc/rc.d/zzz-inelab.sh";
 my $DEFAULTOSID  = "UBUNTU14-64-STD";
diff --git a/install/phases/boss/portfix b/install/phases/boss/portfix
index e9cf3131b2fa8d972fca1450d43e3c4b2df2062c..b9cf17a762c1a5253a243fd3a432c3706eee827e 100755
--- a/install/phases/boss/portfix
+++ b/install/phases/boss/portfix
@@ -138,8 +138,9 @@ sub Install($$$)
 	if ($PGENISUPPORT) {
 	    Phase "mod_wsgi3", "Looking for mod_wsgi3 and installing", sub {
 		if ($FBSD_MAJOR >= 11) {
-		    DoneIfPackageInstalled("ap24-py27-mod_wsgi");
-		    my $pname = GetPackage("ap24-py27-mod_wsgi", $packagedir);
+		    DoneIfPackageInstalled("ap24-${PY_PKGPREFIX}-mod_wsgi");
+		    my $pname = GetPackage("ap24-${PY_PKGPREFIX}-mod_wsgi",
+					   $packagedir);
 		    AddPackage($pname, $packagedir);
 		} else {
 		    DoneIfPackageInstalled("ap22-mod_wsgi");
@@ -148,8 +149,8 @@ sub Install($$$)
 		}
 	    };
 	    Phase "py-flask", "Looking for py-flask and installing", sub {
-		DoneIfPackageInstalled("py27-Flask");
-		my $pname = GetPackage("py27-Flask", $packagedir);
+		DoneIfPackageInstalled("${PY_PKGPREFIX}-Flask");
+		my $pname = GetPackage("${PY_PKGPREFIX}-Flask", $packagedir);
 		AddPackage($pname, $packagedir);
 	    };
 	    Phase "mod_fcgid", "Looking for mod_fcgid and installing", sub {
diff --git a/install/phases/boss/xen b/install/phases/boss/xen
index 587d36fdb8c20356c68fca1adf2af62c85839cbf..11c5b6e05e34e22beaf0cab36700207ac88b7882 100755
--- a/install/phases/boss/xen
+++ b/install/phases/boss/xen
@@ -25,7 +25,7 @@ sub Install($$$)
     # These are the images that are known to run as XEN guests.
     #
     my @images = ("UBUNTU16-64-STD", "UBUNTU18-64-STD", "CENTOS7-64-STD",
-		  "FBSD103-64-STD", "FBSD113-64-STD", "FBSD120-64-STD");
+		  "FBSD103-64-STD", "FBSD113-64-STD", "FBSD121-64-STD");
 
     if ($FBSD_MAJOR <= 10) {
 	$XENIMAGE = "XEN46-64-STD";
diff --git a/install/phases/letsencrypt b/install/phases/letsencrypt
index 7a85689f1ff040c978b014dbda84253e73cfd321..ea52afc16f2ab4aa5ea3d5ff3a392ce1bd0dbe3a 100644
--- a/install/phases/letsencrypt
+++ b/install/phases/letsencrypt
@@ -20,18 +20,19 @@ sub Install($$$)
     SET_TESTBED_VERSION("letsencrypt");
     
     Phase "letsencrypt", "Setting up letsencrypt web certificate", sub {
-	Phase "certbot", "Installing py27-certbot", sub {
-	    DoneIfPackageInstalled("py27-certbot", 0);
+	Phase "certbot", "Installing ${PY_PKGPREFIX}-certbot", sub {
+	    DoneIfPackageInstalled("${PY_PKGPREFIX}-certbot", 0);
 	    $ENV{"ASSUME_ALWAYS_YES"} = "true";
-	    ExecQuietFatal("pkg install py27-certbot");
+	    ExecQuietFatal("pkg install ${PY_PKGPREFIX}-certbot");
 	};
 	Phase "mkdir", "Creating $CHALLENGEDIR", sub {
 	    DoneIfExists($CHALLENGEDIR);
 	    ExecQuietFatal("mkdir -p $CHALLENGEDIR");
 	};
-	my $CERTBOT = "certbot-2.7 certonly -n --webroot -w /usr/testbed/www ".
-	    "--agree-tos -m $TBOPSEMAIL ";
-	my $RENEW   = "/usr/local/bin/certbot-2.7 renew";
+	# there appears to be a "certbot" symlink to the right version
+	my $CERTBOT = "/usr/local/bin/certbot certonly ".
+	    "-n --webroot -w /usr/testbed/www --agree-tos -m $TBOPSEMAIL ";
+	my $RENEW   = "/usr/local/bin/certbot renew";
 	my $CERT    = "/usr/local/etc/letsencrypt/live/";
 
 	if (ISBOSSNODE($server)) {
diff --git a/install/phases/ops/genirack b/install/phases/ops/genirack
index 9bd0b509a2d19aab83be1f2e0468ef9aaf7179b7..c13f6f80d329f14d82585f796e7f748b057d52d6 100755
--- a/install/phases/ops/genirack
+++ b/install/phases/ops/genirack
@@ -12,8 +12,8 @@ my $RACKNTPCONF  = "$TOP_SRCDIR/install/genirack/ntp.conf";
 my $ETCNTPCONF   = "/etc/ntp.conf";
 my $PORTSRC      = "http://www.emulab.net/downloads/FreeBSD-9.0-ports.tar.gz";
 if ($FBSD_MAJOR >= 10) {
-    $PORTSRC     = "http://www.emulab.net/downloads/FreeBSD-10.".
-	$FBSD_MINOR . "-ports.tar.gz";
+    $PORTSRC     = "http://www.emulab.net/downloads/FreeBSD-" .
+	$FBSD_MAJOR . "." . $FBSD_MINOR . "-ports.tar.gz";
 }
 
 sub Install($$$)
diff --git a/install/phases/ops/shellinabox b/install/phases/ops/shellinabox
index 805c240d7ccc2ace3842b8e22e9c9b617ec579c3..1426cd0537b3bf855eb1e539fcf4474f2ad133d0 100644
--- a/install/phases/ops/shellinabox
+++ b/install/phases/ops/shellinabox
@@ -19,6 +19,9 @@ sub Install($$$)
     my $localtarfile  = "/tmp/shellinabox.tar.gz";
     my $LOGFILE       = "$PREFIX/log/shellinabox.log";
 
+    if ($FBSD_MAJOR > 11) {
+	$SOURCEURL = "http://$OUTER_BOSS/downloads/shellinabox-new.tar.Z";
+    }
     Phase "shellinabox", "Installing shellinabox", sub {
 	PhaseSkip("shellinabox not enabled")
 	    if (!$BROWSER_CONSOLE_ENABLE);
diff --git a/pxe/GNUmakefile.in b/pxe/GNUmakefile.in
index c371470c119d67a40dd65820acad017ebedcbbcd..0b67b27ef00af5e81e52869fefb6758c36b82dc6 100644
--- a/pxe/GNUmakefile.in
+++ b/pxe/GNUmakefile.in
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2000-2018 University of Utah and the Flux Group.
+# Copyright (c) 2000-2020 University of Utah and the Flux Group.
 # 
 # {{{EMULAB-LICENSE
 # 
@@ -71,7 +71,7 @@ BI_DBOBJ += event-support.o
 
 CFLAGS  += -DEVENTSYS
 LFLAGS  += $(TESTBED_LIBOBJDIR)/event/libevent.a
-LFLAGS  += -L/usr/local/lib -lpubsub -lcrypto
+LFLAGS  += -L/usr/local/lib -lpubsub -lcrypto -lssl
 ifeq ($(BOOTINFO_PXEEVENTS),1)
 CFLAGS  += -DBOOTINFO_PXEEVENTS
 endif
diff --git a/ssl/GNUmakefile.in b/ssl/GNUmakefile.in
index 8bae60787485831e01ba09899f1954fb032c94d6..c943937899a9b189070a6a050d83f372389dc8c7 100644
--- a/ssl/GNUmakefile.in
+++ b/ssl/GNUmakefile.in
@@ -37,12 +37,10 @@ APACHE_KEYFILE_OPS  = $(APACHE_ETCDIR)/ssl.key/$(USERNODE).key
 include $(OBJDIR)/Makeconf
 
 all:	emulab.pem server.pem localnode.pem ctrlnode.pem \
-	capture.pem capture.fingerprint capture.sha1fingerprint \
-	keys mksig updatecert
+	capture.pem capture.fingerprint keys mksig updatecert
 
 remote-site:	emulab.pem capture.pem capture.fingerprint server.pem \
-	localnode.pem capture.sha1fingerprint apache.pem apache-ops.pem \
-	ctrlnode.pem updatecert
+	localnode.pem apache.pem apache-ops.pem ctrlnode.pem updatecert
 
 clearinghouse:	emulab.pem apache.pem
 
@@ -131,14 +129,17 @@ capture.pem:	dirsmade mkserial capture.cnf ca.cnf capture.key capture.req
 # Generate the fingerprint of the capture certificate
 # NOTE: I'd rather use SHA1 than SHA, but we've widely distributed the
 # tiptunnel binary, and it needs SHA
+# NOTE: We have no choice anymore, openssl no longer supports SHA.
+# We continue to do this on the mothership for now.
 #
 capture.fingerprint:	capture.pem
+ifeq (@TBMAINSITE@,1)
 	openssl x509 -sha -noout -fingerprint -in capture.pem \
-	    > capture.fingerprint
-
-capture.sha1fingerprint:	capture.pem
+	    > capture.shafingerprint
+else
 	openssl x509 -sha1 -noout -fingerprint -in capture.pem \
-	    > capture.sha1fingerprint
+	    > capture.fingerprint
+endif
 
 localnode.pem:	dirsmade mkserial localnode.cnf ca.cnf localnode.key localnode.req
 	cat localnode.key >> localnode.req
@@ -173,7 +174,7 @@ emulab_pubkey.pem:	emulab_privkey.pem
 # by you and writable. 
 #
 %.key:
-	openssl genrsa -out $@ -rand .rand 2048
+	openssl genrsa -out $@ 2048
 
 #
 # Rule to extract public key from private key,
@@ -255,7 +256,6 @@ boss-installX:	install-dirs \
 		$(INSTALL_ETCDIR)/ctrlnode.pem \
 		$(INSTALL_ETCDIR)/capture.pem \
 		$(INSTALL_ETCDIR)/capture.fingerprint \
-		$(INSTALL_ETCDIR)/capture.sha1fingerprint \
 		$(INSTALL_ETCDIR)/emulab_privkey.pem \
 		$(INSTALL_ETCDIR)/emulab_pubkey.pem \
 		$(INSTALL_SBINDIR)/updatecert \
@@ -270,7 +270,6 @@ boss-installX:	install-dirs \
 	chmod 640 $(INSTALL_ETCDIR)/emulab_privkey.pem
 	chmod 640 $(INSTALL_ETCDIR)/capture.pem
 	chmod 644 $(INSTALL_ETCDIR)/capture.fingerprint
-	chmod 644 $(INSTALL_ETCDIR)/capture.sha1fingerprint
 
 install-conf:	usercert.cnf syscert.cnf ca.cnf
 	$(INSTALL_DATA) usercert.cnf $(INSTALL_LIBDIR)/ssl/usercert.cnf
@@ -284,7 +283,6 @@ remote-site-boss-install:	install-dirs \
 		$(INSTALL_ETCDIR)/emulab.pub \
 		$(INSTALL_ETCDIR)/capture.pem \
 		$(INSTALL_ETCDIR)/capture.fingerprint \
-		$(INSTALL_ETCDIR)/capture.sha1fingerprint \
 		$(INSTALL_ETCDIR)/ctrlnode.pem \
 		$(INSTALL_ETCDIR)/server.pem \
 		$(INSTALL_SBINDIR)/updatecert \
@@ -295,7 +293,6 @@ remote-site-boss-install:	install-dirs \
 	chmod 644 $(INSTALL_ETCDIR)/emulab.pub
 	chmod 640 $(INSTALL_ETCDIR)/capture.pem
 	chmod 644 $(INSTALL_ETCDIR)/capture.fingerprint
-	chmod 644 $(INSTALL_ETCDIR)/capture.sha1fingerprint
 	chmod 640 $(INSTALL_ETCDIR)/server.pem
 	chmod 640 $(INSTALL_ETCDIR)/client.pem
 	chmod 640 $(INSTALL_ETCDIR)/ctrlnode.pem