Skip to content
Permalink
Browse files

Merge pull request #2142 from jenkinsci/PR-2138

[FIXED JENKINS-33599] write file with admin password for installer
  • Loading branch information
kohsuke committed Mar 19, 2016
2 parents 904d9b1 + 1735f3c commit c4fb608390c55398193ba300c4a0c07676e6c000
@@ -1,30 +1,7 @@
package jenkins.install;

import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import hudson.BulkChange;
import hudson.model.Descriptor;
import hudson.model.User;
import hudson.model.UserProperty;
import hudson.model.UserPropertyDescriptor;
import hudson.FilePath;
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy;
import hudson.security.HudsonPrivateSecurityRealm;
import hudson.security.SecurityRealm;
@@ -33,10 +10,28 @@
import hudson.util.PluginServletFilter;
import jenkins.model.Jenkins;
import jenkins.security.s2m.AdminWhitelistRule;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponse;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.Locale;
import java.util.UUID;
import java.util.logging.Logger;

/**
* A Jenkins instance used during first-run to provide a limited set of services while
* initial installation is in progress
*
* @since 2.0
*/
public class SetupWizard {
/**
@@ -46,62 +41,68 @@

private final Logger LOGGER = Logger.getLogger(SetupWizard.class.getName());

public SetupWizard(Jenkins j) throws IOException {
User admin;
private final Jenkins jenkins;

public SetupWizard(Jenkins j) throws IOException, InterruptedException {
this.jenkins = j;
// Create an admin user by default with a
// difficult password
FilePath iapf = getInitialAdminPasswordFile();
if(j.getSecurityRealm() == null || j.getSecurityRealm() == SecurityRealm.NO_AUTHENTICATION) { // this seems very fragile
BulkChange bc = new BulkChange(j);

HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null);
j.setSecurityRealm(securityRealm);
String randomUUID = UUID.randomUUID().toString().replace("-", "").toLowerCase(Locale.ENGLISH);
admin = securityRealm.createAccount(SetupWizard.initialSetupAdminUserName, randomUUID);
admin.addProperty(new SetupWizard.AuthenticationKey(randomUUID));

// Lock Jenkins down:
FullControlOnceLoggedInAuthorizationStrategy authStrategy = new FullControlOnceLoggedInAuthorizationStrategy();
authStrategy.setAllowAnonymousRead(false);
j.setAuthorizationStrategy(authStrategy);
try{
HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null);
j.setSecurityRealm(securityRealm);
String randomUUID = UUID.randomUUID().toString().replace("-", "").toLowerCase(Locale.ENGLISH);

// Shut down all the ports we can by default:
j.setSlaveAgentPort(-1); // -1 to disable

// require a crumb issuer
j.setCrumbIssuer(new DefaultCrumbIssuer(false));

// set master -> slave security:
j.getInjector().getInstance(AdminWhitelistRule.class)
.setMasterKillSwitch(false);
// create an admin user
securityRealm.createAccount(SetupWizard.initialSetupAdminUserName, randomUUID);

// JENKINS-33599 - write to a file in the jenkins home directory
// most native packages of Jenkins creates a machine user account 'jenkins' to run Jenkins,
// and use group 'jenkins' for admins. So we allo groups to read this file
iapf.write(randomUUID, "UTF-8");
iapf.chmod(0640);

// Lock Jenkins down:
FullControlOnceLoggedInAuthorizationStrategy authStrategy = new FullControlOnceLoggedInAuthorizationStrategy();
authStrategy.setAllowAnonymousRead(false);
j.setAuthorizationStrategy(authStrategy);

// Shut down all the ports we can by default:
j.setSlaveAgentPort(-1); // -1 to disable

// require a crumb issuer
j.setCrumbIssuer(new DefaultCrumbIssuer(false));

// set master -> slave security:
j.getInjector().getInstance(AdminWhitelistRule.class)
.setMasterKillSwitch(false);

try{
j.save(); // !!
} finally {
bc.commit();
} finally {
bc.abort();
}
}
else {
admin = j.getUser(SetupWizard.initialSetupAdminUserName);
}

String setupKey = null;
if(admin != null && admin.getProperty(SetupWizard.AuthenticationKey.class) != null) {
setupKey = admin.getProperty(SetupWizard.AuthenticationKey.class).getKey();
}
if(setupKey != null) {
LOGGER.info("\n\n*************************************************************\n"
+ "*************************************************************\n"
+ "*************************************************************\n"
+ "\n"
+ "Jenkins initial setup is required. A security token is required to proceed. \n"
+ "Please use the following security token to proceed to installation: \n"
+ "\n"
+ "" + setupKey + "\n"
+ "\n"
+ "*************************************************************\n"
+ "*************************************************************\n"
+ "*************************************************************\n");
}
String setupKey = iapf.readToString().trim();

LOGGER.info("\n\n*************************************************************\n"
+ "*************************************************************\n"
+ "*************************************************************\n"
+ "\n"
+ "Jenkins initial setup is required. An admin user has been created and"
+ "a password generated. \n"
+ "Please use the following password to proceed to installation: \n"
+ "\n"
+ "" + setupKey + "\n"
+ "\n"
+ "This may also be found at: " + iapf.getRemote() + "\n"
+ "\n"
+ "*************************************************************\n"
+ "*************************************************************\n"
+ "*************************************************************\n");

try {
PluginServletFilter.addFilter(FORCE_SETUP_WIZARD_FILTER);
@@ -110,56 +111,25 @@ public SetupWizard(Jenkins j) throws IOException {
}
}

/**
* Gets the file used to store the initial admin password
*/
@Restricted(NoExternalUse.class) // use by Jelly
public FilePath getInitialAdminPasswordFile() {
return jenkins.getRootPath().child("secrets/initialAdminPassword");
}

/**
* Remove the setupWizard filter, ensure all updates are written to disk, etc
*/
public HttpResponse doCompleteInstall(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
Jenkins j = Jenkins.getActiveInstance();
User u = j.getUser("admin");
// JENKINS-33572 - without creating a new 'admin' user, auth key erroneously remained
if(u != null && u.getProperty(AuthenticationKey.class) != null) {
// There must be a better way of removing things...
Iterator<Map.Entry<Descriptor<UserProperty>,UserProperty>> entries = u.getProperties().entrySet().iterator();
while(entries.hasNext()) {
Map.Entry<?, ?> entry = entries.next();
if(entry.getValue() instanceof AuthenticationKey) {
entries.remove();
}
}
u.save();
}
j.setInstallState(InstallState.INITIAL_SETUP_COMPLETED);
public HttpResponse doCompleteInstall() throws IOException, ServletException {
jenkins.setInstallState(InstallState.INITIAL_SETUP_COMPLETED);
InstallUtil.saveLastExecVersion();
PluginServletFilter.removeFilter(FORCE_SETUP_WIZARD_FILTER);
// Also, clean up the setup wizard if it's completed
j.setSetupWizard(null);
jenkins.setSetupWizard(null);
return HttpResponses.okJSON();
}

// Stores a user property for the authentication key, which is really the auto-generated user's password
public static class AuthenticationKey extends UserProperty {
String key;

public AuthenticationKey() {
}

public AuthenticationKey(String key) {
this.key = key;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

@Override
public UserPropertyDescriptor getDescriptor() {
return null;
}
}

/**
* This filter will validate that the security token is provided
@@ -16,22 +16,17 @@

<h1>${%Unlock Jenkins}</h1>
<p>
${%To ensure Jenkins is securely set up by the administrator, a setup security token has been printed to the logs.}
<small>
<a href="https://jenkins-ci.org/redirect/find-jenkins-logs" target="_blank">
${%Not sure where to find the logs?}
</a>
</small>
${%jenkins.install.findSecurityTokenMessage(it.initialAdminPasswordFile)}
</p>
<p>${%Please copy the token and paste it below.}</p>
<p>${%Please copy the password and paste it below.}</p>
<j:if test="${error}">
<div class="alert alert-danger">
<strong>${%ERROR:} </strong>
${%There is a problem with the security token, please check the logs for the correct token}
${%The password entered is incorrect, please check the file for the correct password}
</div>
</j:if>
<div class="form-group ${error ? 'has-error' : ''}">
<label class="control-label" for="security-token">${%Security token}</label>
<label class="control-label" for="security-token">${%Administrator password}</label>
<input name="j_username" value="${j.setupWizard.initialSetupAdminUserName}" type="hidden"/>
<input id="security-token" class="form-control" name="j_password"/>
<link rel="stylesheet" href="${j.installWizardPath}.css" type="text/css" />
@@ -0,0 +1,2 @@
jenkins.install.findSecurityTokenMessage=To ensure Jenkins is securely set up by the administrator, \
a password has been generated and written to the file on the Jenkins server here: <br/><small><code>{0}</code></small>

0 comments on commit c4fb608

Please sign in to comment.
You can’t perform that action at this time.