HADOOP-14077. Add ability to access jmx via proxy. Contributed by Yuanbo Liu.
This commit is contained in:
parent
3a2e30fa9f
commit
172b23af33
@ -17,10 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.hadoop.security;
|
package org.apache.hadoop.security;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||||
import org.apache.hadoop.security.authorize.AuthorizationException;
|
import org.apache.hadoop.security.authorize.AuthorizationException;
|
||||||
import org.apache.hadoop.security.authorize.ProxyUsers;
|
import org.apache.hadoop.security.authorize.ProxyUsers;
|
||||||
import org.apache.hadoop.util.HttpExceptionUtils;
|
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
import org.apache.http.client.utils.URLEncodedUtils;
|
import org.apache.http.client.utils.URLEncodedUtils;
|
||||||
|
|
||||||
@ -41,6 +42,9 @@
|
|||||||
*/
|
*/
|
||||||
public class AuthenticationWithProxyUserFilter extends AuthenticationFilter {
|
public class AuthenticationWithProxyUserFilter extends AuthenticationFilter {
|
||||||
|
|
||||||
|
public static final Log LOG =
|
||||||
|
LogFactory.getLog(AuthenticationWithProxyUserFilter.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant used in URL's query string to perform a proxy user request, the
|
* Constant used in URL's query string to perform a proxy user request, the
|
||||||
* value of the <code>DO_AS</code> parameter is the user the request will be
|
* value of the <code>DO_AS</code> parameter is the user the request will be
|
||||||
@ -66,29 +70,30 @@ public class AuthenticationWithProxyUserFilter extends AuthenticationFilter {
|
|||||||
protected void doFilter(FilterChain filterChain, HttpServletRequest request,
|
protected void doFilter(FilterChain filterChain, HttpServletRequest request,
|
||||||
HttpServletResponse response) throws IOException, ServletException {
|
HttpServletResponse response) throws IOException, ServletException {
|
||||||
|
|
||||||
// authorize proxy user before calling next filter.
|
final String proxyUser = getDoAs(request);
|
||||||
String proxyUser = getDoAs(request);
|
|
||||||
if (proxyUser != null) {
|
if (proxyUser != null) {
|
||||||
UserGroupInformation realUser =
|
|
||||||
UserGroupInformation.createRemoteUser(request.getRemoteUser());
|
|
||||||
UserGroupInformation proxyUserInfo =
|
|
||||||
UserGroupInformation.createProxyUser(proxyUser, realUser);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ProxyUsers.authorize(proxyUserInfo, request.getRemoteAddr());
|
|
||||||
} catch (AuthorizationException ex) {
|
|
||||||
HttpExceptionUtils.createServletExceptionResponse(response,
|
|
||||||
HttpServletResponse.SC_FORBIDDEN, ex);
|
|
||||||
// stop filter chain if there is an Authorization Exception.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final UserGroupInformation finalProxyUser = proxyUserInfo;
|
|
||||||
// Change the remote user after proxy user is authorized.
|
// Change the remote user after proxy user is authorized.
|
||||||
request = new HttpServletRequestWrapper(request) {
|
final HttpServletRequest finalReq = request;
|
||||||
|
request = new HttpServletRequestWrapper(finalReq) {
|
||||||
|
|
||||||
|
private String getRemoteOrProxyUser() throws AuthorizationException {
|
||||||
|
UserGroupInformation realUser =
|
||||||
|
UserGroupInformation.createRemoteUser(finalReq.getRemoteUser());
|
||||||
|
UserGroupInformation proxyUserInfo =
|
||||||
|
UserGroupInformation.createProxyUser(proxyUser, realUser);
|
||||||
|
ProxyUsers.authorize(proxyUserInfo, finalReq.getRemoteAddr());
|
||||||
|
return proxyUserInfo.getUserName();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRemoteUser() {
|
public String getRemoteUser() {
|
||||||
return finalProxyUser.getUserName();
|
try {
|
||||||
|
return getRemoteOrProxyUser();
|
||||||
|
} catch (AuthorizationException ex) {
|
||||||
|
LOG.error("Unable to verify proxy user: " + ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,12 +157,25 @@ public void testAuthenticationWithProxyUser() throws Exception {
|
|||||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
|
Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
// userA cannot impersonate userC, it fails.
|
// userA cannot impersonate userC, but for /stacks, /jmx and /conf,
|
||||||
|
// they doesn't require users to authorize by default, so they
|
||||||
|
// can be accessed.
|
||||||
for (String servlet :
|
for (String servlet :
|
||||||
new String[]{"stacks", "jmx", "conf"}){
|
new String[]{"stacks", "jmx", "conf"}){
|
||||||
HttpURLConnection conn = authUrl
|
HttpURLConnection conn = authUrl
|
||||||
.openConnection(new URL(serverURL + servlet + "?doAs=userC"),
|
.openConnection(new URL(serverURL + servlet + "?doAs=userC"),
|
||||||
token);
|
token);
|
||||||
|
Assert.assertEquals(HttpURLConnection.HTTP_OK,
|
||||||
|
conn.getResponseCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// "/logs" and "/logLevel" require admin authorization,
|
||||||
|
// only userA has the access.
|
||||||
|
for (String servlet :
|
||||||
|
new String[]{"logLevel", "logs"}) {
|
||||||
|
HttpURLConnection conn = authUrl
|
||||||
|
.openConnection(new URL(serverURL + servlet + "?doAs=userC"),
|
||||||
|
token);
|
||||||
Assert.assertEquals(HttpURLConnection.HTTP_FORBIDDEN,
|
Assert.assertEquals(HttpURLConnection.HTTP_FORBIDDEN,
|
||||||
conn.getResponseCode());
|
conn.getResponseCode());
|
||||||
}
|
}
|
||||||
|
@ -392,10 +392,11 @@ void accessDenied(String s) {
|
|||||||
*/
|
*/
|
||||||
boolean checkAccess(Job job) {
|
boolean checkAccess(Job job) {
|
||||||
String remoteUser = request().getRemoteUser();
|
String remoteUser = request().getRemoteUser();
|
||||||
UserGroupInformation callerUGI = null;
|
if (remoteUser == null) {
|
||||||
if (remoteUser != null) {
|
return false;
|
||||||
callerUGI = UserGroupInformation.createRemoteUser(remoteUser);
|
|
||||||
}
|
}
|
||||||
|
UserGroupInformation callerUGI =
|
||||||
|
UserGroupInformation.createRemoteUser(remoteUser);
|
||||||
if (callerUGI != null && !job.checkAccess(callerUGI, JobACL.VIEW_JOB)) {
|
if (callerUGI != null && !job.checkAccess(callerUGI, JobACL.VIEW_JOB)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
||||||
import org.apache.hadoop.security.http.RestCsrfPreventionFilter;
|
import org.apache.hadoop.security.http.RestCsrfPreventionFilter;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.apache.hadoop.yarn.api.ApplicationBaseProtocol;
|
import org.apache.hadoop.yarn.api.ApplicationBaseProtocol;
|
||||||
@ -100,8 +101,8 @@ protected void render(Block html) {
|
|||||||
final GetApplicationReportRequest request =
|
final GetApplicationReportRequest request =
|
||||||
GetApplicationReportRequest.newInstance(appID);
|
GetApplicationReportRequest.newInstance(appID);
|
||||||
if (callerUGI == null) {
|
if (callerUGI == null) {
|
||||||
appReport =
|
throw new AuthenticationException(
|
||||||
appBaseProt.getApplicationReport(request).getApplicationReport();
|
"Failed to get user name from request");
|
||||||
} else {
|
} else {
|
||||||
appReport = callerUGI.doAs(
|
appReport = callerUGI.doAs(
|
||||||
new PrivilegedExceptionAction<ApplicationReport> () {
|
new PrivilegedExceptionAction<ApplicationReport> () {
|
||||||
@ -165,36 +166,76 @@ public ApplicationReport run() throws Exception {
|
|||||||
String schedulerPath = WebAppUtils.getResolvedRMWebAppURLWithScheme(conf) +
|
String schedulerPath = WebAppUtils.getResolvedRMWebAppURLWithScheme(conf) +
|
||||||
"/cluster/scheduler?openQueues=" + app.getQueue();
|
"/cluster/scheduler?openQueues=" + app.getQueue();
|
||||||
|
|
||||||
|
generateOverviewTable(app, schedulerPath, webUiType, appReport);
|
||||||
|
|
||||||
|
Collection<ApplicationAttemptReport> attempts;
|
||||||
|
try {
|
||||||
|
final GetApplicationAttemptsRequest request =
|
||||||
|
GetApplicationAttemptsRequest.newInstance(appID);
|
||||||
|
attempts = callerUGI.doAs(
|
||||||
|
new PrivilegedExceptionAction<Collection<
|
||||||
|
ApplicationAttemptReport>>() {
|
||||||
|
@Override
|
||||||
|
public Collection<ApplicationAttemptReport> run() throws Exception {
|
||||||
|
return appBaseProt.getApplicationAttempts(request)
|
||||||
|
.getApplicationAttemptList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
String message =
|
||||||
|
"Failed to read the attempts of the application " + appID + ".";
|
||||||
|
LOG.error(message, e);
|
||||||
|
html.p()._(message)._();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
createApplicationMetricsTable(html);
|
||||||
|
|
||||||
|
html._(InfoBlock.class);
|
||||||
|
|
||||||
|
generateApplicationTable(html, callerUGI, attempts);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate overview table for app web page.
|
||||||
|
* @param app app info.
|
||||||
|
* @param schedulerPath schedule path.
|
||||||
|
* @param webUiType web ui type.
|
||||||
|
* @param appReport app report.
|
||||||
|
*/
|
||||||
|
private void generateOverviewTable(AppInfo app, String schedulerPath,
|
||||||
|
String webUiType, ApplicationReport appReport) {
|
||||||
ResponseInfo overviewTable = info("Application Overview")
|
ResponseInfo overviewTable = info("Application Overview")
|
||||||
._("User:", schedulerPath, app.getUser())
|
._("User:", schedulerPath, app.getUser())
|
||||||
._("Name:", app.getName())
|
._("Name:", app.getName())
|
||||||
._("Application Type:", app.getType())
|
._("Application Type:", app.getType())
|
||||||
._("Application Tags:",
|
._("Application Tags:",
|
||||||
app.getApplicationTags() == null ? "" : app.getApplicationTags())
|
app.getApplicationTags() == null ? "" : app.getApplicationTags())
|
||||||
._("Application Priority:", clarifyAppPriority(app.getPriority()))
|
._("Application Priority:", clarifyAppPriority(app.getPriority()))
|
||||||
._(
|
._(
|
||||||
"YarnApplicationState:",
|
"YarnApplicationState:",
|
||||||
app.getAppState() == null ? UNAVAILABLE : clarifyAppState(app
|
app.getAppState() == null ? UNAVAILABLE : clarifyAppState(app
|
||||||
.getAppState()))
|
.getAppState()))
|
||||||
._("Queue:", schedulerPath, app.getQueue())
|
._("Queue:", schedulerPath, app.getQueue())
|
||||||
._("FinalStatus Reported by AM:",
|
._("FinalStatus Reported by AM:",
|
||||||
clairfyAppFinalStatus(app.getFinalAppStatus()))
|
clairfyAppFinalStatus(app.getFinalAppStatus()))
|
||||||
._("Started:", Times.format(app.getStartedTime()))
|
._("Started:", Times.format(app.getStartedTime()))
|
||||||
._(
|
._(
|
||||||
"Elapsed:",
|
"Elapsed:",
|
||||||
StringUtils.formatTime(Times.elapsed(app.getStartedTime(),
|
StringUtils.formatTime(Times.elapsed(app.getStartedTime(),
|
||||||
app.getFinishedTime())))
|
app.getFinishedTime())))
|
||||||
._(
|
._(
|
||||||
"Tracking URL:",
|
"Tracking URL:",
|
||||||
app.getTrackingUrl() == null
|
app.getTrackingUrl() == null
|
||||||
|| app.getTrackingUrl().equals(UNAVAILABLE) ? null : root_url(app
|
|| app.getTrackingUrl().equals(UNAVAILABLE) ? null : root_url(app
|
||||||
.getTrackingUrl()),
|
.getTrackingUrl()),
|
||||||
app.getTrackingUrl() == null
|
app.getTrackingUrl() == null
|
||||||
|| app.getTrackingUrl().equals(UNAVAILABLE) ? "Unassigned" : app
|
|| app.getTrackingUrl().equals(UNAVAILABLE) ? "Unassigned" : app
|
||||||
.getAppState() == YarnApplicationState.FINISHED
|
.getAppState() == YarnApplicationState.FINISHED
|
||||||
|| app.getAppState() == YarnApplicationState.FAILED
|
|| app.getAppState() == YarnApplicationState.FAILED
|
||||||
|| app.getAppState() == YarnApplicationState.KILLED ? "History"
|
|| app.getAppState() == YarnApplicationState.KILLED ? "History"
|
||||||
: "ApplicationMaster");
|
: "ApplicationMaster");
|
||||||
if (webUiType != null
|
if (webUiType != null
|
||||||
&& webUiType.equals(YarnWebParams.RM_WEB_UI)) {
|
&& webUiType.equals(YarnWebParams.RM_WEB_UI)) {
|
||||||
LogAggregationStatus status = getLogAggregationStatus();
|
LogAggregationStatus status = getLogAggregationStatus();
|
||||||
@ -226,38 +267,6 @@ public ApplicationReport run() throws Exception {
|
|||||||
overviewTable._("AM container Node Label expression:",
|
overviewTable._("AM container Node Label expression:",
|
||||||
app.getAmNodeLabelExpression() == null ? "<Not set>"
|
app.getAmNodeLabelExpression() == null ? "<Not set>"
|
||||||
: app.getAmNodeLabelExpression());
|
: app.getAmNodeLabelExpression());
|
||||||
|
|
||||||
Collection<ApplicationAttemptReport> attempts;
|
|
||||||
try {
|
|
||||||
final GetApplicationAttemptsRequest request =
|
|
||||||
GetApplicationAttemptsRequest.newInstance(appID);
|
|
||||||
if (callerUGI == null) {
|
|
||||||
attempts = appBaseProt.getApplicationAttempts(request)
|
|
||||||
.getApplicationAttemptList();
|
|
||||||
} else {
|
|
||||||
attempts = callerUGI.doAs(
|
|
||||||
new PrivilegedExceptionAction<Collection<ApplicationAttemptReport>> () {
|
|
||||||
@Override
|
|
||||||
public Collection<ApplicationAttemptReport> run() throws Exception {
|
|
||||||
return appBaseProt.getApplicationAttempts(request)
|
|
||||||
.getApplicationAttemptList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
String message =
|
|
||||||
"Failed to read the attempts of the application " + appID + ".";
|
|
||||||
LOG.error(message, e);
|
|
||||||
html.p()._(message)._();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
createApplicationMetricsTable(html);
|
|
||||||
|
|
||||||
html._(InfoBlock.class);
|
|
||||||
|
|
||||||
generateApplicationTable(html, callerUGI, attempts);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void generateApplicationTable(Block html,
|
protected void generateApplicationTable(Block html,
|
||||||
|
Loading…
Reference in New Issue
Block a user