Skip to content

Commit 191c01c

Browse files
authored
HDDS-3128. Add ozone debug commands for Kerberos (#9868)
1 parent 57ec56b commit 191c01c

21 files changed

Lines changed: 1477 additions & 0 deletions

hadoop-ozone/cli-debug/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@
7474
<groupId>org.apache.commons</groupId>
7575
<artifactId>commons-lang3</artifactId>
7676
</dependency>
77+
<dependency>
78+
<groupId>org.apache.hadoop</groupId>
79+
<artifactId>hadoop-auth</artifactId>
80+
</dependency>
7781
<dependency>
7882
<groupId>org.apache.hadoop</groupId>
7983
<artifactId>hadoop-common</artifactId>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.debug.kerberos;
19+
20+
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED_DEFAULT;
21+
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_AUTHORIZATION_ENABLED;
22+
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY;
23+
24+
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
25+
import org.apache.hadoop.hdds.HddsConfigKeys;
26+
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
27+
import org.apache.hadoop.ozone.OzoneConfigKeys;
28+
import org.apache.hadoop.ozone.om.OMConfigKeys;
29+
30+
/**
31+
* Validates Ozone and Hadoop RPC authorization configuration.
32+
* Checks whether Hadoop authorization and Ozone ACL settings are enabled
33+
* and properly configured.
34+
* Missing or disabled authorization is reported as a warning.
35+
*/
36+
public class AuthorizationProbe extends ConfigProbe {
37+
38+
@Override
39+
public String name() {
40+
return "Authorization Configuration";
41+
}
42+
43+
@Override
44+
public ProbeResult test(OzoneConfiguration conf) {
45+
46+
// Print relevant configs
47+
print(conf, OZONE_SECURITY_ENABLED_KEY);
48+
print(conf, OZONE_AUTHORIZATION_ENABLED);
49+
print(conf, OzoneConfigKeys.OZONE_ACL_ENABLED);
50+
print(conf, OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS);
51+
52+
print(conf, CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHORIZATION);
53+
print(conf, OMConfigKeys.OZONE_OM_SECURITY_CLIENT_PROTOCOL_ACL);
54+
55+
print(conf, HddsConfigKeys.HDDS_SECURITY_CLIENT_DATANODE_CONTAINER_PROTOCOL_ACL);
56+
print(conf, HddsConfigKeys.HDDS_SECURITY_CLIENT_SCM_CONTAINER_PROTOCOL_ACL);
57+
print(conf, HddsConfigKeys.HDDS_SECURITY_CLIENT_SCM_BLOCK_PROTOCOL_ACL);
58+
print(conf, HddsConfigKeys.HDDS_SECURITY_CLIENT_SCM_CERTIFICATE_PROTOCOL_ACL);
59+
60+
ProbeResult result = ProbeResult.PASS;
61+
62+
// Validate Ozone security if enabled
63+
boolean ozoneSecurityEnabled = conf.getBoolean(OZONE_SECURITY_ENABLED_KEY,
64+
OzoneConfigKeys.OZONE_SECURITY_ENABLED_DEFAULT);
65+
66+
// If security disabled, no need to check further.
67+
if (!ozoneSecurityEnabled) {
68+
warn("Ozone security is disabled ("
69+
+ OZONE_SECURITY_ENABLED_KEY + "=" + ozoneSecurityEnabled + ". "
70+
+ "Authorization checks are not enforced in non-secure mode.");
71+
return ProbeResult.WARN; // not a failure
72+
}
73+
74+
// Validate Ozone authorization(master switch)
75+
boolean ozoneAuthEnabled = conf.getBoolean(OZONE_AUTHORIZATION_ENABLED,
76+
OzoneConfigKeys.OZONE_AUTHORIZATION_ENABLED_DEFAULT);
77+
78+
if (!ozoneAuthEnabled) {
79+
warn("Ozone authorization is disabled ("
80+
+ OZONE_AUTHORIZATION_ENABLED + "=" + ozoneAuthEnabled + ". "
81+
+ "No admin or ACL checks will be enforced.");
82+
result = ProbeResult.WARN;
83+
}
84+
85+
// Validate Hadoop authorization
86+
boolean hadoopAuthEnabled = conf.getBoolean(
87+
CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHORIZATION, false);
88+
89+
if (!hadoopAuthEnabled) {
90+
warn("Hadoop authorization is disabled");
91+
result = ProbeResult.WARN;
92+
}
93+
94+
// Validate Ozone ACLs enforcement
95+
boolean ozoneAclEnabled = conf.getBoolean(
96+
OzoneConfigKeys.OZONE_ACL_ENABLED, OZONE_ACL_ENABLED_DEFAULT);
97+
98+
if (!ozoneAclEnabled) {
99+
warn("Ozone ACLs are disabled while authorization is enabled. "
100+
+ "Only admin privilege checks will be enforced.");
101+
result = ProbeResult.WARN;
102+
}
103+
104+
return result;
105+
}
106+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.debug.kerberos;
19+
20+
import java.io.File;
21+
import java.io.InputStream;
22+
import java.nio.file.Files;
23+
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
24+
25+
/**
26+
* Base class for probes with common helpers.
27+
*/
28+
public abstract class ConfigProbe implements DiagnosticProbe {
29+
30+
/**
31+
* Generic key-value printer (used everywhere).
32+
*/
33+
protected void printValue(String key, String value) {
34+
if (value != null && value.startsWith("to Local user")) {
35+
System.out.println(key + " " + value);
36+
} else {
37+
System.out.println(key + " = " +
38+
(value == null ? "(unset)" : value));
39+
}
40+
}
41+
42+
/**
43+
* Config-specific printer.
44+
*/
45+
protected void print(OzoneConfiguration conf, String key) {
46+
printValue(key, conf.getTrimmed(key));
47+
}
48+
49+
/**
50+
* Warning message.
51+
*/
52+
protected void warn(String msg) {
53+
System.err.println("WARNING: " + msg);
54+
}
55+
56+
/**
57+
* Error message.
58+
*/
59+
protected void error(String msg) {
60+
System.err.println("ERROR: " + msg);
61+
}
62+
63+
/**
64+
* Validate that a file exists, is readable, and not empty.
65+
*/
66+
protected boolean canReadFile(File file, String description) {
67+
if (file == null) {
68+
error(description + " is null");
69+
return false;
70+
}
71+
72+
if (!file.exists()) {
73+
error(description + " does not exist: " + file);
74+
return false;
75+
}
76+
77+
if (!file.isFile()) {
78+
error(description + " is not a file: " + file);
79+
return false;
80+
}
81+
82+
if (!file.canRead()) {
83+
error(description + " is not readable: " + file);
84+
return false;
85+
}
86+
87+
try (InputStream fileInputStream = Files.newInputStream(file.toPath())) {
88+
if (fileInputStream.read() == -1) {
89+
error(description + " is empty or invalid: " + file);
90+
return false;
91+
}
92+
return true;
93+
} catch (Exception e) {
94+
error(description + " is not readable: " + file +
95+
" (" + e.getMessage() + ")");
96+
return false;
97+
}
98+
}
99+
100+
protected File getKrb5ConfigFile() {
101+
// 1. Check System Property (Highest priority)
102+
String path = System.getProperty("java.security.krb5.conf");
103+
104+
// 2. Check Environment Variable
105+
if (path == null || path.isEmpty()) {
106+
path = System.getenv("KRB5_CONFIG");
107+
}
108+
109+
// 3. Fallback to default
110+
if (path == null || path.isEmpty()) {
111+
path = "/etc/krb5.conf";
112+
}
113+
114+
return new File(path);
115+
}
116+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.debug.kerberos;
19+
20+
import java.io.ByteArrayOutputStream;
21+
import java.io.PrintStream;
22+
import java.nio.charset.StandardCharsets;
23+
import java.util.Arrays;
24+
import java.util.List;
25+
import java.util.concurrent.Callable;
26+
import org.apache.hadoop.hdds.cli.AbstractSubcommand;
27+
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
28+
import picocli.CommandLine;
29+
30+
/**
31+
* Kerberos diagnostic command for Ozone.
32+
* Usage:Validates each registered probe serially
33+
* and prints diagnostic summary.
34+
* Example: ozone debug kerberos diagnose
35+
*/
36+
@CommandLine.Command(name = "diagnose",
37+
description = "Diagnose Kerberos configuration issues.")
38+
public class DiagnoseSubcommand extends AbstractSubcommand
39+
implements Callable<Integer> {
40+
@Override
41+
public Integer call() throws Exception {
42+
out().println("\n== Ozone Kerberos Diagnostics ==\n");
43+
OzoneConfiguration conf = getOzoneConf();
44+
List<DiagnosticProbe> probes = Arrays.asList(
45+
new HostProbe(),
46+
new EnvironmentProbe(),
47+
new JvmKerberosProbe(),
48+
new KerberosConfigProbe(),
49+
new KinitProbe(),
50+
new KeytabProbe(),
51+
new KerberosTicketProbe(),
52+
new PrincipalMappingProbe(),
53+
new SecurityConfigProbe(),
54+
new AuthorizationProbe(),
55+
new HttpAuthProbe());
56+
57+
int pass = 0, warn = 0, fail = 0;
58+
59+
for (DiagnosticProbe probe : probes) {
60+
61+
out().println("-- " + probe.name() + " --");
62+
63+
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
64+
PrintStream ps = new PrintStream(
65+
buffer, true, StandardCharsets.UTF_8.name());
66+
PrintStream oldOut = System.out;
67+
PrintStream oldErr = System.err;
68+
69+
System.setOut(ps);
70+
System.setErr(ps);
71+
72+
ProbeResult result;
73+
try {
74+
result = probe.test(conf);
75+
} catch (Throwable t) {
76+
t.printStackTrace(System.err);
77+
err().println("ERROR: Probe execution failed: " + t.getMessage());
78+
result = ProbeResult.FAIL;
79+
} finally {
80+
System.setOut(oldOut);
81+
System.setErr(oldErr);
82+
ps.close();
83+
}
84+
85+
String output = new String(buffer.toByteArray(), StandardCharsets.UTF_8);
86+
out().print(output);
87+
88+
switch (result) {
89+
case FAIL:
90+
fail++;
91+
out().println("[FAIL] " + probe.name());
92+
break;
93+
case WARN:
94+
warn++;
95+
out().println("[WARN] " + probe.name());
96+
break;
97+
case PASS:
98+
pass++;
99+
out().println("[PASS] " + probe.name());
100+
break;
101+
default:
102+
throw new IllegalStateException("Unknown result: " + result);
103+
}
104+
out().println();
105+
}
106+
out().println("== Diagnostic Summary ==");
107+
out().println("PASS : " + pass);
108+
out().println("WARN : " + warn);
109+
out().println("FAIL : " + fail);
110+
111+
return fail > 0 ? 1 : 0;
112+
}
113+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.debug.kerberos;
19+
20+
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
21+
22+
/**
23+
* Interface for a diagnostic probe executed by ozone debug kerberos subcommands.
24+
*/
25+
public interface DiagnosticProbe {
26+
27+
/**
28+
* Name of the probe (used in output headers).
29+
*/
30+
String name();
31+
32+
/**
33+
* Execute the diagnostic check and return probe result as enum.
34+
*/
35+
ProbeResult test(OzoneConfiguration conf) throws Exception;
36+
}

0 commit comments

Comments
 (0)