mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-01 19:22:42 +01:00
Standardize SSH key storage
Summary: Ref T5833. This fixes a few weird things with this table: - A bunch of columns were nullable for no reason. - We stored an MD5 hash of the key (unusual) but never used it and callers were responsible for manually populating it. - We didn't perform known-key-text lookups by using an index. Test Plan: - Ran migrations. - Faked duplicate keys, saw them clean up correctly. - Added new keys. - Generated new keys. - Used `bin/auth-ssh` and `bin/auth-ssh-key`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T5833 Differential Revision: https://secure.phabricator.com/D10805
This commit is contained in:
parent
a17a368692
commit
bf17b12daf
13 changed files with 136 additions and 33 deletions
2
resources/sql/autopatches/20141107.ssh.1.colname.sql
Normal file
2
resources/sql/autopatches/20141107.ssh.1.colname.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
CHANGE userPHID objectPHID VARBINARY(64) NOT NULL;
|
2
resources/sql/autopatches/20141107.ssh.2.keyhash.sql
Normal file
2
resources/sql/autopatches/20141107.ssh.2.keyhash.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
DROP COLUMN keyHash;
|
2
resources/sql/autopatches/20141107.ssh.3.keyindex.sql
Normal file
2
resources/sql/autopatches/20141107.ssh.3.keyindex.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
ADD COLUMN keyIndex BINARY(12);
|
50
resources/sql/autopatches/20141107.ssh.4.keymig.php
Normal file
50
resources/sql/autopatches/20141107.ssh.4.keymig.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$table = new PhabricatorAuthSSHKey();
|
||||||
|
$conn_w = $table->establishConnection('w');
|
||||||
|
|
||||||
|
echo "Updating SSH public key indexes...\n";
|
||||||
|
|
||||||
|
$keys = new LiskMigrationIterator($table);
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$id = $key->getID();
|
||||||
|
|
||||||
|
echo "Updating key {$id}...\n";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$hash = $key->toPublicKey()->getHash();
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
echo "Key has bad format! Removing key.\n";
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'DELETE FROM %T WHERE id = %d',
|
||||||
|
$table->getTableName(),
|
||||||
|
$id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$collision = queryfx_all(
|
||||||
|
$conn_w,
|
||||||
|
'SELECT * FROM %T WHERE keyIndex = %s AND id < %d',
|
||||||
|
$table->getTableName(),
|
||||||
|
$hash,
|
||||||
|
$key->getID());
|
||||||
|
if ($collision) {
|
||||||
|
echo "Key is a duplicate! Removing key.\n";
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'DELETE FROM %T WHERE id = %d',
|
||||||
|
$table->getTableName(),
|
||||||
|
$id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET keyIndex = %s WHERE id = %d',
|
||||||
|
$table->getTableName(),
|
||||||
|
$hash,
|
||||||
|
$key->getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Done.\n";
|
2
resources/sql/autopatches/20141107.ssh.5.indexnull.sql
Normal file
2
resources/sql/autopatches/20141107.ssh.5.indexnull.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
CHANGE keyIndex keyIndex BINARY(12) NOT NULL;
|
2
resources/sql/autopatches/20141107.ssh.6.indexkey.sql
Normal file
2
resources/sql/autopatches/20141107.ssh.6.indexkey.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
ADD UNIQUE KEY `key_unique` (keyIndex);
|
23
resources/sql/autopatches/20141107.ssh.7.colnull.sql
Normal file
23
resources/sql/autopatches/20141107.ssh.7.colnull.sql
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
UPDATE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
SET name = '' WHERE name IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
CHANGE name name VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
SET keyType = '' WHERE keyType IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
CHANGE keyType keyType VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
SET keyBody = '' WHERE keyBody IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
CHANGE keyBody keyBody LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL;
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
SET keyComment = '' WHERE keyComment IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||||
|
CHANGE keyComment keyComment VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;
|
|
@ -34,9 +34,15 @@ foreach ($keys as $ssh_key) {
|
||||||
|
|
||||||
$type = $ssh_key->getKeyType();
|
$type = $ssh_key->getKeyType();
|
||||||
$type = preg_replace('@[\x00-\x20]+@', '', $type);
|
$type = preg_replace('@[\x00-\x20]+@', '', $type);
|
||||||
|
if (!strlen($type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$key = $ssh_key->getKeyBody();
|
$key = $ssh_key->getKeyBody();
|
||||||
$key = preg_replace('@[\x00-\x20]+@', '', $key);
|
$key = preg_replace('@[\x00-\x20]+@', '', $key);
|
||||||
|
if (!strlen($key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$options = array(
|
$options = array(
|
||||||
'command="'.$cmd.'"',
|
'command="'.$cmd.'"',
|
||||||
|
|
|
@ -73,21 +73,18 @@ final class PhabricatorAuthSSHKeyQuery
|
||||||
if ($this->objectPHIDs !== null) {
|
if ($this->objectPHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'userPHID IN (%Ls)',
|
'objectPHID IN (%Ls)',
|
||||||
$this->objectPHIDs);
|
$this->objectPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->keys !== null) {
|
if ($this->keys !== null) {
|
||||||
// TODO: This could take advantage of a better key, and the hashing
|
|
||||||
// scheme for this table is a bit nonstandard and questionable.
|
|
||||||
|
|
||||||
$sql = array();
|
$sql = array();
|
||||||
foreach ($this->keys as $key) {
|
foreach ($this->keys as $key) {
|
||||||
$sql[] = qsprintf(
|
$sql[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'(keyType = %s AND keyBody = %s)',
|
'(keyType = %s AND keyIndex = %s)',
|
||||||
$key->getType(),
|
$key->getType(),
|
||||||
$key->getBody());
|
$key->getHash());
|
||||||
}
|
}
|
||||||
$where[] = implode(' OR ', $sql);
|
$where[] = implode(' OR ', $sql);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,43 +4,45 @@ final class PhabricatorAuthSSHKey
|
||||||
extends PhabricatorAuthDAO
|
extends PhabricatorAuthDAO
|
||||||
implements PhabricatorPolicyInterface {
|
implements PhabricatorPolicyInterface {
|
||||||
|
|
||||||
protected $userPHID;
|
protected $objectPHID;
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $keyType;
|
protected $keyType;
|
||||||
|
protected $keyIndex;
|
||||||
protected $keyBody;
|
protected $keyBody;
|
||||||
protected $keyHash;
|
protected $keyComment = '';
|
||||||
protected $keyComment;
|
|
||||||
|
|
||||||
private $object = self::ATTACHABLE;
|
private $object = self::ATTACHABLE;
|
||||||
|
|
||||||
public function getObjectPHID() {
|
|
||||||
return $this->getUserPHID();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConfiguration() {
|
public function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'keyHash' => 'bytes32',
|
'name' => 'text255',
|
||||||
'keyComment' => 'text255?',
|
'keyType' => 'text255',
|
||||||
|
'keyIndex' => 'bytes12',
|
||||||
// T6203/NULLABILITY
|
'keyBody' => 'text',
|
||||||
// These seem like they should not be nullable.
|
'keyComment' => 'text255',
|
||||||
'name' => 'text255?',
|
|
||||||
'keyType' => 'text255?',
|
|
||||||
'keyBody' => 'text?',
|
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'userPHID' => array(
|
'key_object' => array(
|
||||||
'columns' => array('userPHID'),
|
'columns' => array('objectPHID'),
|
||||||
),
|
),
|
||||||
'keyHash' => array(
|
'key_unique' => array(
|
||||||
'columns' => array('keyHash'),
|
'columns' => array('keyIndex'),
|
||||||
'unique' => true,
|
'unique' => true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function save() {
|
||||||
|
$this->setKeyIndex($this->toPublicKey()->getHash());
|
||||||
|
return parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toPublicKey() {
|
||||||
|
return PhabricatorAuthSSHPublicKey::newFromStoredKey($this);
|
||||||
|
}
|
||||||
|
|
||||||
public function getEntireKey() {
|
public function getEntireKey() {
|
||||||
$parts = array(
|
$parts = array(
|
||||||
$this->getKeyType(),
|
$this->getKeyType(),
|
||||||
|
@ -60,6 +62,8 @@ final class PhabricatorAuthSSHKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,15 @@ final class PhabricatorAuthSSHPublicKey extends Phobject {
|
||||||
// <internal>
|
// <internal>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function newFromStoredKey(PhabricatorAuthSSHKey $key) {
|
||||||
|
$public_key = new PhabricatorAuthSSHPublicKey();
|
||||||
|
$public_key->type = $key->getKeyType();
|
||||||
|
$public_key->body = $key->getKeyBody();
|
||||||
|
$public_key->comment = $key->getKeyComment();
|
||||||
|
|
||||||
|
return $public_key;
|
||||||
|
}
|
||||||
|
|
||||||
public static function newFromRawKey($entire_key) {
|
public static function newFromRawKey($entire_key) {
|
||||||
$entire_key = trim($entire_key);
|
$entire_key = trim($entire_key);
|
||||||
if (!strlen($entire_key)) {
|
if (!strlen($entire_key)) {
|
||||||
|
@ -83,4 +92,11 @@ final class PhabricatorAuthSSHPublicKey extends Phobject {
|
||||||
return $this->comment;
|
return $this->comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHash() {
|
||||||
|
$body = $this->getBody();
|
||||||
|
$body = trim($body);
|
||||||
|
$body = rtrim($body, '=');
|
||||||
|
return PhabricatorHash::digestForIndex($body);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -897,7 +897,7 @@ EOBODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
$keys = id(new PhabricatorAuthSSHKey())->loadAllWhere(
|
$keys = id(new PhabricatorAuthSSHKey())->loadAllWhere(
|
||||||
'userPHID = %s',
|
'objectPHID = %s',
|
||||||
$this->getPHID());
|
$this->getPHID());
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$key->delete();
|
$key->delete();
|
||||||
|
|
|
@ -44,8 +44,7 @@ final class PhabricatorSettingsPanelSSHKeys
|
||||||
$this->getPanelURI());
|
$this->getPanelURI());
|
||||||
|
|
||||||
$id = nonempty($edit, $delete);
|
$id = nonempty($edit, $delete);
|
||||||
|
if ($id && (int)$id) {
|
||||||
if ($id) {
|
|
||||||
$key = id(new PhabricatorAuthSSHKeyQuery())
|
$key = id(new PhabricatorAuthSSHKeyQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($id))
|
->withIDs(array($id))
|
||||||
|
@ -59,8 +58,8 @@ final class PhabricatorSettingsPanelSSHKeys
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$key = new PhabricatorAuthSSHKey();
|
$key = id(new PhabricatorAuthSSHKey())
|
||||||
$key->setUserPHID($user->getPHID());
|
->setObjectPHID($user->getPHID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($delete) {
|
if ($delete) {
|
||||||
|
@ -89,7 +88,6 @@ final class PhabricatorSettingsPanelSSHKeys
|
||||||
|
|
||||||
$key->setKeyType($type);
|
$key->setKeyType($type);
|
||||||
$key->setKeyBody($body);
|
$key->setKeyBody($body);
|
||||||
$key->setKeyHash(md5($body));
|
|
||||||
$key->setKeyComment($comment);
|
$key->setKeyComment($comment);
|
||||||
|
|
||||||
$e_key = null;
|
$e_key = null;
|
||||||
|
@ -309,11 +307,10 @@ final class PhabricatorSettingsPanelSSHKeys
|
||||||
$body = $public_key->getBody();
|
$body = $public_key->getBody();
|
||||||
|
|
||||||
$key = id(new PhabricatorAuthSSHKey())
|
$key = id(new PhabricatorAuthSSHKey())
|
||||||
->setUserPHID($user->getPHID())
|
->setObjectPHID($user->getPHID())
|
||||||
->setName('id_rsa_phabricator')
|
->setName('id_rsa_phabricator')
|
||||||
->setKeyType($type)
|
->setKeyType($type)
|
||||||
->setKeyBody($body)
|
->setKeyBody($body)
|
||||||
->setKeyHash(md5($body))
|
|
||||||
->setKeyComment(pht('Generated'))
|
->setKeyComment(pht('Generated'))
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue