diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index df062744286b66509e776779d71f58647c468bfe..7c841d06939861882c1b36445fa89d7b3170a7c5 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -40,6 +40,7 @@ import hudson.AbortException; import hudson.remoting.Launcher; import static hudson.slaves.SlaveComputer.LogHolder.SLAVE_LOG_HANDLER; import hudson.slaves.OfflineCause.ChannelTermination; +import hudson.util.Secret; import java.io.File; import java.io.OutputStream; @@ -58,6 +59,13 @@ import java.util.concurrent.Future; import java.security.Security; import hudson.util.io.ReopenableFileOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.security.GeneralSecurityException; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.RequestDispatcher; import jenkins.model.Jenkins; import jenkins.slaves.JnlpSlaveAgentProtocol; import org.kohsuke.stapler.StaplerRequest; @@ -67,7 +75,11 @@ import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpRedirect; import javax.servlet.ServletException; -import javax.servlet.http.HttpServletResponse; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponseWrapper; +import org.kohsuke.stapler.ResponseImpl; +import org.kohsuke.stapler.WebMethod; +import org.kohsuke.stapler.compression.FilterServletOutputStream; /** * {@link Computer} for {@link Slave}s. @@ -129,6 +141,9 @@ public class SlaveComputer extends Computer { return acceptingTasks; } + /** + * @since 1.498 + */ public String getJnlpMac() { return JnlpSlaveAgentProtocol.SLAVE_SECRET.mac(getName()); } @@ -535,6 +550,40 @@ public class SlaveComputer extends Computer { return new Slave.JnlpJar(fileName); } + @WebMethod(name="slave-agent.jnlp") + public void doSlaveAgentJnlp(StaplerRequest req, StaplerResponse res) throws IOException, ServletException { + RequestDispatcher view = req.getView(this, "slave-agent.jnlp.jelly"); + if ("true".equals(req.getParameter("encrypt"))) { + req.setAttribute("jnlpMac", "SLAVE_SECRET"); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + StaplerResponse temp = new ResponseImpl(req.getStapler(), new HttpServletResponseWrapper(res) { + @Override public ServletOutputStream getOutputStream() throws IOException { + return new FilterServletOutputStream(baos); + } + @Override public PrintWriter getWriter() throws IOException { + throw new IllegalStateException(); + } + }); + view.forward(req, temp); + byte[] jnlpMac = JnlpSlaveAgentProtocol.SLAVE_SECRET.mac(getName().getBytes("UTF-8")); + SecretKey key = new SecretKeySpec(jnlpMac, 0, /* export restrictions */ 128 / 8, "AES"); + byte[] encrypted; + try { + Cipher c = Secret.getCipher("AES"); + c.init(Cipher.ENCRYPT_MODE, key); + encrypted = c.doFinal(baos.toByteArray()); + } catch (GeneralSecurityException x) { + throw new IOException(x); + } + res.setContentType("application/octet-stream"); + res.getOutputStream().write(encrypted); + } else { + checkPermission(CONNECT); + req.setAttribute("jnlpMac", getJnlpMac()); + view.forward(req, res); + } + } + @Override protected void kill() { super.kill(); diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index deebb958cee68dee23164d672ff03743c21e46bf..a9e856af4e6fc9361895d0c6fdbad176ed030cb4 100755 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3567,6 +3567,8 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro || rest.startsWith("/adjuncts/") || rest.startsWith("/signup") || rest.startsWith("/tcpSlaveAgentListener") + // XXX SlaveComputer.doSlaveAgentJnlp; there should be an annotation to request unprotected access + || rest.matches("/computer/.+/slave-agent[.]jnlp") && "true".equals(Stapler.getCurrentRequest().getParameter("encrypt")) || rest.startsWith("/cli") || rest.startsWith("/federatedLoginService/") || rest.startsWith("/securityRealm")) diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly b/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly index cecd9d5e7374afe344fd4641f52a15e7b7309bc2..7f7c31a13e847988150dd09e33feb3b69cd6e75d 100644 --- a/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/main.jelly @@ -65,7 +65,8 @@ THE SOFTWARE.

${%Run from slave command line:}

-
java -jar slave.jar -jnlpUrl ${h.inferHudsonURL(request)}${it.url}slave-agent.jnlp -jnlpCredentials ${app.authentication.name}:your-API-token
+ +
java -jar slave.jar -jnlpUrl ${h.inferHudsonURL(request)}${it.url}slave-agent.jnlp -secret ${it.jnlpMac}
@@ -85,4 +86,4 @@ THE SOFTWARE.

- \ No newline at end of file + diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly b/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly index 317743245c989e2e773fc98defff3708d48fe560..12c7bb12e190f5fd303e461a9fb6b773399a4f7c 100644 --- a/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly +++ b/core/src/main/resources/hudson/slaves/SlaveComputer/slave-agent.jnlp.jelly @@ -31,8 +31,6 @@ THE SOFTWARE. - - ${it.checkPermission(connect)} - ${it.jnlpMac} + ${jnlpMac} ${it.node.nodeName} -tunnel diff --git a/pom.xml b/pom.xml index 78dde3bc43196ac21af6255bfcfca11308b7959f..94eb405382f67ebc2c5ed291df8c3b00c6f7633f 100644 --- a/pom.xml +++ b/pom.xml @@ -187,7 +187,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 2.21 + 2.22 diff --git a/test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java b/test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java index b4d43cd3b477e87f417264393c2af10d1ef3eb90..ef00eb050830e02dc5e8e3906c10d5b9ff259823 100644 --- a/test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java +++ b/test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java @@ -83,7 +83,7 @@ public class JnlpAccessWithSecuredHudsonTest extends HudsonTestCase { assertTrue(jarResource.getWebResponse().getContentType().toLowerCase(Locale.ENGLISH).startsWith("application/")); } - + // XXX this should be the only part with ANONYMOUS_READONLY try { jnlp = (XmlPage) jnlpAgent.goTo("computer/test/slave-agent.jnlp", "application/x-java-jnlp-file"); fail("anonymous users must not be able to get secrets"); @@ -91,4 +91,7 @@ public class JnlpAccessWithSecuredHudsonTest extends HudsonTestCase { assertEquals(HttpURLConnection.HTTP_FORBIDDEN, x.getStatusCode()); } } + + // XXX try to use -secret + }