Add timeout to ssh client
[teamcity/git-plugin.git] / git-agent / src / jetbrains / buildServer / buildTriggers / vcs / git / agent / command / impl / SshHandler.java
1 /*
2  * Copyright 2000-2018 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package jetbrains.buildServer.buildTriggers.vcs.git.agent.command.impl;
18
19 import jetbrains.buildServer.buildTriggers.vcs.git.AuthSettings;
20 import jetbrains.buildServer.buildTriggers.vcs.git.AuthenticationMethod;
21 import jetbrains.buildServer.buildTriggers.vcs.git.agent.AgentPluginConfig;
22 import jetbrains.buildServer.buildTriggers.vcs.git.agent.Context;
23 import jetbrains.buildServer.buildTriggers.vcs.git.agent.GitCommandLine;
24 import jetbrains.buildServer.log.Loggers;
25 import jetbrains.buildServer.ssh.TeamCitySshKey;
26 import jetbrains.buildServer.ssh.VcsRootSshKeyManager;
27 import jetbrains.buildServer.util.FileUtil;
28 import jetbrains.buildServer.vcs.VcsException;
29 import jetbrains.buildServer.vcs.VcsRoot;
30 import jetbrains.buildServer.version.ServerVersionHolder;
31 import jetbrains.buildServer.version.ServerVersionInfo;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34 import org.jetbrains.git4idea.ssh.GitSSHHandler;
35 import org.jetbrains.git4idea.ssh.GitSSHService;
36
37 import java.io.File;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.Vector;
42
43 /**
44  * SSH handler implementation
45  */
46 public class SshHandler implements GitSSHService.Handler {
47   /**
48    * The handler number
49    */
50   private final int myHandlerNo;
51   /**
52    * SSH service
53    */
54   private final GitSSHService mySsh;
55   private final AuthSettings myAuthSettings;
56   private final List<File> myFilesToClean = new ArrayList<File>();
57
58   /**
59    * The constructor that registers the handler in the SSH service and command line
60    *
61    * @param ssh the SSH service
62    * @param authSettings authentication settings
63    * @param cmd the command line to register with
64    * @throws VcsException if there is a problem with registering the handler
65    */
66   public SshHandler(@NotNull GitSSHService ssh,
67                     @Nullable VcsRootSshKeyManager sshKeyManager,
68                     @NotNull AuthSettings authSettings,
69                     @NotNull GitCommandLine cmd,
70                     @NotNull File tmpDir,
71                     @NotNull Context ctx) throws VcsException {
72     mySsh = ssh;
73     myAuthSettings = authSettings;
74     cmd.addEnvParam(GitSSHHandler.SSH_PORT_ENV, Integer.toString(mySsh.getXmlRcpPort()));
75     if (myAuthSettings.isIgnoreKnownHosts())
76       cmd.addEnvParam(GitSSHHandler.SSH_IGNORE_KNOWN_HOSTS_ENV, "true");
77     if (authSettings.getAuthMethod() == AuthenticationMethod.TEAMCITY_SSH_KEY) {
78       String keyId = authSettings.getTeamCitySshKeyId();
79       if (keyId != null && sshKeyManager != null) {
80         VcsRoot root = myAuthSettings.getRoot();
81         if (root != null) {
82           TeamCitySshKey key = sshKeyManager.getKey(root);
83           if (key != null) {
84             try {
85               File privateKey = FileUtil.createTempFile(tmpDir, "key", "", true);
86               myFilesToClean.add(privateKey);
87               FileUtil.writeFileAndReportErrors(privateKey, new String(key.getPrivateKey()));
88               cmd.addEnvParam(GitSSHHandler.TEAMCITY_PRIVATE_KEY_PATH, privateKey.getCanonicalPath());
89               String passphrase = myAuthSettings.getPassphrase();
90               cmd.addEnvParam(GitSSHHandler.TEAMCITY_PASSPHRASE, passphrase != null ? passphrase : "");
91             } catch (Exception e) {
92               deleteKeys();
93               throw new VcsException(e);
94             }
95           }
96         }
97       }
98     }
99     if (ctx.getSshMacType() != null)
100       cmd.addEnvParam(GitSSHHandler.TEAMCITY_SSH_MAC_TYPE, ctx.getSshMacType());
101     if (ctx.getPreferredSshAuthMethods() != null)
102       cmd.addEnvParam(GitSSHHandler.TEAMCITY_SSH_PREFERRED_AUTH_METHODS, ctx.getPreferredSshAuthMethods());
103     cmd.addEnvParam(GitSSHHandler.TEAMCITY_DEBUG_SSH, String.valueOf(Loggers.VCS.isDebugEnabled()));
104     AgentPluginConfig config = ctx.getConfig();
105     if (config != null)
106       cmd.addEnvParam(GitSSHHandler.TEAMCITY_SSH_IDLE_TIMEOUT_SECONDS, String.valueOf(config.getIdleTimeoutSeconds()));
107     String teamCityVersion = getTeamCityVersion();
108     if (teamCityVersion != null) {
109       cmd.addEnvParam(GitSSHHandler.TEAMCITY_VERSION, teamCityVersion);
110     }
111     try {
112       cmd.addEnvParam(GitSSHHandler.GIT_SSH_ENV, ssh.getScriptPath());
113     } catch (IOException e) {
114       deleteKeys();
115       throw new VcsException("SSH script cannot be generated", e);
116     }
117     myHandlerNo = ssh.registerHandler(this);
118     cmd.addEnvParam(GitSSHHandler.SSH_HANDLER_ENV, Integer.toString(myHandlerNo));
119   }
120
121   /**
122    * Unregister the handler
123    */
124   public void unregister() {
125     deleteKeys();
126     mySsh.unregisterHandler(myHandlerNo);
127   }
128
129
130   private void deleteKeys() {
131     for (File f : myFilesToClean) {
132       FileUtil.delete(f);
133     }
134   }
135
136   public boolean verifyServerHostKey(String hostname,
137                                      int port,
138                                      String serverHostKeyAlgorithm,
139                                      String serverHostKey,
140                                      boolean isNew) {
141     return false;
142   }
143
144   public String askPassphrase(String username, String keyPath, boolean resetPassword, String lastError) {
145     if (resetPassword) {
146       return null;
147     }
148     return myAuthSettings.getPassphrase();
149   }
150
151   public Vector<String> replyToChallenge(String username,
152                                          String name,
153                                          String instruction,
154                                          int numPrompts,
155                                          Vector<String> prompt,
156                                          Vector<Boolean> echo,
157                                          String lastError) {
158     return null;
159   }
160
161   public String askPassword(String username, boolean resetPassword, String lastError) {
162     // The password is injected into URL
163     return null;
164   }
165
166   @Nullable
167   private static String getTeamCityVersion() {
168     try {
169       ServerVersionInfo version = ServerVersionHolder.getVersion();
170       return "TeamCity Agent " + version.getDisplayVersion();
171     } catch (Exception e) {
172       return null;
173     }
174   }
175 }