diff --git a/sql/database-create.sql b/sql/database-create.sql
index 6b965b20b6abaeeafded590f2d3a4f48b15983a9..3824f1cda1608a440890b878f2b87028af727d13 100644
--- a/sql/database-create.sql
+++ b/sql/database-create.sql
@@ -3897,6 +3897,8 @@ CREATE TABLE `user_pubkeys` (
   `uid` varchar(8) NOT NULL default '',
   `uid_idx` mediumint(8) unsigned NOT NULL default '0',
   `idx` int(10) unsigned NOT NULL auto_increment,
+  `internal` tinyint(1) NOT NULL default '0',
+  `nodelete` tinyint(1) NOT NULL default '0',
   `pubkey` text,
   `stamp` datetime default NULL,
   `comment` varchar(128) NOT NULL default '',
diff --git a/sql/updates/4/275 b/sql/updates/4/275
new file mode 100644
index 0000000000000000000000000000000000000000..4144540d7aaae4155acc99369eb40bb34a6ca3f8
--- /dev/null
+++ b/sql/updates/4/275
@@ -0,0 +1,35 @@
+#
+# Tweak the ssh table to prevent deletion of internal keys.
+#
+use strict;
+use libdb;
+use EmulabConstants;
+
+sub DoUpdate($$$)
+{
+    my ($dbhandle, $dbname, $version) = @_;
+    my $OURDOMAIN = $EmulabConstants::OURDOMAIN;
+
+    #
+    # Mark the unencrypted Emulab generated keys as internal so we
+    # know which ones they are.
+    #
+    if (!DBSlotExists("user_pubkeys", "internal")) {
+        DBQueryFatal("ALTER TABLE user_pubkeys ADD ".
+		     " `internal` tinyint(1) NOT NULL default '0' ".
+		     " after idx");
+    }
+    #
+    # Other keys can be marked nodelete so that user cannot remove them.
+    #
+    if (!DBSlotExists("user_pubkeys", "nodelete")) {
+        DBQueryFatal("ALTER TABLE user_pubkeys ADD ".
+		     " `nodelete` tinyint(1) NOT NULL default '0' ".
+		     " after internal");
+    }
+    DBQueryFatal("update user_pubkeys set internal=1 ".
+		 "where comment like '%\@${OURDOMAIN}' and ".
+		 "      comment=concat(uid, '\@${OURDOMAIN}')");
+
+    return 0;
+}