blob: 6823d16c0ed8c56db307bfb8d8fa25dfdc1e048a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ambari.server.serveraction.kerberos;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
import org.apache.ambari.server.agent.CommandReport;
import org.apache.ambari.server.agent.stomp.TopologyHolder;
import org.apache.ambari.server.events.TopologyUpdateEvent;
import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
import org.apache.ambari.server.utils.ShellCommandUtil;
import org.apache.ambari.server.utils.StageUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
public class FinalizeKerberosServerAction extends KerberosServerAction {
private final static Logger LOG = LoggerFactory.getLogger(FinalizeKerberosServerAction.class);
private final TopologyHolder topologyHolder;
@Inject
public FinalizeKerberosServerAction(TopologyHolder topologyHolder) {
this.topologyHolder = topologyHolder;
}
/**
* Processes an identity as necessary.
* <p/>
* This implementation ensures that keytab files for the Ambari identities have the correct
* permissions. This is important in the event a secure cluster was created via Blueprints since
* some user accounts and groups may not have been available (at the OS level) when the keytab files
* were created.
*
* @param resolvedPrincipal a ResolvedKerberosPrincipal object to process
* @param operationHandler a KerberosOperationHandler used to perform Kerberos-related
* tasks for specific Kerberos implementations
* (MIT, Active Directory, etc...)
* @param kerberosConfiguration a Map of configuration properties from kerberos-env
* @param requestSharedDataContext a Map to be used a shared data among all ServerActions related
* to a given request @return null, always
* @throws AmbariException
*/
@Override
protected CommandReport processIdentity(ResolvedKerberosPrincipal resolvedPrincipal,
KerberosOperationHandler operationHandler,
Map<String, String> kerberosConfiguration,
Map<String, Object> requestSharedDataContext)
throws AmbariException {
if (resolvedPrincipal != null) {
// If the record's HOSTNAME value is "ambari-server", rather than an actual hostname it will
// not match the Ambari server's host name. This will occur if the there is no agent installed
// on the Ambari server host. This is ok, since any keytab files installed on the Ambari server
// host will already have the permissions set so that only the Ambari server can read it.
// There is no need to update the permissions for those keytab files so that installed services
// can access them since no services will be installed on the host.
if (StageUtils.getHostName().equals(resolvedPrincipal.getHostName())) {
// If the principal name exists in one of the shared data maps, it has been processed by the
// current "Enable Kerberos" or "Add component" workflow and therefore should already have
// the correct permissions assigned. The relevant keytab files can be skipped.
Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext);
if ((principalPasswordMap == null) || !principalPasswordMap.containsKey(resolvedPrincipal.getPrincipal())) {
String keytabFilePath = resolvedPrincipal.getKeytabPath();
if (!StringUtils.isEmpty(keytabFilePath)) {
Set<String> visited = (Set<String>) requestSharedDataContext.get(this.getClass().getName() + "_visited");
if (!visited.contains(keytabFilePath)) {
String ownerName = resolvedPrincipal.getResolvedKerberosKeytab().getOwnerName();
String ownerAccess = resolvedPrincipal.getResolvedKerberosKeytab().getOwnerAccess();
boolean ownerWritable = "w".equalsIgnoreCase(ownerAccess) || "rw".equalsIgnoreCase(ownerAccess);
boolean ownerReadable = "r".equalsIgnoreCase(ownerAccess) || "rw".equalsIgnoreCase(ownerAccess);
String groupName = resolvedPrincipal.getResolvedKerberosKeytab().getGroupName();
String groupAccess = resolvedPrincipal.getResolvedKerberosKeytab().getGroupAccess();
boolean groupWritable = "w".equalsIgnoreCase(groupAccess) || "rw".equalsIgnoreCase(groupAccess);
boolean groupReadable = "r".equalsIgnoreCase(groupAccess) || "rw".equalsIgnoreCase(groupAccess);
ShellCommandUtil.Result result;
String message;
result = ShellCommandUtil.setFileOwner(keytabFilePath, ownerName);
if (result.isSuccessful()) {
message = String.format("Updated the owner of the keytab file at %s to %s",
keytabFilePath, ownerName);
LOG.info(message);
actionLog.writeStdOut(message);
} else {
message = String.format("Failed to update the owner of the keytab file at %s to %s: %s",
keytabFilePath, ownerName, result.getStderr());
LOG.error(message);
actionLog.writeStdOut(message);
actionLog.writeStdErr(message);
}
result = ShellCommandUtil.setFileGroup(keytabFilePath, groupName);
if (result.isSuccessful()) {
message = String.format("Updated the group of the keytab file at %s to %s",
keytabFilePath, groupName);
LOG.info(message);
actionLog.writeStdOut(message);
} else {
message = String.format("Failed to update the group of the keytab file at %s to %s: %s",
keytabFilePath, groupName, result.getStderr());
LOG.error(message);
actionLog.writeStdOut(message);
actionLog.writeStdErr(message);
}
result = ShellCommandUtil.setFileMode(keytabFilePath,
ownerReadable, ownerWritable, false,
groupReadable, groupWritable, false,
false, false, false);
if (result.isSuccessful()) {
message = String.format("Updated the access mode of the keytab file at %s to owner:'%s' and group:'%s'",
keytabFilePath, ownerAccess, groupAccess);
LOG.info(message);
actionLog.writeStdOut(message);
} else {
message = String.format("Failed to update the access mode of the keytab file at %s to owner:'%s' and group:'%s': %s",
keytabFilePath, ownerAccess, groupAccess, result.getStderr());
LOG.error(message);
actionLog.writeStdOut(message);
actionLog.writeStdErr(message);
}
visited.add(keytabFilePath);
}
}
}
}
}
return null;
}
/**
* @param requestSharedDataContext a Map to be used a shared data among all ServerActions related
* to a given request
* @return
* @throws AmbariException
* @throws InterruptedException
*/
@Override
public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws AmbariException, InterruptedException {
String dataDirectoryPath = getCommandParameterValue(DATA_DIRECTORY);
if(getKDCType(getCommandParameters()) != KDCType.NONE) {
// Ensure the keytab files for the Ambari identities have the correct permissions
// This is important in the event a secure cluster was created via Blueprints since some
// user accounts and group may not have been created when the keytab files were created.
requestSharedDataContext.put(this.getClass().getName() + "_visited", new HashSet<String>());
processIdentities(requestSharedDataContext);
requestSharedDataContext.remove(this.getClass().getName() + "_visited");
}
deleteDataDirectory(dataDirectoryPath);
return sendTopologyUpdateEvent();
}
private CommandReport sendTopologyUpdateEvent() throws AmbariException, InterruptedException {
CommandReport commandReport = null;
try {
final TopologyUpdateEvent updateEvent = topologyHolder.getCurrentData();
topologyHolder.updateData(updateEvent);
} catch (Exception e) {
String message = "Could not send topology update event when enabling kerberos";
actionLog.writeStdErr(message);
LOG.error(message, e);
commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
}
return commandReport == null ? createCompletedCommandReport() : commandReport;
}
}