Skip to content

Commit

Permalink
JENKINS-30749 - make Jenkins secure out of the box:
Browse files Browse the repository at this point in the history
* create initial admin user with difficult password (based on UUID)
* force login with password as security token
* force initial admin user creation
  • Loading branch information
kzantow committed Mar 5, 2016
1 parent ecf9e3b commit 5368c96
Show file tree
Hide file tree
Showing 39 changed files with 1,908 additions and 1,018 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/PluginManager.java
Expand Up @@ -1189,7 +1189,7 @@ public void run() {
break;
}
updateCenter.persistInstallStatus();
jenkins.setInstallState(InstallState.INITIAL_PLUGINS_INSTALLED);
jenkins.setInstallState(InstallState.INITIAL_PLUGINS_INSTALLING.getNextState());
InstallUtil.saveLastExecVersion();
}
}.start();
Expand Down
90 changes: 40 additions & 50 deletions core/src/main/java/hudson/model/UpdateCenter.java
Expand Up @@ -57,7 +57,8 @@
import jenkins.install.InstallUtil;
import jenkins.model.Jenkins;
import jenkins.util.io.OnMaster;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.apache.commons.codec.binary.Base64;
Expand All @@ -73,20 +74,19 @@
import javax.net.ssl.SSLHandshakeException;
import javax.servlet.ServletException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -131,7 +131,7 @@
*/
@ExportedBean
public class UpdateCenter extends AbstractModelObject implements Saveable, OnMaster {

private static final String UPDATE_CENTER_URL = System.getProperty(UpdateCenter.class.getName()+".updateCenterUrl","http://updates.jenkins-ci.org/");

/**
Expand All @@ -142,7 +142,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas

@Restricted(NoExternalUse.class)
public static final String ID_UPLOAD = "_upload";

/**
* {@link ExecutorService} that performs installation.
* @since 1.501
Expand All @@ -155,7 +155,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
*/
protected final ExecutorService updateService = Executors.newCachedThreadPool(
new NamingThreadFactory(new DaemonThreadFactory(), "Update site data downloader"));

/**
* List of created {@link UpdateCenterJob}s. Access needs to be synchronized.
*/
Expand Down Expand Up @@ -311,29 +311,16 @@ public HttpResponse doConnectionStatus(StaplerRequest request) {
}
}

/**
* Called to bypass install wizard
*/
@Restricted(DoNotUse.class) // WebOnly
public HttpResponse doCompleteInstall() {
if(isRestartRequiredForCompletion()) {
Jenkins.getActiveInstance().setInstallState(InstallState.RESTART);
}
InstallUtil.saveLastExecVersion();
Jenkins.getActiveInstance().setInstallState(InstallState.INITIAL_PLUGINS_INSTALLED);
return HttpResponses.okJSON();
}

/**
* Called to determine if there was an incomplete installation, what the statuses of the plugins are
*/
@Restricted(DoNotUse.class) // WebOnly
public HttpResponse doIncompleteInstallStatus() {
try {
Map<String,String> jobs = InstallUtil.getPersistedInstallStatus();
if(jobs == null) {
jobs = Collections.emptyMap();
}
Map<String,String> jobs = InstallUtil.getPersistedInstallStatus();
if(jobs == null) {
jobs = Collections.emptyMap();
}
return HttpResponses.okJSON(jobs);
} catch (Exception e) {
return HttpResponses.errorJSON(String.format("ERROR: %s", e.getMessage()));
Expand All @@ -353,16 +340,16 @@ public synchronized void persistInstallStatus() {
if (job instanceof InstallationJob) {
InstallationJob installationJob = (InstallationJob) job;
if(!installationJob.status.isSuccess()) {
activeInstalls = true;
activeInstalls = true;
}
}
}

if(activeInstalls) {
InstallUtil.persistInstallStatus(jobs); // save this info
InstallUtil.persistInstallStatus(jobs); // save this info
}
else {
InstallUtil.clearInstallStatus(); // clear this info
InstallUtil.clearInstallStatus(); // clear this info
}
}

Expand All @@ -379,7 +366,10 @@ public synchronized void persistInstallStatus() {
public HttpResponse doInstallStatus(StaplerRequest request) {
try {
String correlationId = request.getParameter("correlationId");
Map<String,Object> response = new HashMap<>();
response.put("state", Jenkins.getInstance().getInstallState().name());
List<Map<String, String>> installStates = new ArrayList<>();
response.put("jobs", installStates);
List<UpdateCenterJob> jobCopy = getJobs();

for (UpdateCenterJob job : jobCopy) {
Expand All @@ -400,7 +390,7 @@ public HttpResponse doInstallStatus(StaplerRequest request) {
}
}
}
return HttpResponses.okJSON(JSONArray.fromObject(installStates));
return HttpResponses.okJSON(JSONObject.fromObject(response));
} catch (Exception e) {
return HttpResponses.errorJSON(String.format("ERROR: %s", e.getMessage()));
}
Expand Down Expand Up @@ -558,7 +548,7 @@ public void doSafeRestart(StaplerRequest request, StaplerResponse response) thro
}
response.sendRedirect2(".");
}

/**
* Cancel all scheduled jenkins restarts
*/
Expand Down Expand Up @@ -845,16 +835,16 @@ public List<Plugin> getUpdates() {

return new ArrayList<Plugin>(pluginMap.values());
}

/**
* Ensure that all UpdateSites are up to date, without requiring a user to
* browse to the instance.
*
*
* @return a list of {@link FormValidation} for each updated Update Site
* @throws ExecutionException
* @throws InterruptedException
* @throws ExecutionException
* @throws InterruptedException
* @since 1.501
*
*
*/
public List<FormValidation> updateAllSites() throws InterruptedException, ExecutionException {
List <Future<FormValidation>> futures = new ArrayList<Future<FormValidation>>();
Expand All @@ -864,8 +854,8 @@ public List<FormValidation> updateAllSites() throws InterruptedException, Execut
futures.add(future);
}
}
List<FormValidation> results = new ArrayList<FormValidation>();

List<FormValidation> results = new ArrayList<FormValidation>();
for (Future<FormValidation> f : futures) {
results.add(f.get());
}
Expand Down Expand Up @@ -1211,7 +1201,7 @@ public Future<UpdateCenterJob> submit() {
public String getErrorMessage() {
return error != null ? error.getMessage() : null;
}

public Throwable getError() {
return error;
}
Expand All @@ -1226,18 +1216,18 @@ public class RestartJenkinsJob extends UpdateCenterJob {
*/
@Exported(inline=true)
public volatile RestartJenkinsJobStatus status = new Pending();

/**
* Cancel job
*/
*/
public synchronized boolean cancel() {
if (status instanceof Pending) {
status = new Canceled();
return true;
}
return false;
}

public RestartJenkinsJob(UpdateSite site) {
super(site);
}
Expand All @@ -1260,26 +1250,26 @@ public synchronized void run() {
public abstract class RestartJenkinsJobStatus {
@Exported
public final int id = iota.incrementAndGet();

}

public class Pending extends RestartJenkinsJobStatus {
@Exported
public String getType() {
return getClass().getSimpleName();
}
}

public class Running extends RestartJenkinsJobStatus {

}

public class Failure extends RestartJenkinsJobStatus {

}

public class Canceled extends RestartJenkinsJobStatus {

}
}

Expand Down Expand Up @@ -1603,7 +1593,7 @@ protected File getDestination() {
File baseDir = pm.rootDir;
return new File(baseDir, plugin.name + ".jpi");
}

private File getLegacyDestination() {
File baseDir = pm.rootDir;
return new File(baseDir, plugin.name + ".hpi");
Expand Down Expand Up @@ -1649,7 +1639,7 @@ protected void onSuccess() {
public String toString() {
return super.toString()+"[plugin="+plugin.title+"]";
}

/**
* Called when the download is completed to overwrite
* the old file with the new file.
Expand Down Expand Up @@ -1704,7 +1694,7 @@ protected File getDestination() {
File baseDir = pm.rootDir;
final File legacy = new File(baseDir, plugin.name + ".hpi");
if(legacy.exists()){
return legacy;
return legacy;
}
return new File(baseDir, plugin.name + ".jpi");
}
Expand Down
Expand Up @@ -29,37 +29,61 @@
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import javax.inject.Inject;
import java.util.Collections;
import java.util.List;

/**
* {@link AuthorizationStrategy} that grants full-control to authenticated user
* (other than anonymous users.)
* and optionally read access to anonymous users
*
* @author Kohsuke Kawaguchi
*/
public class FullControlOnceLoggedInAuthorizationStrategy extends AuthorizationStrategy {
/**
* Whether to allow read access, default behavior
* previously was true
*/
private boolean allowAnonymousRead = true;

@DataBoundConstructor
public FullControlOnceLoggedInAuthorizationStrategy() {
}

@Override
public ACL getRootACL() {
return THE_ACL;
return allowAnonymousRead ? ANONYMOUS_READ : AUTHENTICATED_READ;
}

public List<String> getGroups() {
return Collections.emptyList();
}

/**
* If true, anonymous read access will be allowed
*/
public boolean isAllowAnonymousRead() {
return allowAnonymousRead;
}

@DataBoundSetter
public void setAllowAnonymousRead(boolean allowAnonymousRead) {
this.allowAnonymousRead = allowAnonymousRead;
}

private static final SparseACL THE_ACL = new SparseACL(null);
private static final SparseACL AUTHENTICATED_READ = new SparseACL(null);
private static final SparseACL ANONYMOUS_READ = new SparseACL(null);

static {
THE_ACL.add(ACL.EVERYONE, Jenkins.ADMINISTER,true);
THE_ACL.add(ACL.ANONYMOUS, Jenkins.ADMINISTER,false);
THE_ACL.add(ACL.ANONYMOUS,Permission.READ,true);
ANONYMOUS_READ.add(ACL.EVERYONE, Jenkins.ADMINISTER,true);
ANONYMOUS_READ.add(ACL.ANONYMOUS, Jenkins.ADMINISTER,false);
ANONYMOUS_READ.add(ACL.ANONYMOUS, Permission.READ,true);

AUTHENTICATED_READ.add(ACL.EVERYONE, Jenkins.ADMINISTER, true);
AUTHENTICATED_READ.add(ACL.ANONYMOUS, Jenkins.ADMINISTER, false);
AUTHENTICATED_READ.add(ACL.ANONYMOUS, Permission.READ, false);
}

/**
Expand Down

0 comments on commit 5368c96

Please sign in to comment.