diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java
index 24a7d02925..4c36030c00 100644
--- a/core/src/main/java/hudson/FilePath.java
+++ b/core/src/main/java/hudson/FilePath.java
@@ -629,7 +629,7 @@ public final class FilePath implements Serializable {
act(new SecureFileCallable() {
private static final long serialVersionUID = 1L;
public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- writing(f);
+ symlinking(f);
Util.createSymlink(f.getParentFile(),target,f.getName(),listener);
return null;
}
@@ -2755,6 +2755,17 @@ public final class FilePath implements Serializable {
return f;
}
+ /**
+ * Pass through 'f' after ensuring that we can create that symlink.
+ */
+ private File symlinking(File f) {
+ FilePathFilter filter = filterNonNull();
+ if (!f.exists())
+ filter.create(f);
+ filter.symlink(f);
+ return f;
+ }
+
/**
* Pass through 'f' after ensuring that we can delete that file.
*/
diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java
index 8d008e446a..4a1ed5337c 100644
--- a/core/src/main/java/jenkins/FilePathFilter.java
+++ b/core/src/main/java/jenkins/FilePathFilter.java
@@ -40,6 +40,13 @@ public abstract class FilePathFilter {
*/
public boolean write(File f) throws SecurityException { return false; }
+ /**
+ * Checks if a symlink can be created at 'f'
+ *
+ * On POSIX, this corresponds to the 'w' permission of the file itself.
+ */
+ public boolean symlink(File f) throws SecurityException { return false; }
+
/**
* Checks if the given directory can be created.
*
diff --git a/core/src/main/java/jenkins/FilePathFilterAggregator.java b/core/src/main/java/jenkins/FilePathFilterAggregator.java
index 859d36d3a3..8bf4949e17 100644
--- a/core/src/main/java/jenkins/FilePathFilterAggregator.java
+++ b/core/src/main/java/jenkins/FilePathFilterAggregator.java
@@ -99,6 +99,15 @@ class FilePathFilterAggregator extends FilePathFilter {
return defaultAction();
}
+ @Override
+ public boolean symlink(File f) throws SecurityException {
+ for (Entry e : all) {
+ if (e.filter.symlink(f))
+ return true;
+ }
+ return defaultAction();
+ }
+
@Override
public boolean create(File f) throws SecurityException {
for (Entry e : all) {
diff --git a/core/src/main/java/jenkins/ReflectiveFilePathFilter.java b/core/src/main/java/jenkins/ReflectiveFilePathFilter.java
index 9905524431..ce963943cd 100644
--- a/core/src/main/java/jenkins/ReflectiveFilePathFilter.java
+++ b/core/src/main/java/jenkins/ReflectiveFilePathFilter.java
@@ -26,6 +26,11 @@ public abstract class ReflectiveFilePathFilter extends FilePathFilter {
return op("write", f);
}
+ @Override
+ public boolean symlink(File f) throws SecurityException {
+ return op("symlink",f);
+ }
+
@Override
public boolean mkdirs(File f) throws SecurityException {
return op("mkdirs", f);
diff --git a/core/src/main/java/jenkins/SoloFilePathFilter.java b/core/src/main/java/jenkins/SoloFilePathFilter.java
index cf9096705d..c2cfa58ef0 100644
--- a/core/src/main/java/jenkins/SoloFilePathFilter.java
+++ b/core/src/main/java/jenkins/SoloFilePathFilter.java
@@ -28,7 +28,7 @@ public final class SoloFilePathFilter extends FilePathFilter {
private boolean noFalse(String op, File f, boolean b) {
if (!b)
- throw new SecurityException("slave may not " + op + " " + f);
+ throw new SecurityException("slave may not " + op + " " + f+"\nSee http://jenkins-ci.org/security-144 for more details");
return true;
}
@@ -42,6 +42,11 @@ public final class SoloFilePathFilter extends FilePathFilter {
return noFalse("write",f,base.write(f));
}
+ @Override
+ public boolean symlink(File f) throws SecurityException {
+ return noFalse("symlink",f,base.write(f));
+ }
+
@Override
public boolean mkdirs(File f) throws SecurityException {
return noFalse("mkdirs",f,base.mkdirs(f));
diff --git a/core/src/main/java/jenkins/security/admin/package-info.java b/core/src/main/java/jenkins/security/admin/package-info.java
deleted file mode 100644
index e3607c45bd..0000000000
--- a/core/src/main/java/jenkins/security/admin/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Series of extensions that provide a hook for administrator to whitelist access from slave to master.
- */
-package jenkins.security.admin;
diff --git a/core/src/main/java/jenkins/security/admin/AdminCallableMonitor.java b/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java
similarity index 92%
rename from core/src/main/java/jenkins/security/admin/AdminCallableMonitor.java
rename to core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java
index 7df5a8b31e..d6a483af76 100644
--- a/core/src/main/java/jenkins/security/admin/AdminCallableMonitor.java
+++ b/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import hudson.Extension;
import hudson.FilePath;
@@ -27,6 +27,10 @@ public class AdminCallableMonitor extends AdministrativeMonitor {
@Inject
AdminWhitelistRule rule;
+ public AdminCallableMonitor() {
+ super("slaveToMasterAccessControl");
+ }
+
@Override
public boolean isActivated() {
return !rule.rejected.describe().isEmpty();
diff --git a/core/src/main/java/jenkins/security/admin/AdminCallableWhitelist.java b/core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java
similarity index 92%
rename from core/src/main/java/jenkins/security/admin/AdminCallableWhitelist.java
rename to core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java
index 22f552b7b7..a6bc100d31 100644
--- a/core/src/main/java/jenkins/security/admin/AdminCallableWhitelist.java
+++ b/core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java
@@ -1,8 +1,7 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import hudson.Extension;
import hudson.remoting.Callable;
-import jenkins.security.CallableWhitelist;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleSensitive;
diff --git a/core/src/main/java/jenkins/security/admin/AdminFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java
similarity index 93%
rename from core/src/main/java/jenkins/security/admin/AdminFilePathFilter.java
rename to core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java
index 6458d9483c..7832f82bbb 100644
--- a/core/src/main/java/jenkins/security/admin/AdminFilePathFilter.java
+++ b/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import hudson.Extension;
import hudson.remoting.ChannelBuilder;
@@ -29,7 +29,7 @@ public class AdminFilePathFilter extends ReflectiveFilePathFilter {
@Override
protected boolean op(String op, File path) throws SecurityException {
- return rule.filePathRules.checkFileAccess(op,path);
+ return rule.checkFileAccess(op,path);
}
@Extension
diff --git a/core/src/main/java/jenkins/security/admin/AdminWhitelistRule.java b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java
similarity index 58%
rename from core/src/main/java/jenkins/security/admin/AdminWhitelistRule.java
rename to core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java
index 8848f51996..880a76a388 100644
--- a/core/src/main/java/jenkins/security/admin/AdminWhitelistRule.java
+++ b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java
@@ -1,10 +1,13 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import hudson.Extension;
import hudson.FilePath;
+import hudson.Functions;
import hudson.Util;
import hudson.util.HttpResponses;
import jenkins.model.Jenkins;
+import jenkins.util.io.FileBoolean;
+import org.apache.commons.io.FileUtils;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleSensitive;
import org.kohsuke.stapler.HttpResponse;
@@ -13,12 +16,20 @@ import org.kohsuke.stapler.StaplerProxy;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.interceptor.RequirePOST;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
import java.util.Collection;
import java.util.Enumeration;
import java.util.logging.Logger;
+import static java.util.logging.Level.*;
+
/**
* Rules of whitelisting for {@link RoleSensitive} objects and {@link FilePath}s.
*
@@ -43,6 +54,8 @@ public class AdminWhitelistRule implements StaplerProxy {
private final Jenkins jenkins;
+ private boolean masterKillSwitch;
+
public AdminWhitelistRule() throws IOException, InterruptedException {
this.jenkins = Jenkins.getInstance();
@@ -54,10 +67,10 @@ public class AdminWhitelistRule implements StaplerProxy {
// 0-byte file is used as a signal from the admin to prevent this overwriting
placeDefaultRule(
new File(jenkins.getRootDir(), "secrets/whitelisted-callables.d/default.conf"),
- "callable.conf");
+ getClass().getResourceAsStream("callable.conf"));
placeDefaultRule(
new File(jenkins.getRootDir(), "secrets/filepath-filters.d/30-default.conf"),
- "filepath-filter.conf");
+ transformForWindows(getClass().getResourceAsStream("filepath-filter.conf")));
this.whitelisted = new CallableWhitelistConfig(
new File(jenkins.getRootDir(),"secrets/whitelisted-callables.d/gui.conf"));
@@ -65,16 +78,63 @@ public class AdminWhitelistRule implements StaplerProxy {
new File(jenkins.getRootDir(),"secrets/rejected-callables.txt"),
whitelisted);
this.filePathRules = new FilePathRuleConfig(
- new File(jenkins.getRootDir(),"secrets/filepath-filters.d/50-local.conf"));
+ new File(jenkins.getRootDir(),"secrets/filepath-filters.d/50-gui.conf"));
+ this.masterKillSwitch = loadMasterKillSwitchFile();
}
- private void placeDefaultRule(File f, String resource) throws IOException, InterruptedException {
- if (!f.exists() || f.length()>0) {
- new FilePath(f).copyFrom(getClass().getResource(resource));
+ /**
+ * Reads the master kill switch.
+ *
+ * Instead of {@link FileBoolean}, we use a text file so that the admin can prevent Jenkins from
+ * writing this to file.
+ */
+ private boolean loadMasterKillSwitchFile() {
+ File f = getMasterKillSwitchFile();
+ try {
+ if (!f.exists()) return true;
+ return Boolean.parseBoolean(FileUtils.readFileToString(f).trim());
+ } catch (IOException e) {
+ LOGGER.log(WARNING, "Failed to read "+f, e);
+ return false;
+ }
+ }
+
+ private File getMasterKillSwitchFile() {
+ return new File(jenkins.getRootDir(),"secrets/slave-to-master-security-kill-switch");
+ }
+
+ /**
+ * Transform path for Windows.
+ */
+ private InputStream transformForWindows(InputStream src) throws IOException {
+ BufferedReader r = new BufferedReader(new InputStreamReader(src));
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PrintStream p = new PrintStream(out);
+ String line;
+ while ((line=r.readLine())!=null) {
+ if (!line.startsWith("#") && Functions.isWindows())
+ line = line.replace("/","\\\\");
+ p.println(line);
+ }
+ p.close();
+ return new ByteArrayInputStream(out.toByteArray());
+ }
+
+ private void placeDefaultRule(File f, InputStream src) throws IOException, InterruptedException {
+ try {
+ new FilePath(f).copyFrom(src);
+ } catch (IOException e) {
+ // we allow admins to create a read-only file here to block overwrite,
+ // so this can fail legitimately
+ if (!f.canWrite()) return;
+ LOGGER.log(WARNING, "Failed to generate "+f,e);
}
}
public boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context) {
+ if (masterKillSwitch)
+ return true; // master kill switch is on. subsystem deactivated
+
String name = subject.getClass().getName();
if (whitelisted.contains(name))
@@ -85,6 +145,14 @@ public class AdminWhitelistRule implements StaplerProxy {
return false;
}
+ public boolean checkFileAccess(String op, File f) {
+ // if the master kill switch is off, we allow everything
+ if (masterKillSwitch)
+ return true;
+
+ return filePathRules.checkFileAccess(op, f);
+ }
+
@RequirePOST
public HttpResponse doSubmit(StaplerRequest req) throws IOException {
jenkins.checkPermission(Jenkins.ADMINISTER);
@@ -133,6 +201,21 @@ public class AdminWhitelistRule implements StaplerProxy {
return HttpResponses.ok();
}
+ public boolean getMasterKillSwitch() {
+ return masterKillSwitch;
+ }
+
+ public void setMasterKillSwitch(boolean state) {
+ try {
+ jenkins.checkPermission(Jenkins.ADMINISTER);
+ FileUtils.writeStringToFile(getMasterKillSwitchFile(),Boolean.toString(state));
+ // treat the file as the canonical source of information in case write fails
+ masterKillSwitch = loadMasterKillSwitchFile();
+ } catch (IOException e) {
+ LOGGER.log(WARNING, "Failed to write master kill switch", e);
+ }
+ }
+
/**
* Restricts the access to administrator.
*/
diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java
similarity index 94%
rename from core/src/main/java/jenkins/security/CallableDirectionChecker.java
rename to core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java
index 8b6507eca6..706d17c0be 100644
--- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java
+++ b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java
@@ -1,8 +1,10 @@
-package jenkins.security;
+package jenkins.security.s2m;
import hudson.Extension;
import hudson.remoting.Callable;
import hudson.remoting.ChannelBuilder;
+import jenkins.security.ChannelConfigurator;
+import jenkins.security.Roles;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleChecker;
import org.jenkinsci.remoting.RoleSensitive;
@@ -57,7 +59,7 @@ public class CallableDirectionChecker extends RoleChecker {
return;
}
- throw new SecurityException("Sending " + name + " from slave to master is prohibited. See http://jenkins-ci.org/security-144 for more details");
+ throw new SecurityException("Sending " + name + " from slave to master is prohibited.\nSee http://jenkins-ci.org/security-144 for more details");
}
/**
diff --git a/core/src/main/java/jenkins/security/admin/CallableRejectionConfig.java b/core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java
similarity index 98%
rename from core/src/main/java/jenkins/security/admin/CallableRejectionConfig.java
rename to core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java
index 0ce0e09948..fecdfb7a1f 100644
--- a/core/src/main/java/jenkins/security/admin/CallableRejectionConfig.java
+++ b/core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import com.google.common.collect.ImmutableSet;
import jenkins.model.Jenkins;
diff --git a/core/src/main/java/jenkins/security/CallableWhitelist.java b/core/src/main/java/jenkins/security/s2m/CallableWhitelist.java
similarity index 96%
rename from core/src/main/java/jenkins/security/CallableWhitelist.java
rename to core/src/main/java/jenkins/security/s2m/CallableWhitelist.java
index 7d69d7f886..50023a0cd0 100644
--- a/core/src/main/java/jenkins/security/CallableWhitelist.java
+++ b/core/src/main/java/jenkins/security/s2m/CallableWhitelist.java
@@ -1,10 +1,11 @@
-package jenkins.security;
+package jenkins.security.s2m;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.remoting.Callable;
import hudson.remoting.ChannelBuilder;
import jenkins.model.Jenkins;
+import jenkins.security.ChannelConfigurator;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleChecker;
import org.jenkinsci.remoting.RoleSensitive;
diff --git a/core/src/main/java/jenkins/security/admin/CallableWhitelistConfig.java b/core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java
similarity index 96%
rename from core/src/main/java/jenkins/security/admin/CallableWhitelistConfig.java
rename to core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java
index 063a5ebc37..a56d5f8b33 100644
--- a/core/src/main/java/jenkins/security/admin/CallableWhitelistConfig.java
+++ b/core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import com.google.common.collect.ImmutableSet;
import hudson.Util;
diff --git a/core/src/main/java/jenkins/security/admin/ConfigDirectory.java b/core/src/main/java/jenkins/security/s2m/ConfigDirectory.java
similarity index 98%
rename from core/src/main/java/jenkins/security/admin/ConfigDirectory.java
rename to core/src/main/java/jenkins/security/s2m/ConfigDirectory.java
index 7b2356265b..6356cc038e 100644
--- a/core/src/main/java/jenkins/security/admin/ConfigDirectory.java
+++ b/core/src/main/java/jenkins/security/s2m/ConfigDirectory.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import java.io.BufferedReader;
import java.io.File;
diff --git a/core/src/main/java/jenkins/security/admin/ConfigFile.java b/core/src/main/java/jenkins/security/s2m/ConfigFile.java
similarity index 98%
rename from core/src/main/java/jenkins/security/admin/ConfigFile.java
rename to core/src/main/java/jenkins/security/s2m/ConfigFile.java
index 5face75819..69d9358eed 100644
--- a/core/src/main/java/jenkins/security/admin/ConfigFile.java
+++ b/core/src/main/java/jenkins/security/s2m/ConfigFile.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import hudson.CopyOnWrite;
import hudson.util.TextFile;
diff --git a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java
similarity index 97%
rename from core/src/main/java/jenkins/security/DefaultFilePathFilter.java
rename to core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java
index 4696e25cf3..563d19269e 100644
--- a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java
+++ b/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java
@@ -22,12 +22,12 @@
* THE SOFTWARE.
*/
-package jenkins.security;
+package jenkins.security.s2m;
import hudson.Extension;
import hudson.remoting.ChannelBuilder;
import jenkins.ReflectiveFilePathFilter;
-import jenkins.security.admin.AdminFilePathFilter;
+import jenkins.security.ChannelConfigurator;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
diff --git a/core/src/main/java/jenkins/security/admin/FilePathRule.java b/core/src/main/java/jenkins/security/s2m/FilePathRule.java
similarity index 92%
rename from core/src/main/java/jenkins/security/admin/FilePathRule.java
rename to core/src/main/java/jenkins/security/s2m/FilePathRule.java
index 2fb82f6661..c247769e1f 100644
--- a/core/src/main/java/jenkins/security/admin/FilePathRule.java
+++ b/core/src/main/java/jenkins/security/s2m/FilePathRule.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import hudson.FilePath;
diff --git a/core/src/main/java/jenkins/security/admin/FilePathRuleConfig.java b/core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java
similarity index 75%
rename from core/src/main/java/jenkins/security/admin/FilePathRuleConfig.java
rename to core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java
index 4bbe3165ac..76289186bd 100644
--- a/core/src/main/java/jenkins/security/admin/FilePathRuleConfig.java
+++ b/core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -12,6 +12,8 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static hudson.Functions.isWindows;
+
/**
* Config file that lists {@link FilePathRule} rules.
*
@@ -37,9 +39,14 @@ class FilePathRuleConfig extends ConfigDirectory
line = line.trim();
if (line.isEmpty()) return null;
- line = line.replace("","/jobs/.+/builds/");
- line = line.replace("",Jenkins.getInstance().getRootDir().getPath());
+ line = line.replace("","/builds/");
line = line.replace("","[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]");
+ line = line.replace("","/jobs/.+");
+ line = line.replace("","\\Q"+Jenkins.getInstance().getRootDir().getPath()+"\\E");
+
+ // config file is always /-separated even on Windows, so bring it back to \-separation.
+ // This is done in the context of regex, so it has to be \\, which means in the source code it is \\\\
+ if (isWindows()) line = line.replace("/","\\\\");
Matcher m = PARSER.matcher(line);
if (!m.matches())
@@ -73,10 +80,13 @@ class FilePathRuleConfig extends ConfigDirectory
for (FilePathRule rule : get()) {
if (rule.op.matches(op)) {
- if (pathStr==null)
- // do not canonicalize nor absolutize, so that JENKINS_HOME that spans across
+ if (pathStr==null) {
+ // do not canonicalize, so that JENKINS_HOME that spans across
// multiple volumes via symlinks can look logically like one unit.
pathStr = path.getPath();
+ if (isWindows()) // Windows accepts '/' as separator, but for rule matching we want to normalize for consistent comparison
+ pathStr = pathStr.replace('/','\\');
+ }
if (rule.path.matcher(pathStr).matches()) {
// exclusion rule is only to bypass later path rules within #filePathRules,
diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java
new file mode 100644
index 0000000000..d83d436f4b
--- /dev/null
+++ b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java
@@ -0,0 +1,55 @@
+package jenkins.security.s2m;
+
+import hudson.Extension;
+import jenkins.model.GlobalConfiguration;
+import jenkins.model.GlobalConfigurationCategory;
+import jenkins.model.Jenkins;
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.StaplerRequest;
+
+import javax.inject.Inject;
+
+/**
+ * Exposes {@link AdminWhitelistRule#masterKillSwitch} to the admin.
+ *
+ * @author Kohsuke Kawaguchi
+ * @since 1.THU
+ */
+@Extension
+public class MasterKillSwitchConfiguration extends GlobalConfiguration {
+ @Inject
+ AdminWhitelistRule rule;
+
+ @Inject
+ Jenkins jenkins;
+
+ @Override
+ public GlobalConfigurationCategory getCategory() {
+ return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class);
+ }
+
+ public boolean getMasterToSlaveAccessControl() {
+ return !rule.getMasterKillSwitch();
+ }
+
+ @Override
+ public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
+ if (isRelevant()) {
+ // don't record on/off unless this becomes relevant, so that we can differentiate
+ // those who have disabled vs those who haven't cared.
+ rule.setMasterKillSwitch(!json.has("masterToSlaveAccessControl"));
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if the configuration of this subsystem becomes relevant.
+ * Unless this option is relevant, we don't let users choose this.
+ */
+ public boolean isRelevant() {
+ return jenkins.getComputers().length>1 // if there's no slave, there's no point
+ && jenkins.isUseSecurity() // if security is off, likewise this is pointless
+ ;
+ }
+}
+
diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java
new file mode 100644
index 0000000000..7a5df576f1
--- /dev/null
+++ b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java
@@ -0,0 +1,39 @@
+package jenkins.security.s2m;
+
+import hudson.Extension;
+import hudson.model.AdministrativeMonitor;
+import org.kohsuke.stapler.HttpResponse;
+import org.kohsuke.stapler.HttpResponses;
+import org.kohsuke.stapler.QueryParameter;
+
+import javax.inject.Inject;
+import java.io.IOException;
+
+/**
+ * If {@link AdminWhitelistRule#masterKillSwitch} is on, warn the user.
+ *
+ * @author Kohsuke Kawaguchi
+ * @since 1.THU
+ */
+@Extension
+public class MasterKillSwitchWarning extends AdministrativeMonitor {
+ @Inject
+ AdminWhitelistRule rule;
+
+ @Inject
+ MasterKillSwitchConfiguration config;
+
+ @Override
+ public boolean isActivated() {
+ return rule.getMasterKillSwitch() && config.isRelevant();
+ }
+
+ public HttpResponse doAct(@QueryParameter String dismiss) throws IOException {
+ if(dismiss!=null) {
+ disable(true);
+ return HttpResponses.redirectViaContextPath("/manage");
+ } else {
+ return HttpResponses.redirectViaContextPath("configureSecurity");
+ }
+ }
+}
diff --git a/core/src/main/java/jenkins/security/admin/OpMatcher.java b/core/src/main/java/jenkins/security/s2m/OpMatcher.java
similarity index 92%
rename from core/src/main/java/jenkins/security/admin/OpMatcher.java
rename to core/src/main/java/jenkins/security/s2m/OpMatcher.java
index cfebb38b88..7cb83623a2 100644
--- a/core/src/main/java/jenkins/security/admin/OpMatcher.java
+++ b/core/src/main/java/jenkins/security/s2m/OpMatcher.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import jenkins.ReflectiveFilePathFilter;
diff --git a/core/src/main/java/jenkins/security/admin/RejectedCallable.java b/core/src/main/java/jenkins/security/s2m/RejectedCallable.java
similarity index 93%
rename from core/src/main/java/jenkins/security/admin/RejectedCallable.java
rename to core/src/main/java/jenkins/security/s2m/RejectedCallable.java
index 363c7e7cfc..4cfd55c6f8 100644
--- a/core/src/main/java/jenkins/security/admin/RejectedCallable.java
+++ b/core/src/main/java/jenkins/security/s2m/RejectedCallable.java
@@ -1,4 +1,4 @@
-package jenkins.security.admin;
+package jenkins.security.s2m;
import hudson.PluginWrapper;
import jenkins.model.Jenkins;
diff --git a/core/src/main/java/jenkins/security/s2m/package-info.java b/core/src/main/java/jenkins/security/s2m/package-info.java
new file mode 100644
index 0000000000..84875777f6
--- /dev/null
+++ b/core/src/main/java/jenkins/security/s2m/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Slave -> master security.
+ */
+package jenkins.security.s2m;
diff --git a/core/src/main/resources/jenkins/diagnostics/PinningIsBlockingBundledPluginMonitor/message.properties b/core/src/main/resources/jenkins/diagnostics/PinningIsBlockingBundledPluginMonitor/message.properties
index 2236e9cec0..c1fc741c55 100644
--- a/core/src/main/resources/jenkins/diagnostics/PinningIsBlockingBundledPluginMonitor/message.properties
+++ b/core/src/main/resources/jenkins/diagnostics/PinningIsBlockingBundledPluginMonitor/message.properties
@@ -1,3 +1,4 @@
-blurb=The following plugins are currently pinned, and \
- this prevents Jenkins from using a newer version that''s bundled in the core. \
- We recommend you to unpin these plugins:
\ No newline at end of file
+blurb=This version of Jenkins comes with new versions of the following plugins that are currently \
+ pinned in \
+ the plugin manager. \
+ It is recommended to upgrade them to at least the version bundled with Jenkins.
diff --git a/core/src/main/resources/jenkins/security/admin/AdminCallableMonitor/message.jelly b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly
similarity index 100%
rename from core/src/main/resources/jenkins/security/admin/AdminCallableMonitor/message.jelly
rename to core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly
diff --git a/core/src/main/resources/jenkins/security/admin/AdminCallableMonitor/message.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties
similarity index 100%
rename from core/src/main/resources/jenkins/security/admin/AdminCallableMonitor/message.properties
rename to core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties
diff --git a/core/src/main/resources/jenkins/security/admin/AdminWhitelistRule/index.jelly b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly
similarity index 89%
rename from core/src/main/resources/jenkins/security/admin/AdminWhitelistRule/index.jelly
rename to core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly
index 27736403ec..9662491e1c 100644
--- a/core/src/main/resources/jenkins/security/admin/AdminWhitelistRule/index.jelly
+++ b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly
@@ -34,10 +34,18 @@ THE SOFTWARE.
specify which commands are open for slaves to execute and which ones are not.
While plugin developers work on updating this,
as an administrator, you can mark commands as OK for slaves to execute (aka "whitelisting".)
-
+
+
+
+
+ The slave → master access control subsystem is currently disabled.
+ This is unsafe if you have slaves from other less trusted people.
+ You can put it back on from
Global Security Configuration UI
+
+
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy
new file mode 100644
index 0000000000..bc95f575f7
--- /dev/null
+++ b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy
@@ -0,0 +1,11 @@
+package jenkins.security.s2m.MasterKillSwitchConfiguration
+
+def f=namespace(lib.FormTagLib)
+
+if (instance.isRelevant()) {
+ f.optionalBlock(field:"masterToSlaveAccessControl", title:_("Enable Slave \u2192 Master Access Control")) {
+ f.nested() {
+ raw _("Rules can be tweaked
here")
+ }
+ }
+}
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl.html b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl.html
new file mode 100644
index 0000000000..e05b9581df
--- /dev/null
+++ b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.jelly b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.jelly
new file mode 100644
index 0000000000..6192f8b897
--- /dev/null
+++ b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.jelly
@@ -0,0 +1,36 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties
new file mode 100644
index 0000000000..7c847a2111
--- /dev/null
+++ b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties
@@ -0,0 +1,2 @@
+blurb=Slave to master security subsystem is currently off. \
+
Please read the documentation and consider turning it on.
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/security/admin/callable.conf b/core/src/main/resources/jenkins/security/s2m/callable.conf
similarity index 77%
rename from core/src/main/resources/jenkins/security/admin/callable.conf
rename to core/src/main/resources/jenkins/security/s2m/callable.conf
index 700b0a3c85..070379c61f 100644
--- a/core/src/main/resources/jenkins/security/admin/callable.conf
+++ b/core/src/main/resources/jenkins/security/s2m/callable.conf
@@ -21,3 +21,9 @@ org.jenkinsci.plugins.gitclient.CliGitAPIImpl$GetPrivateKeys
# ssh-credentials plugin
com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator$1
+
+# cygwin-process-killer plugin
+com.synopsys.arc.jenkinsci.plugins.cygwinprocesskiller.CygwinProcessKiller$KillerRemoteCall
+
+# selenium-plugin
+hudson.plugins.selenium.JenkinsCapabilityMatcher$LabelMatcherCallable
diff --git a/core/src/main/resources/jenkins/security/admin/filepath-filter.conf b/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf
similarity index 83%
rename from core/src/main/resources/jenkins/security/admin/filepath-filter.conf
rename to core/src/main/resources/jenkins/security/s2m/filepath-filter.conf
index cd9b39bef7..faa7cffa16 100644
--- a/core/src/main/resources/jenkins/security/admin/filepath-filter.conf
+++ b/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf
@@ -13,12 +13,12 @@
# that slaves have absolutely no business with.
# Unless there are rules in other files allowing access to other portions of $JENKINS_HOME,
# this rule as it stands here has no effect, because anything left unspecified is rejected.
-deny all
/secrets/.*
+deny all /secrets($|/.*)
# User content is publicly readable, so quite safe for slaves to read, too.
# (The xunit plugin is known to read from here.)
# https://wiki.jenkins-ci.org/display/JENKINS/User+Content
-allow read,stat /userContent/.*
+allow read,stat /userContent($|/.*)
# In the next rule we grant general access under build directories, so first we protect
# the actual build record that Jenkins core reads, which nothing should be touching.
@@ -30,9 +30,11 @@ deny all /build.xml
# - cobertura writes coverage.xml
# - violations writes violations.xml and other content under violations/
# - dependency-check writes archive/artifacts.txt
-allow all /.+
+# But not allowing deletion to prevent data loss and symlink to prevent jailbreaking.
+allow create,mkdirs,read,stat,write /.+
-# TODO allow /cobertura/
+# cobertura also writes out annotated sources to a dir under the job:
+allow create,mkdirs,read,stat,write /jobs/.+/cobertura.*
# all the other accesses that aren't specified here will be left upto other rules in this directory.
# if no rules in those other files matches, then the access will be rejected.
diff --git a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java
index 0b1a40b680..9143042fcf 100644
--- a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java
+++ b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java
@@ -30,19 +30,32 @@ import hudson.remoting.Callable;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.Collection;
-import org.jenkinsci.remoting.Role;
+import jenkins.security.s2m.AdminWhitelistRule;
+import jenkins.security.s2m.DefaultFilePathFilter;
+import jenkins.security.s2m.MasterKillSwitchConfiguration;
import org.jenkinsci.remoting.RoleChecker;
+import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.jvnet.hudson.test.JenkinsRule;
+import javax.inject.Inject;
+
public class DefaultFilePathFilterTest {
@Rule public JenkinsRule r = new JenkinsRule();
+ @Inject
+ AdminWhitelistRule rule;
+
+ @Before
+ public void setUp() {
+ r.jenkins.getInjector().injectMembers(this);
+ rule.setMasterKillSwitch(false);
+ }
+
@Test public void remotePath() throws Exception {
Slave s = r.createOnlineSlave();
FilePath forward = s.getRootPath().child("forward");
diff --git a/war/pom.xml b/war/pom.xml
index 4762613f47..738521484a 100644
--- a/war/pom.xml
+++ b/war/pom.xml
@@ -280,7 +280,7 @@ THE SOFTWARE.
org.jenkins-ci.plugins
credentials
- 1.17
+ 1.18
hpi