File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\jenkins\slaves\JnlpSlaveAgentProtocol.java.html

1 package jenkins.slaves;
2
3 import hudson.AbortException;
4 import hudson.Extension;
5 import hudson.remoting.Channel;
6 import hudson.remoting.Channel.Listener;
7 import hudson.remoting.Engine;
8 import hudson.slaves.SlaveComputer;
9 import jenkins.AgentProtocol;
10 import jenkins.model.Jenkins;
11 import jenkins.security.HMACConfidentialKey;
12
13 import java.io.BufferedInputStream;
14 import java.io.BufferedOutputStream;
15 import java.io.DataInputStream;
16 import java.io.DataOutputStream;
17 import java.io.IOException;
18 import java.io.OutputStream;
19 import java.io.PrintWriter;
20 import java.net.Socket;
21 import java.util.logging.Level;
22 import java.util.logging.Logger;
23
24 /**
25 * {@link AgentProtocol} that accepts connection from slave agents.
26 *
27 * <h2>Security</h2>
28 * <p>
29 * Once connected, remote slave agents can send in commands to be
30 * executed on the master, so in a way this is like an rsh service.
31 * Therefore, it is important that we reject connections from
32 * unauthorized remote slaves.
33 *
34 * <p>
35 * We do this by computing HMAC of the slave name.
36 * This code is sent to the slave inside the <tt>.jnlp</tt> file
37 * (this file itself is protected by HTTP form-based authentication that
38 * we use everywhere else in Jenkins), and the slave sends this
39 * token back when it connects to the master.
40 * Unauthorized slaves can't access the protected <tt>.jnlp</tt> file,
41 * so it can't impersonate a valid slave.
42 *
43 * <p>
44 * We don't want to force the JNLP slave agents to be restarted
45 * whenever the server restarts, so right now this secret master key
46 * is generated once and used forever, which makes this whole scheme
47 * less secure.
48 *
49 * @author Kohsuke Kawaguchi
50 * @since 1.467
51 */
52 @Extension
53 public class JnlpSlaveAgentProtocol extends AgentProtocol {
54 @Override
55 public String getName() {
56 return "JNLP-connect";
57 }
58
59 @Override
60 public void handle(Socket socket) throws IOException, InterruptedException {
61 new Handler(socket).run();
62 }
63
64 protected static class Handler {
65 protected final Socket socket;
66
67 /**
68 * Wrapping Socket input stream.
69 */
70 protected final DataInputStream in;
71
72 /**
73 * For writing handshaking response.
74 *
75 * This is a poor design choice that we just carry forward for compatibility.
76 * For better protocol design, {@link DataOutputStream} is preferred for newer
77 * protocols.
78 */
79 protected final PrintWriter out;
80
81 public Handler(Socket socket) throws IOException {
82 this.socket = socket;
83 in = new DataInputStream(socket.getInputStream());
84 out = new PrintWriter(socket.getOutputStream(),true);
85 }
86
87 protected void run() throws IOException, InterruptedException {
88 final String secret = in.readUTF();
89 final String nodeName = in.readUTF();
90
91 if(!SLAVE_SECRET.mac(nodeName).equals(secret)) {
92 error(out, "Unauthorized access");
93 return;
94 }
95
96
97 SlaveComputer computer = (SlaveComputer) Jenkins.getInstance().getComputer(nodeName);
98 if(computer==null) {
99 error(out, "No such slave: "+nodeName);
100 return;
101 }
102
103 if(computer.getChannel()!=null) {
104 error(out, nodeName+" is already connected to this master. Rejecting this connection.");
105 return;
106 }
107
108 out.println(Engine.GREETING_SUCCESS);
109
110 jnlpConnect(computer);
111 }
112
113 protected Channel jnlpConnect(SlaveComputer computer) throws InterruptedException, IOException {
114 final String nodeName = computer.getName();
115 final OutputStream log = computer.openLogFile();
116 PrintWriter logw = new PrintWriter(log,true);
117 logw.println("JNLP agent connected from "+ socket.getInetAddress());
118
119 try {
120 computer.setChannel(new BufferedInputStream(socket.getInputStream()), new BufferedOutputStream(socket.getOutputStream()), log,
121 new Listener() {
122 @Override
123 public void onClosed(Channel channel, IOException cause) {
124 if(cause!=null)
125 LOGGER.log(Level.WARNING, Thread.currentThread().getName()+" for + " + nodeName + " terminated",cause);
126 try {
127 socket.close();
128 } catch (IOException e) {
129 // ignore
130 }
131 }
132 });
133 return computer.getChannel();
134 } catch (AbortException e) {
135 logw.println(e.getMessage());
136 logw.println("Failed to establish the connection with the slave");
137 throw e;
138 } catch (IOException e) {
139 logw.println("Failed to establish the connection with the slave " + nodeName);
140 e.printStackTrace(logw);
141 throw e;
142 }
143 }
144
145 protected void error(PrintWriter out, String msg) throws IOException {
146 out.println(msg);
147 LOGGER.log(Level.WARNING,Thread.currentThread().getName()+" is aborted: "+msg);
148 socket.close();
149 }
150 }
151
152 private static final Logger LOGGER = Logger.getLogger(JnlpSlaveAgentProtocol.class.getName());
153
154 /**
155 * This secret value is used as a seed for slaves.
156 */
157 public static final HMACConfidentialKey SLAVE_SECRET = new HMACConfidentialKey(JnlpSlaveAgentProtocol.class,"secret");
158 }
159