PageRenderTime 3314ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java

https://gitlab.com/CORP-RESELLER/jenkins
Java | 229 lines | 186 code | 15 blank | 28 comment | 4 complexity | cb9a95af1c8d4ca87fbeb28a3f98df78 MD5 | raw file
  1. package jenkins.security.s2m;
  2. import hudson.Extension;
  3. import hudson.FilePath;
  4. import hudson.Functions;
  5. import hudson.Util;
  6. import hudson.util.HttpResponses;
  7. import jenkins.model.Jenkins;
  8. import jenkins.util.io.FileBoolean;
  9. import org.apache.commons.io.FileUtils;
  10. import org.jenkinsci.remoting.Role;
  11. import org.jenkinsci.remoting.RoleSensitive;
  12. import org.kohsuke.stapler.HttpResponse;
  13. import org.kohsuke.stapler.QueryParameter;
  14. import org.kohsuke.stapler.StaplerProxy;
  15. import org.kohsuke.stapler.StaplerRequest;
  16. import org.kohsuke.stapler.interceptor.RequirePOST;
  17. import java.io.BufferedReader;
  18. import java.io.ByteArrayInputStream;
  19. import java.io.ByteArrayOutputStream;
  20. import java.io.File;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.InputStreamReader;
  24. import java.io.PrintStream;
  25. import java.util.Collection;
  26. import java.util.Enumeration;
  27. import java.util.logging.Logger;
  28. import static java.util.logging.Level.*;
  29. /**
  30. * Rules of whitelisting for {@link RoleSensitive} objects and {@link FilePath}s.
  31. *
  32. * @author Kohsuke Kawaguchi
  33. */
  34. @Extension
  35. public class AdminWhitelistRule implements StaplerProxy {
  36. /**
  37. * Ones that we rejected but want to run by admins.
  38. */
  39. public final CallableRejectionConfig rejected;
  40. /**
  41. * Callables that admins have whitelisted explicitly.
  42. */
  43. public final CallableWhitelistConfig whitelisted;
  44. /**
  45. * FilePath access pattern rules specified by the admin
  46. */
  47. public final FilePathRuleConfig filePathRules;
  48. private final Jenkins jenkins;
  49. private boolean masterKillSwitch;
  50. public AdminWhitelistRule() throws IOException, InterruptedException {
  51. this.jenkins = Jenkins.getInstance();
  52. // while this file is not a secret, write access to this file is dangerous,
  53. // so put this in the better-protected part of $JENKINS_HOME, which is in secrets/
  54. // overwrite 30-default.conf with what we think is the best from the core.
  55. // this file shouldn't be touched by anyone. For local customization, use other files in the conf dir.
  56. // 0-byte file is used as a signal from the admin to prevent this overwriting
  57. placeDefaultRule(
  58. new File(jenkins.getRootDir(), "secrets/whitelisted-callables.d/default.conf"),
  59. getClass().getResourceAsStream("callable.conf"));
  60. placeDefaultRule(
  61. new File(jenkins.getRootDir(), "secrets/filepath-filters.d/30-default.conf"),
  62. transformForWindows(getClass().getResourceAsStream("filepath-filter.conf")));
  63. this.whitelisted = new CallableWhitelistConfig(
  64. new File(jenkins.getRootDir(),"secrets/whitelisted-callables.d/gui.conf"));
  65. this.rejected = new CallableRejectionConfig(
  66. new File(jenkins.getRootDir(),"secrets/rejected-callables.txt"),
  67. whitelisted);
  68. this.filePathRules = new FilePathRuleConfig(
  69. new File(jenkins.getRootDir(),"secrets/filepath-filters.d/50-gui.conf"));
  70. this.masterKillSwitch = loadMasterKillSwitchFile();
  71. }
  72. /**
  73. * Reads the master kill switch.
  74. *
  75. * Instead of {@link FileBoolean}, we use a text file so that the admin can prevent Jenkins from
  76. * writing this to file.
  77. */
  78. private boolean loadMasterKillSwitchFile() {
  79. File f = getMasterKillSwitchFile();
  80. try {
  81. if (!f.exists()) return true;
  82. return Boolean.parseBoolean(FileUtils.readFileToString(f).trim());
  83. } catch (IOException e) {
  84. LOGGER.log(WARNING, "Failed to read "+f, e);
  85. return false;
  86. }
  87. }
  88. private File getMasterKillSwitchFile() {
  89. return new File(jenkins.getRootDir(),"secrets/slave-to-master-security-kill-switch");
  90. }
  91. /**
  92. * Transform path for Windows.
  93. */
  94. private InputStream transformForWindows(InputStream src) throws IOException {
  95. BufferedReader r = new BufferedReader(new InputStreamReader(src));
  96. ByteArrayOutputStream out = new ByteArrayOutputStream();
  97. PrintStream p = new PrintStream(out);
  98. String line;
  99. while ((line=r.readLine())!=null) {
  100. if (!line.startsWith("#") && Functions.isWindows())
  101. line = line.replace("/","\\\\");
  102. p.println(line);
  103. }
  104. p.close();
  105. return new ByteArrayInputStream(out.toByteArray());
  106. }
  107. private void placeDefaultRule(File f, InputStream src) throws IOException, InterruptedException {
  108. try {
  109. new FilePath(f).copyFrom(src);
  110. } catch (IOException e) {
  111. // we allow admins to create a read-only file here to block overwrite,
  112. // so this can fail legitimately
  113. if (!f.canWrite()) return;
  114. LOGGER.log(WARNING, "Failed to generate "+f,e);
  115. }
  116. }
  117. public boolean isWhitelisted(RoleSensitive subject, Collection<Role> expected, Object context) {
  118. if (masterKillSwitch)
  119. return true; // master kill switch is on. subsystem deactivated
  120. String name = subject.getClass().getName();
  121. if (whitelisted.contains(name))
  122. return true; // whitelisted by admin
  123. // otherwise record the problem and refuse to execute that
  124. rejected.report(subject.getClass());
  125. return false;
  126. }
  127. public boolean checkFileAccess(String op, File f) {
  128. // if the master kill switch is off, we allow everything
  129. if (masterKillSwitch)
  130. return true;
  131. return filePathRules.checkFileAccess(op, f);
  132. }
  133. @RequirePOST
  134. public HttpResponse doSubmit(StaplerRequest req) throws IOException {
  135. jenkins.checkPermission(Jenkins.RUN_SCRIPTS);
  136. String whitelist = Util.fixNull(req.getParameter("whitelist"));
  137. if (!whitelist.endsWith("\n"))
  138. whitelist+="\n";
  139. Enumeration e = req.getParameterNames();
  140. while (e.hasMoreElements()) {
  141. String name = (String) e.nextElement();
  142. if (name.startsWith("class:")) {
  143. whitelist += name.substring(6)+"\n";
  144. }
  145. }
  146. whitelisted.set(whitelist);
  147. String newRules = Util.fixNull(req.getParameter("filePathRules"));
  148. filePathRules.parseTest(newRules); // test first before writing a potentially broken rules
  149. filePathRules.set(newRules);
  150. return HttpResponses.redirectToDot();
  151. }
  152. /**
  153. * Approves all the currently rejected subjects
  154. */
  155. @RequirePOST
  156. public HttpResponse doApproveAll() throws IOException {
  157. StringBuilder buf = new StringBuilder();
  158. for (Class c : rejected.get()) {
  159. buf.append(c.getName()).append('\n');
  160. }
  161. whitelisted.append(buf.toString());
  162. return HttpResponses.ok();
  163. }
  164. /**
  165. * Approves specific callables by their names.
  166. */
  167. @RequirePOST
  168. public HttpResponse doApprove(@QueryParameter String value) throws IOException {
  169. whitelisted.append(value);
  170. return HttpResponses.ok();
  171. }
  172. public boolean getMasterKillSwitch() {
  173. return masterKillSwitch;
  174. }
  175. public void setMasterKillSwitch(boolean state) {
  176. try {
  177. jenkins.checkPermission(Jenkins.RUN_SCRIPTS);
  178. FileUtils.writeStringToFile(getMasterKillSwitchFile(),Boolean.toString(state));
  179. // treat the file as the canonical source of information in case write fails
  180. masterKillSwitch = loadMasterKillSwitchFile();
  181. } catch (IOException e) {
  182. LOGGER.log(WARNING, "Failed to write master kill switch", e);
  183. }
  184. }
  185. /**
  186. * Restricts the access to administrator.
  187. */
  188. @Override
  189. public Object getTarget() {
  190. jenkins.checkPermission(Jenkins.RUN_SCRIPTS);
  191. return this;
  192. }
  193. private static final Logger LOGGER = Logger.getLogger(AdminWhitelistRule.class.getName());
  194. }