Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
index b2d1d25f44..57dc83953d 100644
--- a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
@@ -1,311 +1,311 @@
<?php
abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
private $args;
private $repository;
private $hasWriteAccess;
private $shouldProxy;
private $baseRequestPath;
public function getRepository() {
if (!$this->repository) {
throw new Exception(pht('Repository is not available yet!'));
}
return $this->repository;
}
private function setRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function getArgs() {
return $this->args;
}
public function getEnvironment() {
$env = array(
DiffusionCommitHookEngine::ENV_USER => $this->getSSHUser()->getUsername(),
DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL => 'ssh',
);
$identifier = $this->getRequestIdentifier();
if ($identifier !== null) {
$env[DiffusionCommitHookEngine::ENV_REQUEST] = $identifier;
}
$remote_address = $this->getSSHRemoteAddress();
if ($remote_address !== null) {
$env[DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS] = $remote_address;
}
return $env;
}
/**
* Identify and load the affected repository.
*/
abstract protected function identifyRepository();
abstract protected function executeRepositoryOperations();
abstract protected function raiseWrongVCSException(
PhabricatorRepository $repository);
protected function getBaseRequestPath() {
return $this->baseRequestPath;
}
protected function writeError($message) {
$this->getErrorChannel()->write($message);
return $this;
}
protected function getCurrentDeviceName() {
$device = AlmanacKeys::getLiveDevice();
if ($device) {
return $device->getName();
}
return php_uname('n');
}
protected function shouldProxy() {
return $this->shouldProxy;
}
protected function getProxyCommand($for_write) {
$viewer = $this->getSSHUser();
$repository = $this->getRepository();
$is_cluster_request = $this->getIsClusterRequest();
$uri = $repository->getAlmanacServiceURI(
$viewer,
array(
'neverProxy' => $is_cluster_request,
'protocols' => array(
'ssh',
),
'writable' => $for_write,
));
if (!$uri) {
throw new Exception(
pht(
'Failed to generate an intracluster proxy URI even though this '.
'request was routed as a proxy request.'));
}
$uri = new PhutilURI($uri);
$username = AlmanacKeys::getClusterSSHUser();
if ($username === null) {
throw new Exception(
pht(
'Unable to determine the username to connect with when trying '.
'to proxy an SSH request within the Phabricator cluster.'));
}
$port = $uri->getPort();
$host = $uri->getDomain();
$key_path = AlmanacKeys::getKeyPath('device.key');
if (!Filesystem::pathExists($key_path)) {
throw new Exception(
pht(
'Unable to proxy this SSH request within the cluster: this device '.
'is not registered and has a missing device key (expected to '.
'find key at "%s").',
$key_path));
}
$options = array();
$options[] = '-o';
$options[] = 'StrictHostKeyChecking=no';
$options[] = '-o';
$options[] = 'UserKnownHostsFile=/dev/null';
// This is suppressing "added <address> to the list of known hosts"
// messages, which are confusing and irrelevant when they arise from
// proxied requests. It might also be suppressing lots of useful errors,
- // of course. Ideally, we would enforce host keys eventually.
+ // of course. Ideally, we would enforce host keys eventually. See T13121.
$options[] = '-o';
- $options[] = 'LogLevel=quiet';
+ $options[] = 'LogLevel=ERROR';
// NOTE: We prefix the command with "@username", which the far end of the
// connection will parse in order to act as the specified user. This
// behavior is only available to cluster requests signed by a trusted
// device key.
return csprintf(
'ssh %Ls -l %s -i %s -p %s %s -- %s %Ls',
$options,
$username,
$key_path,
$port,
$host,
'@'.$this->getSSHUser()->getUsername(),
$this->getOriginalArguments());
}
final public function execute(PhutilArgumentParser $args) {
$this->args = $args;
$viewer = $this->getSSHUser();
$have_diffusion = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDiffusionApplication',
$viewer);
if (!$have_diffusion) {
throw new Exception(
pht(
'You do not have permission to access the Diffusion application, '.
'so you can not interact with repositories over SSH.'));
}
$repository = $this->identifyRepository();
$this->setRepository($repository);
// NOTE: Here, we're just figuring out if this is a proxyable request to
// a clusterized repository or not. We don't (and can't) use the URI we get
// back directly.
// For example, we may get a read-only URI here but be handling a write
// request. We only care if we get back `null` (which means we should
// handle the request locally) or anything else (which means we should
// proxy it to an appropriate device).
$is_cluster_request = $this->getIsClusterRequest();
$uri = $repository->getAlmanacServiceURI(
$viewer,
array(
'neverProxy' => $is_cluster_request,
'protocols' => array(
'ssh',
),
));
$this->shouldProxy = (bool)$uri;
try {
return $this->executeRepositoryOperations();
} catch (Exception $ex) {
$this->writeError(get_class($ex).': '.$ex->getMessage());
return 1;
}
}
protected function loadRepositoryWithPath($path, $vcs) {
$viewer = $this->getSSHUser();
$info = PhabricatorRepository::parseRepositoryServicePath($path, $vcs);
if ($info === null) {
throw new Exception(
pht(
'Unrecognized repository path "%s". Expected a path like "%s", '.
'"%s", or "%s".',
$path,
'/diffusion/X/',
'/diffusion/123/',
'/source/thaumaturgy.git'));
}
$identifier = $info['identifier'];
$base = $info['base'];
$this->baseRequestPath = $base;
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIdentifiers(array($identifier))
->needURIs(true)
->executeOne();
if (!$repository) {
throw new Exception(
pht('No repository "%s" exists!', $identifier));
}
$is_cluster = $this->getIsClusterRequest();
$protocol = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
if (!$repository->canServeProtocol($protocol, false, $is_cluster)) {
throw new Exception(
pht(
'This repository ("%s") is not available over SSH.',
$repository->getDisplayName()));
}
if ($repository->getVersionControlSystem() != $vcs) {
$this->raiseWrongVCSException($repository);
}
return $repository;
}
protected function requireWriteAccess($protocol_command = null) {
if ($this->hasWriteAccess === true) {
return;
}
$repository = $this->getRepository();
$viewer = $this->getSSHUser();
if ($viewer->isOmnipotent()) {
throw new Exception(
pht(
'This request is authenticated as a cluster device, but is '.
'performing a write. Writes must be performed with a real '.
'user account.'));
}
$protocol = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
if ($repository->canServeProtocol($protocol, true)) {
$can_push = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
DiffusionPushCapability::CAPABILITY);
if (!$can_push) {
throw new Exception(
pht('You do not have permission to push to this repository.'));
}
} else {
if ($protocol_command !== null) {
throw new Exception(
pht(
'This repository is read-only over SSH (tried to execute '.
'protocol command "%s").',
$protocol_command));
} else {
throw new Exception(
pht('This repository is read-only over SSH.'));
}
}
$this->hasWriteAccess = true;
return $this->hasWriteAccess;
}
protected function shouldSkipReadSynchronization() {
$viewer = $this->getSSHUser();
// Currently, the only case where devices interact over SSH without
// assuming user credentials is when synchronizing before a read. These
// synchronizing reads do not themselves need to be synchronized.
if ($viewer->isOmnipotent()) {
return true;
}
return false;
}
protected function newPullEvent() {
$viewer = $this->getSSHUser();
$repository = $this->getRepository();
$remote_address = $this->getSSHRemoteAddress();
return id(new PhabricatorRepositoryPullEvent())
->setEpoch(PhabricatorTime::getNow())
->setRemoteAddress($remote_address)
->setRemoteProtocol(PhabricatorRepositoryPullEvent::PROTOCOL_SSH)
->setPullerPHID($viewer->getPHID())
->setRepositoryPHID($repository->getPHID());
}
}
diff --git a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php
index 1aab14b57b..b1eebd92a1 100644
--- a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php
+++ b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php
@@ -1,59 +1,62 @@
<?php
final class DrydockSSHCommandInterface extends DrydockCommandInterface {
private $credential;
private $connectTimeout;
private function loadCredential() {
if ($this->credential === null) {
$credential_phid = $this->getConfig('credentialPHID');
$this->credential = PassphraseSSHKey::loadFromPHID(
$credential_phid,
PhabricatorUser::getOmnipotentUser());
}
return $this->credential;
}
public function setConnectTimeout($timeout) {
$this->connectTimeout = $timeout;
return $this;
}
public function getExecFuture($command) {
$credential = $this->loadCredential();
$argv = func_get_args();
$argv = $this->applyWorkingDirectoryToArgv($argv);
$full_command = call_user_func_array('csprintf', $argv);
$flags = array();
+
+ // See T13121. Attempt to suppress the "Permanently added X to list of
+ // known hosts" message without suppressing anything important.
$flags[] = '-o';
- $flags[] = 'LogLevel=quiet';
+ $flags[] = 'LogLevel=ERROR';
$flags[] = '-o';
$flags[] = 'StrictHostKeyChecking=no';
$flags[] = '-o';
$flags[] = 'UserKnownHostsFile=/dev/null';
$flags[] = '-o';
$flags[] = 'BatchMode=yes';
if ($this->connectTimeout) {
$flags[] = '-o';
$flags[] = 'ConnectTimeout='.$this->connectTimeout;
}
return new ExecFuture(
'ssh %Ls -l %P -p %s -i %P %s -- %s',
$flags,
$credential->getUsernameEnvelope(),
$this->getConfig('port'),
$credential->getKeyfileEnvelope(),
$this->getConfig('host'),
$full_command);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 3, 3:21 PM (10 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
165951
Default Alt Text
(11 KB)

Event Timeline