diff --git a/core/src/main/java/hudson/TcpSlaveAgentListener.java b/core/src/main/java/hudson/TcpSlaveAgentListener.java index f5d28db6ff04b13bf2d3aa51eada94d5e11dc658..8fe3a1ec0f9ca0d966b92f8d99f1f6cb1bc5311e 100644 --- a/core/src/main/java/hudson/TcpSlaveAgentListener.java +++ b/core/src/main/java/hudson/TcpSlaveAgentListener.java @@ -4,16 +4,13 @@ import hudson.model.Hudson; import hudson.model.Slave.ComputerImpl; import hudson.remoting.Channel; import hudson.remoting.Channel.Listener; -import hudson.util.TextFile; import java.io.DataInputStream; -import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; -import java.security.SecureRandom; import java.util.logging.Level; import java.util.logging.Logger; @@ -28,7 +25,7 @@ import java.util.logging.Logger; * unauthorized remote slaves. * *

- * The approach here is to have {@link #secretKey a secret key} on the master. + * The approach here is to have {@link Hudson#getSecretKey() a secret key} on the master. * This key is sent to the slave inside the .jnlp file * (this file itself is protected by HTTP form-based authentication that * we use everywhere else in Hudson), and the slave sends this @@ -48,7 +45,6 @@ public class TcpSlaveAgentListener extends Thread { private final ServerSocket serverSocket; private volatile boolean shuttingDown; - private final String secretKey; public final int configuredPort; @@ -63,18 +59,6 @@ public class TcpSlaveAgentListener extends Thread { LOGGER.info("JNLP slave agent listener started on TCP port "+getPort()); - // get or create the secret - TextFile secretFile = new TextFile(new File(Hudson.getInstance().getRootDir(),"secret.key")); - if(secretFile.exists()) { - secretKey = secretFile.readTrim(); - } else { - SecureRandom sr = new SecureRandom(); - byte[] random = new byte[32]; - sr.nextBytes(random); - secretKey = Util.toHexString(random); - secretFile.write(secretKey); - } - start(); } @@ -85,8 +69,8 @@ public class TcpSlaveAgentListener extends Thread { return serverSocket.getLocalPort(); } - public String getSecretKey() { - return secretKey; + private String getSecretKey() { + return Hudson.getInstance().getSecretKey(); } public void run() { @@ -169,7 +153,7 @@ public class TcpSlaveAgentListener extends Thread { * Handles JNLP slave agent connection request. */ private void runJnlpConnect(DataInputStream in, PrintWriter out) throws IOException, InterruptedException { - if(!secretKey.equals(in.readUTF())) { + if(!getSecretKey().equals(in.readUTF())) { error(out, "Unauthorized access"); return; } diff --git a/core/src/main/java/hudson/model/Hudson.java b/core/src/main/java/hudson/model/Hudson.java index 7d87e9daef81bdee86871a879585cb1b9c2b7e17..f1138ff7a9a8213d95c782b5bf98d109de4c2867 100644 --- a/core/src/main/java/hudson/model/Hudson.java +++ b/core/src/main/java/hudson/model/Hudson.java @@ -37,9 +37,9 @@ import hudson.security.HudsonFilter; import hudson.security.LegacyAuthorizationStrategy; import hudson.security.LegacySecurityRealm; import hudson.security.Permission; +import hudson.security.PermissionGroup; import hudson.security.SecurityMode; import hudson.security.SecurityRealm; -import hudson.security.PermissionGroup; import hudson.tasks.BuildStep; import hudson.tasks.BuildWrapper; import hudson.tasks.BuildWrappers; @@ -58,14 +58,15 @@ import hudson.util.DaemonThreadFactory; import hudson.util.FormFieldValidator; import hudson.util.HudsonIsLoading; import hudson.util.MultipartFormDataParser; +import hudson.util.TextFile; import hudson.util.XStream2; import hudson.widgets.Widget; import net.sf.json.JSONObject; import org.acegisecurity.Authentication; import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthorityImpl; -import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.acegisecurity.ui.AbstractProcessingFilter; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; @@ -83,8 +84,8 @@ import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; +import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import javax.servlet.http.HttpSession; import java.io.File; import java.io.FileFilter; @@ -94,6 +95,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.URL; +import java.security.SecureRandom; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; @@ -287,6 +289,12 @@ public final class Hudson extends View implements ItemGroup, Node, return theInstance; } + /** + * Secrete key generated once and used for a long time, beyond + * container start/stop. + */ + private final String secretKey; + public Hudson(File root, ServletContext context) throws IOException { this.root = root; @@ -304,6 +312,18 @@ public final class Hudson extends View implements ItemGroup, Node, throw e; } + // get or create the secret + TextFile secretFile = new TextFile(new File(Hudson.getInstance().getRootDir(),"secret.key")); + if(secretFile.exists()) { + secretKey = secretFile.readTrim(); + } else { + SecureRandom sr = new SecureRandom(); + byte[] random = new byte[32]; + sr.nextBytes(random); + secretKey = Util.toHexString(random); + secretFile.write(secretKey); + } + // load plugins. pluginManager = new PluginManager(context); @@ -359,6 +379,15 @@ public final class Hudson extends View implements ItemGroup, Node, return pluginManager; } + /** + * Returns a secret key that survives across container start/stop. + *

+ * This value is useful for implementing some of the security features. + */ + public String getSecretKey() { + return secretKey; + } + /** * Gets the SCM descriptor by name. Primarily used for making them web-visible. */