Credentials in store

This commit is contained in:
Matt Mitchell 2016-11-07 14:24:10 -08:00
parent 08ebd64a07
commit a8e7188547
8 changed files with 113 additions and 99 deletions

11
pom.xml
View File

@ -116,6 +116,17 @@
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plain-credentials</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
<scm>

View File

@ -48,14 +48,12 @@ public class AzureVMAgent extends AbstractCloudSlave {
private final String cloudName;
private final String adminUserName;
private final String credentialsId;
private final String sshPrivateKey;
private final String sshPassPhrase;
private final String adminPassword;
private final String jvmOptions;
private boolean shutdownOnIdle;
@ -117,10 +115,9 @@ public class AzureVMAgent extends AbstractCloudSlave {
final RetentionStrategy<AzureVMComputer> retentionStrategy,
final List<? extends NodeProperty<?>> nodeProperties,
final String cloudName,
final String adminUserName,
final String credentialsId,
final String sshPrivateKey,
final String sshPassPhrase,
final String adminPassword,
final String jvmOptions,
final boolean shutdownOnIdle,
final boolean eligibleForReuse,
@ -143,10 +140,9 @@ public class AzureVMAgent extends AbstractCloudSlave {
this.cloudName = cloudName;
this.templateName = templateName;
this.adminUserName = adminUserName;
this.credentialsId = credentialsId;
this.sshPrivateKey = sshPrivateKey;
this.sshPassPhrase = sshPassPhrase;
this.adminPassword = adminPassword;
this.jvmOptions = jvmOptions;
this.shutdownOnIdle = shutdownOnIdle;
this.eligibleForReuse = eligibleForReuse;
@ -178,10 +174,9 @@ public class AzureVMAgent extends AbstractCloudSlave {
final Mode mode,
final String label,
final String cloudName,
final String adminUserName,
final String credentialsId,
final String sshPrivateKey,
final String sshPassPhrase,
final String adminPassword,
final String jvmOptions,
final boolean shutdownOnIdle,
final boolean eligibleForReuse,
@ -213,10 +208,9 @@ public class AzureVMAgent extends AbstractCloudSlave {
new AzureVMCloudRetensionStrategy(retentionTimeInMin),
Collections.<NodeProperty<?>>emptyList(),
cloudName,
adminUserName,
credentialsId,
sshPrivateKey,
sshPassPhrase,
adminPassword,
jvmOptions,
shutdownOnIdle,
eligibleForReuse,
@ -245,8 +239,8 @@ public class AzureVMAgent extends AbstractCloudSlave {
return mode;
}
public String getAdminUserName() {
return adminUserName;
public String getCredentialsId() {
return credentialsId;
}
public String getSubscriptionId() {
@ -285,10 +279,6 @@ public class AzureVMAgent extends AbstractCloudSlave {
return deploymentName;
}
public String getAdminPassword() {
return adminPassword;
}
public CleanUpAction getCleanUpAction() {
return cleanUpAction;
}
@ -478,7 +468,7 @@ public class AzureVMAgent extends AbstractCloudSlave {
public String toString() {
return "AzureVMAgent ["
+ "\n\tcloudName=" + cloudName
+ "\n\tadminUserName=" + adminUserName
+ "\n\tcredentialsId=" + credentialsId
+ "\n\tjvmOptions=" + jvmOptions
+ "\n\tshutdownOnIdle=" + shutdownOnIdle
+ "\n\tretentionTimeInMin=" + retentionTimeInMin

View File

@ -15,6 +15,14 @@
*/
package com.microsoft.azure;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import com.microsoft.azure.Messages;
import java.io.IOException;
import java.util.ArrayList;
@ -38,15 +46,21 @@ import hudson.RelativePath;
import hudson.model.Describable;
import hudson.model.TaskListener;
import hudson.model.Descriptor;
import hudson.model.Item;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.labels.LabelAtom;
import hudson.security.ACL;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import org.kohsuke.stapler.AncestorInPath;
/**
* This class defines the configuration of Azure instance templates
@ -96,9 +110,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
private final String initScript;
private final String adminUserName;
private final String adminPassword;
private final String credentialsId;
private final String agentWorkspace;
@ -146,8 +158,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
final String imageVersion,
final String agentLaunchMethod,
final String initScript,
final String adminUserName,
final String adminPassword,
final String credentialsId,
final String virtualNetworkName,
final String subnetName,
final String agentWorkspace,
@ -182,8 +193,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
this.shutdownOnIdle = shutdownOnIdle;
this.initScript = initScript;
this.agentLaunchMethod = agentLaunchMethod;
this.adminUserName = adminUserName;
this.adminPassword = adminPassword;
this.credentialsId = credentialsId;
this.virtualNetworkName = virtualNetworkName;
this.subnetName = subnetName;
this.agentWorkspace = agentWorkspace;
@ -274,12 +284,8 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
return initScript;
}
public String getAdminUserName() {
return adminUserName;
}
public String getAdminPassword() {
return adminPassword;
public String getCredentialsId() {
return credentialsId;
}
public String getVirtualNetworkName() {
@ -444,8 +450,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
imageVersion,
agentLaunchMethod,
initScript,
adminUserName,
adminPassword,
credentialsId,
virtualNetworkName,
subnetName,
retentionTimeInMin + "",
@ -478,6 +483,13 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
return model;
}
public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item owner) {
// when configuring the job, you only want those credentials that are available to ACL.SYSTEM selectable
// as we cannot select from a user's credentials unless they are the only user submitting the build
// (which we cannot assume) thus ACL.SYSTEM is correct here.
return new StandardListBoxModel().withAll(CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class, owner, ACL.SYSTEM, Collections.<DomainRequirement>emptyList()));
}
public ListBoxModel doFillOsTypeItems() throws IOException, ServletException {
ListBoxModel model = new ListBoxModel();
@ -564,17 +576,6 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
return FormValidation.ok();
}
public FormValidation doCheckAdminUserName(@QueryParameter final String value) {
if (StringUtils.isNotBlank(value)) {
if (AzureUtil.isValidUserName(value)) {
return FormValidation.ok();
} else {
return FormValidation.error(Messages.Azure_GC_UserName_Err());
}
}
return FormValidation.ok();
}
public FormValidation doCheckNoOfParallelJobs(@QueryParameter final String value) {
if (StringUtils.isNotBlank(value)) {
String result = AzureVMManagementServiceDelegate.verifyNoOfExecutors(value);
@ -644,8 +645,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
@QueryParameter String imageVersion,
@QueryParameter String agentLaunchMethod,
@QueryParameter String initScript,
@QueryParameter String adminUserName,
@QueryParameter String adminPassword,
@QueryParameter String credentialsId,
@QueryParameter String virtualNetworkName,
@QueryParameter String subnetName,
@QueryParameter String retentionTimeInMin,
@ -673,12 +673,11 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
+ "imageVersion: {17};\n\t"
+ "agentLaunchMethod: {18};\n\t"
+ "initScript: {19};\n\t"
+ "adminUserName: {20};\n\t"
+ "adminPassword: {21};\n\t"
+ "virtualNetworkName: {22};\n\t"
+ "subnetName: {23};\n\t"
+ "retentionTimeInMin: {24};\n\t"
+ "jvmOptions: {25};",
+ "credentialsId: {20};\n\t"
+ "virtualNetworkName: {21};\n\t"
+ "subnetName: {22};\n\t"
+ "retentionTimeInMin: {23};\n\t"
+ "jvmOptions: {24};",
new Object[] {
subscriptionId,
clientId,
@ -700,8 +699,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
imageVersion,
agentLaunchMethod,
initScript,
adminUserName,
(StringUtils.isNotBlank(adminPassword) ? "********" : null),
credentialsId,
virtualNetworkName,
subnetName,
retentionTimeInMin,
@ -736,8 +734,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
imageVersion,
agentLaunchMethod,
initScript,
adminUserName,
adminPassword,
credentialsId,
virtualNetworkName,
subnetName,
retentionTimeInMin,

View File

@ -15,6 +15,10 @@
*/
package com.microsoft.azure;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.fasterxml.jackson.databind.JsonNode;
import hudson.model.Descriptor.FormException;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -79,9 +83,11 @@ import com.microsoft.azure.util.CleanUpAction;
import com.microsoft.azure.util.Constants;
import com.microsoft.azure.util.ExecutionEngine;
import com.microsoft.azure.util.FailureStage;
import hudson.security.ACL;
import java.io.InputStream;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.logging.Level;
@ -246,8 +252,11 @@ public class AzureVMManagementServiceDelegate {
}
ObjectNode.class.cast(tmp.get("variables")).put("vmSize", template.getVirtualMachineSize());
ObjectNode.class.cast(tmp.get("variables")).put("adminUsername", template.getAdminUserName());
ObjectNode.class.cast(tmp.get("variables")).put("adminPassword", template.getAdminPassword());
// Grab the username/pass
StandardUsernamePasswordCredentials creds = AzureUtil.getCredentials(template.getCredentialsId());
ObjectNode.class.cast(tmp.get("variables")).put("adminUsername", creds.getUsername());
ObjectNode.class.cast(tmp.get("variables")).put("adminPassword", creds.getPassword().getPlainText());
if (StringUtils.isNotBlank(template.getStorageAccountName())) {
ObjectNode.class.cast(tmp.get("variables")).put("storageAccountName", template.getStorageAccountName());
@ -375,7 +384,7 @@ public class AzureVMManagementServiceDelegate {
azureAgent.setSshPort(Constants.DEFAULT_SSH_PORT);
LOGGER.log(Level.INFO, "Azure agent details:\nnodeName{0}\nadminUserName={1}\nshutdownOnIdle={2}\nretentionTimeInMin={3}\nlabels={4}",
new Object[] { azureAgent.getNodeName(), azureAgent.getAdminUserName(), azureAgent.isShutdownOnIdle(),
new Object[] { azureAgent.getNodeName(), azureAgent.getCredentialsId(), azureAgent.isShutdownOnIdle(),
azureAgent.getRetentionTimeInMin(), azureAgent.getLabelString()});
}
@ -459,10 +468,9 @@ public class AzureVMManagementServiceDelegate {
template.getUseAgentAlwaysIfAvail(),
template.getLabels(),
template.getAzureCloud().getDisplayName(),
template.getAdminUserName(),
template.getCredentialsId(),
null,
null,
template.getAdminPassword(),
template.getJvmOptions(),
template.isShutdownOnIdle(),
false,
@ -1005,8 +1013,7 @@ public class AzureVMManagementServiceDelegate {
* @param imageVersion
* @param agentLaunchMethod
* @param initScript
* @param adminUserName
* @param adminPassword
* @param credentialsId
* @param virtualNetworkName
* @param subnetName
* @param retentionTimeInMin
@ -1036,8 +1043,7 @@ public class AzureVMManagementServiceDelegate {
final String imageVersion,
final String agentLaunchMethod,
final String initScript,
final String adminUserName,
final String adminPassword,
final String credentialsId,
final String virtualNetworkName,
final String subnetName,
final String retentionTimeInMin,
@ -1069,13 +1075,6 @@ public class AzureVMManagementServiceDelegate {
return errors;
}
//verify password
validationResult = verifyAdminPassword(adminPassword);
addValidationResultIfFailed(validationResult, errors);
if (returnOnSingleError && errors.size() > 0) {
return errors;
}
//verify JVM Options
validationResult = verifyJvmOptions(jvmOptions);
addValidationResultIfFailed(validationResult, errors);
@ -1312,18 +1311,6 @@ public class AzureVMManagementServiceDelegate {
}
}
private static String verifyAdminPassword(final String adminPassword) {
if (StringUtils.isBlank(adminPassword)) {
return Messages.Azure_GC_Template_PWD_Null_Or_Empty();
}
if (AzureUtil.isValidPassword(adminPassword)) {
return Constants.OP_SUCCESS;
} else {
return Messages.Azure_GC_Template_PWD_Not_Valid();
}
}
private static String verifyJvmOptions(final String jvmOptions) {
if (StringUtils.isBlank(jvmOptions) || AzureUtil.isValidJvmOption(jvmOptions)) {
return Constants.OP_SUCCESS;

View File

@ -15,6 +15,10 @@
*/
package com.microsoft.azure.remote;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
@ -25,6 +29,8 @@ import com.microsoft.azure.AzureVMAgent;
import com.microsoft.azure.AzureVMComputer;
import com.microsoft.azure.AzureVMAgentTemplate;
import com.microsoft.azure.Messages;
import com.microsoft.azure.exceptions.AzureCloudException;
import com.microsoft.azure.util.AzureUtil;
import com.microsoft.azure.util.CleanUpAction;
import com.microsoft.azure.util.Constants;
import com.microsoft.azure.util.FailureStage;
@ -33,6 +39,7 @@ import hudson.model.Descriptor;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.remoting.Channel.Listener;
import hudson.security.ACL;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.OfflineCause;
import hudson.slaves.SlaveComputer;
@ -44,6 +51,7 @@ import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
@ -134,8 +142,11 @@ public class AzureVMAgentSSHLauncher extends ComputerLauncher {
// Execute initialization script
// Make sure to change file permission for execute if needed. TODO: need to test
// Grab the username/pass
StandardUsernamePasswordCredentials creds = AzureUtil.getCredentials(agent.getCredentialsId());
String command = "sh " + remoteInitFileName;
int exitStatus = executeRemoteCommand(session, command, logger, agent.getExecuteInitScriptAsRoot(), agent.getAdminPassword());
int exitStatus = executeRemoteCommand(session, command, logger, agent.getExecuteInitScriptAsRoot(), creds.getPassword().getPlainText());
if (exitStatus != 0) {
if (agent.getDoNotUseMachineIfInitFails()) {
LOGGER.log(Level.SEVERE, "AzureVMAgentSSHLauncher: launch: init script failed: exit code={0} (marking agent for deletion)", exitStatus);
@ -234,8 +245,6 @@ public class AzureVMAgentSSHLauncher extends ComputerLauncher {
new Object[] { dnsName, sshPort, e.getMessage() });
throw e;
}
}
private void copyFileToRemote(Session jschSession, InputStream stream, String remotePath) throws Exception {
@ -374,7 +383,10 @@ public class AzureVMAgentSSHLauncher extends ComputerLauncher {
while (true) {
currRetryCount++;
try {
session = getRemoteSession(agent.getAdminUserName(), agent.getAdminPassword(), agent.getPublicDNSName(),
// Grab the username/pass
StandardUsernamePasswordCredentials creds = AzureUtil.getCredentials(agent.getCredentialsId());
session = getRemoteSession(creds.getUsername(), creds.getPassword().getPlainText(), agent.getPublicDNSName(),
agent.getSshPort());
LOGGER.info("AzureVMAgentSSHLauncher: connectToSsh: Got remote connection");
} catch (Exception e) {

View File

@ -15,9 +15,17 @@
*/
package com.microsoft.azure.util;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.microsoft.azure.exceptions.AzureCloudException;
import hudson.security.ACL;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
public class AzureUtil {
@ -342,4 +350,18 @@ public class AzureUtil {
Constants.VM_NAME_HASH_LENGTH, numberOfDigits),
shortenedDeploymentHash);
}
public static StandardUsernamePasswordCredentials getCredentials(String credentialsId) throws AzureCloudException {
// Grab the pass
StandardUsernamePasswordCredentials creds = CredentialsMatchers.firstOrNull(CredentialsProvider.lookupCredentials(
StandardUsernamePasswordCredentials.class, Jenkins.getInstance(), ACL.SYSTEM,
Collections.<DomainRequirement>emptyList()),
CredentialsMatchers.withId(credentialsId));
if (creds == null) {
throw new AzureCloudException("Could not find credentials with id: " + credentialsId);
}
return creds;
}
}

View File

@ -1,5 +1,5 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:c="/lib/credentials" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<table width="100%">
<f:section title="${%General_Configuration}">
<f:entry title="${%Template_Name}" field="templateName" help="/plugin/azure-vm-agents-plugin/help-templateName.html">
@ -67,6 +67,10 @@
<f:select />
</f:entry>
<f:entry title="${%Admin_Credentials}" field="credentialsId" help="/plugin/azure-vm-agents-plugin/help-credentials.html">
<c:select expressionAllowed="false"/>
</f:entry>
<f:section title="${%Initialization_Configuration}">
<f:entry title="${%Init_Script}" field="initScript" help="/plugin/azure-vm-agents-plugin/help-initScript.html">
<f:textarea />
@ -80,14 +84,6 @@
<f:checkbox default="true"/>
</f:entry>
</f:section>
<f:entry title="${%Username}" field="adminUserName" help="/plugin/azure-vm-agents-plugin/help-adminUserName.html">
<f:textbox />
</f:entry>
<f:entry title="${%Password}" field="adminPassword" help="/plugin/azure-vm-agents-plugin/help-adminPassword.html">
<f:password />
</f:entry>
</f:section>
<f:advanced>

View File

@ -27,8 +27,7 @@ Init_Script=Initialization Script
Execute_Init_Script_As_Root=Run Initialization Script As Root (Linux Only)
Do_Not_Use_Machine_If_Init_Fails=Don't Use VM If Initialization Script Fails (Linux Only)
Username=Username
Password=Password
Admin_Credentials=Admin Credentials
Agent_Workspace=Agent Workspace
JVM_Options=JVM Options