private void configureGitSshCommand()

in git-common/src/main/java/jetbrains/buildServer/buildTriggers/vcs/git/command/GitCommandLine.java [195:300]


  private void configureGitSshCommand(@NotNull GitCommandSettings settings) throws VcsException {
    final AuthSettings authSettings = settings.getAuthSettings();
    if (authSettings == null) return;

    //Git has 2 environment variables related to ssh: GIT_SSH and GIT_SSH_COMMAND.
    //We use GIT_SSH_COMMAND because git resolves the executable specified in it,
    //i.e. it finds the 'ssh' executable which is not in the PATH on windows by default.

    //We specify the following command:
    //
    //  GIT_SSH_COMMAND=ssh -i "<path to decrypted key>" (-o "StrictHostKeyChecking=no" -vvv)
    //
    //The key is decrypted by us because on MacOS ssh seems to ignore the SSH_ASKPASS and
    //runs the MacOS graphical keychain helper. Disabling it via the -o "KeychainIntegration=no"
    //option results in the 'Bad configuration option: keychainintegration' error.

    final boolean ignoreKnownHosts = isIgnoreKnownHosts(authSettings);
    final String sendEnv = getSshRequestToken();
    File privateKey = null;
    try {
      privateKey = getPrivateKey(authSettings);
      if (privateKey != null || ignoreKnownHosts || StringUtil.isNotEmpty(sendEnv)) {
        final StringBuilder gitSshCommand = new StringBuilder("ssh");
        if (privateKey != null) {
          gitSshCommand.append(" -i \"").append(privateKey.getAbsolutePath().replace('\\', '/')).append("\"");
          if (!TeamCityProperties.getBoolean(SSH_FALLBACK_TO_DEFAULT_CONFIG)) {
            gitSshCommand.append(" -F \"none\"");
          }
        }
        if (ignoreKnownHosts) {
          gitSshCommand.append(" -o \"StrictHostKeyChecking=no\" -o \"UserKnownHostsFile=/dev/null\" -o \"GlobalKnownHostsFile=/dev/null\"");
        } else {
          String knownHosts = myCtx.getSshKnownHosts(authSettings);
          if (knownHosts == null) {
            myLogger.warning(
              "\"Ignore known hosts database\" setting is disabled, please make sure that per-user or global known host key database contains remote host key, otherwise git operations may hang or fail in unexpected way");
          } else {
            File knownHostsFile = FileUtil.createTempFile(myCtx.getTempDir(), "known_hosts", "", true);
            addPostAction(() -> FileUtil.delete(knownHostsFile));
            try (FileWriter writer = new FileWriter(knownHostsFile)) {
              writer.write(knownHosts);
            }
            String knownHostsPath = knownHostsFile.getAbsolutePath();
            gitSshCommand.append(String.format(" -o \"UserKnownHostsFile=%s\" -o \"GlobalKnownHostsFile=%s\"", knownHostsPath, knownHostsPath));
          }

        }

        if (myProxy.isSshProxyEnabled()) {
          int port = myProxy.getSshProxyPort();
          String fullProxyAddr = port != 0 ? String.format("%s:%d", myProxy.getSshProxyHost(), port) : myProxy.getSshProxyHost();
          String socksProxyCommand;
          if (myProxy.getCustomSshProxyCommand() != null) {
            String customCommand = myProxy.getCustomSshProxyCommand();
            customCommand = customCommand.replace("%host", myProxy.getSshProxyHost()); // not null because isSshProxyEnabled check
            customCommand = customCommand.replace("%port", String.valueOf(myProxy.getSshProxyPort()));
            socksProxyCommand = String.format(" -o ProxyCommand=\"%s %%h %%p\"", customCommand);
          } else if (SystemInfo.isWindows) {
            String strType;
            if (myProxy.getSshProxyType() == ProxyHandler.SshProxyType.HTTP) {
              // http proxy
              strType = "H";
            } else {
              // socks proxy
              strType = "S";
            }
            socksProxyCommand = String.format(" -o ProxyCommand=\"connect -%s %s %%h %%p\"", strType, fullProxyAddr);
          } else {
            String strType;
            switch (Objects.requireNonNull(myProxy.getSshProxyType())) {
              case SOCKS4: strType = "4"; break;
              case SOCKS5: strType = "5"; break;
              default: strType = "connect"; break;
            }
            socksProxyCommand = String.format(" -o ProxyCommand=\"nc -v -X %s -x %s %%h %%p\"", strType, fullProxyAddr);
          }
          gitSshCommand.append(socksProxyCommand);
        }

        if (authSettings.getAuthMethod().isKeyAuth()) {
          gitSshCommand.append(" -o \"PreferredAuthentications=publickey\" -o \"PasswordAuthentication=no\" -o \"KbdInteractiveAuthentication=no\"");
        } else {
          gitSshCommand.append(" -o \"PreferredAuthentications=password,keyboard-interactive\" -o \"PubkeyAuthentication=no\"");
        }
        gitSshCommand.append(" -o \"IdentitiesOnly=yes\"");

        if (StringUtil.isNotEmpty(sendEnv)) {
          gitSshCommand.append(" -o \"SetEnv TEAMCITY_SSH_REQUEST_TOKEN").append("=").append(sendEnv).append("\"");
        }
        final String sshCommandOptions = myCtx.getSshCommandOptions();
        if (StringUtil.isNotEmpty(sshCommandOptions)) {
          gitSshCommand.append(" ").append(sshCommandOptions);
        }
        if (myCtx.isDebugSsh() || settings.isTrace()) {
          gitSshCommand.append(" -vvv");
        }
        addEnvParam("GIT_SSH_COMMAND", gitSshCommand.toString());
      }
    } catch (Exception e) {
      if (privateKey != null)
        FileUtil.delete(privateKey);
      if (e instanceof VcsException)
        throw (VcsException) e;
      throw new VcsException(e);
    }
  }