in brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java [1780:1958]
protected UserCreation createUserStatements(@Nullable Image image, ConfigBag config) {
//NB: private key is not installed remotely, just used to get/validate the public key
boolean windows = isWindows(image, config);
String user = getUser(config);
String explicitLoginUser = config.get(LOGIN_USER);
String loginUser = groovyTruth(explicitLoginUser) ? explicitLoginUser : (image != null && image.getDefaultCredentials() != null) ? image.getDefaultCredentials().identity : null;
boolean dontCreateUser = config.get(DONT_CREATE_USER);
boolean grantUserSudo = config.get(GRANT_USER_SUDO);
OsCredential credential = LocationConfigUtils.getOsCredential(config);
credential.checkNoErrors().logAnyWarnings();
String passwordToSet = Strings.isNonBlank(credential.getPassword()) ? credential.getPassword() : Identifiers.makeRandomId(12);
List<Statement> statements = Lists.newArrayList();
LoginCredentials createdUserCreds = null;
if (dontCreateUser) {
// dontCreateUser:
// if caller has not specified a user, we'll just continue to use the loginUser;
// if caller *has*, we set up our credentials assuming that user and credentials already exist
if (Strings.isBlank(user)) {
// createdUserCreds returned from this method will be null;
// we will use the creds returned by jclouds on the node
LOG.info("Not setting up any user (subsequently using loginUser {})", user, loginUser);
config.put(USER, loginUser);
} else {
LOG.info("Not creating user {}, and not installing its password or authorizing keys (assuming it exists)", user);
if (credential.isUsingPassword()) {
createdUserCreds = LoginCredentials.builder().user(user).password(credential.getPassword()).build();
if (Boolean.FALSE.equals(config.get(DISABLE_ROOT_AND_PASSWORD_SSH))) {
statements.add(org.jclouds.scriptbuilder.statements.ssh.SshStatements.sshdConfig(ImmutableMap.of("PasswordAuthentication", "yes")));
}
} else if (credential.hasKey()) {
createdUserCreds = LoginCredentials.builder().user(user).privateKey(credential.getPrivateKeyData()).build();
}
}
} else if (windows) {
// TODO Generate statements to create the user.
// createdUserCreds returned from this method will be null;
// we will use the creds returned by jclouds on the node
LOG.warn("Not creating or configuring user on Windows VM, despite "+DONT_CREATE_USER.getName()+" set to false");
// TODO extractVmCredentials() will use user:publicKeyData defaults, if we don't override this.
// For linux, how would we configure Brooklyn to use the node.getCredentials() - i.e. the version
// that the cloud automatically generated?
if (config.get(USER) != null) config.put(USER, "");
if (config.get(PASSWORD) != null) config.put(PASSWORD, "");
if (config.get(PRIVATE_KEY_DATA) != null) config.put(PRIVATE_KEY_DATA, "");
if (config.get(PRIVATE_KEY_FILE) != null) config.put(PRIVATE_KEY_FILE, "");
if (config.get(PUBLIC_KEY_DATA) != null) config.put(PUBLIC_KEY_DATA, "");
if (config.get(PUBLIC_KEY_FILE) != null) config.put(PUBLIC_KEY_FILE, "");
} else if (Strings.isBlank(user) || user.equals(loginUser) || user.equals(ROOT_USERNAME)) {
boolean useKey = Strings.isNonBlank(credential.getPublicKeyData());
// For subsequent ssh'ing, we'll be using the loginUser
if (Strings.isBlank(user)) {
user = loginUser;
config.put(USER, user);
}
// Using the pre-existing loginUser; setup the publicKey/password so can login as expected
// *Always* change the password (unless dontCreateUser was specified)
statements.add(new ReplaceShadowPasswordEntry(Sha512Crypt.function(), user, passwordToSet));
createdUserCreds = LoginCredentials.builder().user(user).password(passwordToSet).build();
if (useKey) {
statements.add(new AuthorizeRSAPublicKeys("~"+user+"/.ssh", ImmutableList.of(credential.getPublicKeyData())));
if (Strings.isNonBlank(credential.getPrivateKeyData())) {
createdUserCreds = LoginCredentials.builder().user(user).privateKey(credential.getPrivateKeyData()).build();
}
}
if (!useKey || Boolean.FALSE.equals(config.get(DISABLE_ROOT_AND_PASSWORD_SSH))) {
// ensure password is permitted for ssh
statements.add(org.jclouds.scriptbuilder.statements.ssh.SshStatements.sshdConfig(ImmutableMap.of("PasswordAuthentication", "yes")));
if (user.equals(ROOT_USERNAME)) {
statements.add(org.jclouds.scriptbuilder.statements.ssh.SshStatements.sshdConfig(ImmutableMap.of("PermitRootLogin", "yes")));
}
}
} else {
String pubKey = credential.getPublicKeyData();
String privKey = credential.getPrivateKeyData();
if (credential.isEmpty()) {
/*
* TODO have an explicit `create_new_key_per_machine` config key.
* error if privateKeyData is set in this case.
* publicKeyData automatically added to EXTRA_SSH_KEY_URLS_TO_AUTH.
*
* if this config key is not set, use a key `brooklyn_id_rsa` and `.pub` in `MGMT_BASE_DIR`,
* with permission 0600, creating it if necessary, and logging the fact that this was created.
*/
if (!config.containsKey(PRIVATE_KEY_FILE) && loggedSshKeysHint.compareAndSet(false, true)) {
LOG.info("Default SSH keys not found or not usable; will create new keys for each machine. "
+ "Create ~/.ssh/id_rsa or "
+ "set "+PRIVATE_KEY_FILE.getName()+" / "+PRIVATE_KEY_PASSPHRASE.getName()+" / "+PASSWORD.getName()+" "
+ "as appropriate for this location if you wish to be able to log in without Brooklyn.");
}
KeyPair newKeyPair = SecureKeys.newKeyPair();
pubKey = SecureKeys.toPub(newKeyPair);
privKey = SecureKeys.toPem(newKeyPair);
LOG.debug("Brooklyn key being created for "+user+" at new machine "+this+" is:\n"+privKey);
}
// ensure credential is not used any more, as we have extracted all useful info
credential = null;
// Create the user
// note AdminAccess requires _all_ fields set, due to http://code.google.com/p/jclouds/issues/detail?id=1095
AdminAccess.Builder adminBuilder = AdminAccess.builder()
.adminUsername(user)
.grantSudoToAdminUser(grantUserSudo);
adminBuilder.cryptFunction(Sha512Crypt.function());
boolean useKey = Strings.isNonBlank(pubKey);
adminBuilder.cryptFunction(Sha512Crypt.function());
// always set this password; if not supplied, it will be a random string
adminBuilder.adminPassword(passwordToSet);
// log the password also, in case we need it
LOG.debug("Password '"+passwordToSet+"' being created for user '"+user+"' at the machine we are about to provision in "+this+"; "+
(useKey ? "however a key will be used to access it" : "this will be the only way to log in"));
if (grantUserSudo && config.get(JcloudsLocationConfig.DISABLE_ROOT_AND_PASSWORD_SSH)) {
// the default - set root password which we forget, because we have sudo acct
// (and lock out root and passwords from ssh)
adminBuilder.resetLoginPassword(true);
adminBuilder.loginPassword(Identifiers.makeRandomId(12));
} else {
adminBuilder.resetLoginPassword(false);
adminBuilder.loginPassword(Identifiers.makeRandomId(12)+"-ignored");
}
if (useKey) {
adminBuilder.authorizeAdminPublicKey(true).adminPublicKey(pubKey);
} else {
adminBuilder.authorizeAdminPublicKey(false).adminPublicKey(Identifiers.makeRandomId(12)+"-ignored");
}
// jclouds wants us to give it the private key, otherwise it might refuse to authorize the public key
// (in AdminAccess.build, if adminUsername != null && adminPassword != null);
// we don't want to give it the private key, but we *do* want the public key authorized;
// this code seems to trigger that.
// (we build the creds below)
adminBuilder.installAdminPrivateKey(false).adminPrivateKey(Identifiers.makeRandomId(12)+"-ignored");
// lock SSH means no root login and no passwordless login
// if we're using a password or we don't have sudo, then don't do this!
adminBuilder.lockSsh(useKey && grantUserSudo && config.get(JcloudsLocationConfig.DISABLE_ROOT_AND_PASSWORD_SSH));
statements.add(adminBuilder.build());
if (useKey) {
createdUserCreds = LoginCredentials.builder().user(user).privateKey(privKey).build();
} else if (passwordToSet!=null) {
createdUserCreds = LoginCredentials.builder().user(user).password(passwordToSet).build();
}
if (!useKey || Boolean.FALSE.equals(config.get(DISABLE_ROOT_AND_PASSWORD_SSH))) {
// ensure password is permitted for ssh
statements.add(org.jclouds.scriptbuilder.statements.ssh.SshStatements.sshdConfig(ImmutableMap.of("PasswordAuthentication", "yes")));
}
}
String customTemplateOptionsScript = config.get(CUSTOM_TEMPLATE_OPTIONS_SCRIPT_CONTENTS);
if (Strings.isNonBlank(customTemplateOptionsScript)) {
statements.add(new LiteralStatement(customTemplateOptionsScript));
}
LOG.debug("Machine we are about to create in "+this+" will be customized with: "+
statements);
return new UserCreation(createdUserCreds, statements);
}