kerberos認證的教程網(wǎng)上有很多,但是es的真的找遍全網(wǎng)都很少有詳細的教程!我苦讀官網(wǎng),到處搜羅零碎信息,才終于完成es的kerberos認證。
一、elasticsearch升級白金版
在我跟著官網(wǎng)步驟勤勤懇懇操作卻還是不行的時候,才突然發(fā)現(xiàn)基礎(chǔ)版并不支持kerberos認證。所以我們需要升級白金版,而白金版是付費使用的。下面是白金版破解流程(僅供學習)如果公司要用的話當然是乖乖掏錢啊
1. 下載對應(yīng)版本源碼,修改相關(guān)類
源碼地址:https://github.com/elastic/elasticsearch
我使用的是es的7.5.2版本,所以下載7.5.2版本的源碼
修改LicenseVerifier.java,路徑為:
x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseVerifier.java
public class LicenseVerifier {
/**
* verifies the license content with the signature using the packaged
* public key
* @param license to verify
* @return true if valid, false otherwise
*/
public static boolean verifyLicense(final License license, byte[] publicKeyData) {
return true;
}
public static boolean verifyLicense(final License license) {
return true;
}
}
修改XPackBuild.java,路徑為:
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackBuild.java
public class XPackBuild {
public static final XPackBuild CURRENT;
static {
CURRENT = new XPackBuild("Unknown", "Unknown");
}
/**
* Returns path to xpack codebase path
*/
@SuppressForbidden(reason = "looks up path of xpack.jar directly")
static Path getElasticsearchCodebase() {
URL url = XPackBuild.class.getProtectionDomain().getCodeSource().getLocation();
try {
return PathUtils.get(url.toURI());
} catch (URISyntaxException bogus) {
throw new RuntimeException(bogus);
}
}
private String shortHash;
private String date;
XPackBuild(String shortHash, String date) {
this.shortHash = shortHash;
this.date = date;
}
public String shortHash() {
return shortHash;
}
public String date() {
return date;
}
}
2. 編譯后替換jar包中的class文件
編譯:
javac -cp "/home/es/elasticsearch/lib/*:/home/es/elasticsearch/modules/x-pack-core/*" LicenseVerifier.java
javac -cp "/home/es/elasticsearch/lib/*:/home/es/elasticsearch/modules/x-pack-core/*" XPackBuild.java
替換:
cp /home/es/elasticsearch/modules/x-pack-core/x-pack-core-7.5.2.jar x-pack-core-7.5.2.jar
unzip x-pack-core-7.5.2.jar -d ./x-pack-core-7.5.2
cp LicenseVerifier.class ./x-pack-core-7.5.2/org/elasticsearch/license/LicenseVerifier.class
cp XPackBuild.class ./x-pack-core-7.5.2/org/elasticsearch/xpack/core/XPackBuild.class
jar -cvf x-pack-core-7.5.2.crack.jar -C x-pack-core-7.5.2 .
cp x-pack-core-7.5.2.crack.jar /home/es/elasticsearch/modules/x-pack-core/x-pack-core-8.3.3.jar
3.更新license
官方申請地址:https://register.elastic.co/marvel_register
申請到的是json格式的許可證,將 ”type":“basic” 修改為 “platinum”,即白金版,還可以將"expiry_date_in_millis" 這個時間戳改大,延長有效期
建一個json文件license.json,把修改后的json放進去,然后上傳該license
curl -XPUT node1:9200/_xpack/license -H “Content-Type: application/json” -d @license.json
二、搭建kerberos服務(wù)
kerberos相關(guān)教程網(wǎng)上到處都是,所以我下面就簡單寫一下具體步驟
1. 安裝kerberos服務(wù)端
yum install -y krb5-server
安裝完會在/var/kerberos/krb5kdc下生成兩個文件:kadm5.acl kdc.conf
2. 安裝kerberos客戶端
yum install -y krb5-workstation krb5-libs
安裝完成會生成配置文件/etc/krb5.conf
3. 服務(wù)端配置文件kdc.conf
[kdcdefaults]
kdc_ports = 88
kdc_tcp_ports = 88
[realms]
EXAMPLE.COM = {
master_key_type = aes256-cts
acl_file = /var/kerberos/krb5kdc/kadm5.acl
dict_file = /usr/share/dict/words
admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal camellia256-cts:normal camellia128-cts:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal
}
EXAMPLE.COM :設(shè)定的realm,名字自定義,但要保證所有相關(guān)的地方一致
4.服務(wù)端配置文件kadm5.acl
*/admin@EXAMPLE.COM *
EXAMPLE.COM和前面的realm保持一致
這個配置表示所有匹配*/admin的principal都擁有所有權(quán)限
5. 客戶端配置文件/etc/krb5.conf
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
default_realm = EXAMPLE.COM
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
EXAMPLE.COM = {
kdc = node1
admin_server = node1
}
[domain_realm]
# .example.com = EXAMPLE.COM
# example.com = EXAMPLE.COM
6. 初始化Kadmin數(shù)據(jù)庫
kdb5_util create -s -r EXAMPLE.COM
需要初始化密碼,我用的123456
7. 啟動Kerberos服務(wù)并設(shè)置開機自啟動
systemctl start krb5kdc
systemctl start kadmin
systemctl enable krb5kdc
systemctl enable kadmin
8. 添加principal并生成keytab供es使用
elasticsearch官網(wǎng)建議principal格式為:HTTP/主機名@realm
執(zhí)行kadmin.local即可操作kadmin數(shù)據(jù)庫
kadmin.local
添加principal,需要初始化密碼,我設(shè)置的123456
addprinc HTTP/node1
生成該principal的keytab文件,我將該文件放到/home/keytabs目錄下
ktadd -norandkey -kt /home/keytabs/es.keytab HTTP/node1@EXAMPLE.COM
三、es添加kerberos認證
官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/7.5/kerberos-realm.html
1. 配置JVM系統(tǒng)屬性定位krb5.conf
Kerberos配置文件(krb5.conf)提供了默認realm、密鑰分發(fā)中心(KDC)和Kerberos身份驗證所需的其他配置細節(jié)等信息。Elasticsearch使用Java GSS框架支持Kerberos身份驗證,而GSS會嘗試通過定位和加載krb5.conf來查找這些值。我們需要通過配置JVM系統(tǒng)屬性java.security.krb5.conf 來使GSS定位到文件位置
編輯es的jvm.options配置文件,添加如下配置:
-Djava.security.krb5.conf=/etc/krb5.conf
2. 將keytab文件放到es的配置目錄下
將上面生成的keytab文件放到es的config目錄下
cp /home/keytabs/es.keytab /home/es/elasticsearch/config/es.keytab
這里可能會涉及到權(quán)限問題,最好讓/home/es/elasticsearch/config/es.keytab和其他es文件處于同一個用戶和用戶組,權(quán)限也和其他文件保持一致
3. 在elasticsearch.yml中添加配置并重啟
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.authc.realms.kerberos.kerb1:
order: 1
keytab.path: es.keytab
remove_realm_name: false
官網(wǎng)沒有提到前兩行配置,但必須開啟這兩個才能使用kerberos認證,否則啟動會報錯并提示需要加這兩項配置
4. 為kerberos用戶映射角色
官網(wǎng)示例如下:
POST /_security/role_mapping/kerbrolemapping
{
"roles" : [ "monitoring_user" ],
"enabled": true,
"rules" : {
"field" : { "username" : "user@REALM" }
}
}
“roles” 為我們映射的角色
“username"為我們上面創(chuàng)建的principal,也就是kerberos用戶
但官網(wǎng)示例的“monitoring_user"這個角色很多權(quán)限都沒有,我們學習或者測試的話可以使用superuser,擁有所有權(quán)限。實際使用可根據(jù)需求而定。
示例如下:
curl -u elastic -H "Content-Type: application/json" -XPOST node1:9200/_security/role_mapping/kerbrolemapping -d
{
"roles" : [ "superuser" ],
"enabled": true,
"rules" : {
"field" : { "username" : "HTTP/node1@EXAMPLE.COM" }
}
}
如果不知道elastic用戶的密碼可以通過bin/elasticsearch-setup-passwords interactive 重置
5. 驗證是否成功
首先在客戶端進行主體認證,有如下兩種方式,選其一即可
密碼認證:kinit HTTP/node1@EXAMPLE.COM
,并輸入密碼
keytab認證:kinit HTTP/node1@EXAMPLE.COM -kt /home/keytabs/es.keytab
然后使用negotiate參數(shù)調(diào)用curl,以便通過HTTP執(zhí)行Kerberos身份驗證:
curl --negotiate -u : -XGET node1:9200/
能成功返回es信息則認證成功
四、java操作kerberos認證的es
KerberosDemo:文章來源:http://www.zghlxwxcb.cn/news/detail-798093.html
public class KerberosDemo {
public static void main(String[] args) {
RestHighLevelClient restHighLevelClient = null;
try {
System.setProperty("http.auth.preference","Kerberos");
System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");
System.setProperty("sun.security.krb5.debug", "true");
System.setProperty("sun.security.spnego.debug", "true");
//密碼認證方式
/*SpnegoHttpClientConfigCallbackHandler callbackHandler = new SpnegoHttpClientConfigCallbackHandler("HTTP/node1@EXAMPLE.COM",
new SecureString("123456"), true);*/
//keytab認證方式
SpnegoHttpClientConfigCallbackHandler callbackHandler = new SpnegoHttpClientConfigCallbackHandler("HTTP/node1@EXAMPLE.COM",
"/home/keytabs/es.keytab", true);
//業(yè)務(wù)邏輯開始
List<HttpHost> hosts = new ArrayList<>();
HttpHost hostNew = new HttpHost("node1", 9200, "http");
hosts.add(hostNew);
HttpHost[] httpHosts = hosts.toArray(new HttpHost[0]);
RestClientBuilder restClientBuilder = RestClient.builder(httpHosts);
restClientBuilder.setHttpClientConfigCallback(callbackHandler);
restHighLevelClient = new RestHighLevelClient(restClientBuilder);
//測試獲取所有的索引
GetIndexRequest getIndexRequest = new GetIndexRequest("*");
GetIndexResponse getIndexResponse = restHighLevelClient.indices().get(getIndexRequest, RequestOptions.DEFAULT);
String[] indexNames = getIndexResponse.getIndices();
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}finally {
if(restHighLevelClient !=null){
try {
restHighLevelClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
demo中用到的類SpnegoHttpClientConfigCallbackHandler :文章來源地址http://www.zghlxwxcb.cn/news/detail-798093.html
public class SpnegoHttpClientConfigCallbackHandler implements HttpClientConfigCallback {
private static final String SUN_KRB5_LOGIN_MODULE = "com.sun.security.auth.module.Krb5LoginModule";
private static final String CRED_CONF_NAME = "ESClientLoginConf";
private static final Oid SPNEGO_OID = getSpnegoOid();
private static Oid getSpnegoOid() {
Oid oid = null;
try {
oid = new Oid("1.3.6.1.5.5.2");
} catch (GSSException gsse) {
throw ExceptionsHelper.convertToRuntime(gsse);
}
return oid;
}
private final String userPrincipalName;
private final SecureString password;
private final String keytabPath;
private final boolean enableDebugLogs;
private LoginContext loginContext;
/**
* principalName and password.
*
* @param userPrincipalName user principal name
* @param password password for user
* @param enableDebugLogs if {@code true} enables kerberos debug logs
*/
public SpnegoHttpClientConfigCallbackHandler(final String userPrincipalName, final SecureString password,
final boolean enableDebugLogs) {
this.userPrincipalName = userPrincipalName;
this.password = password;
this.keytabPath = null;
this.enableDebugLogs = enableDebugLogs;
}
/**
* principalName and keytab.
*
* @param userPrincipalName User principal name
* @param keytabPath path to keytab file for user
* @param enableDebugLogs if {@code true} enables kerberos debug logs
*/
public SpnegoHttpClientConfigCallbackHandler(final String userPrincipalName, final String keytabPath, final boolean enableDebugLogs) {
this.userPrincipalName = userPrincipalName;
this.keytabPath = keytabPath;
this.password = null;
this.enableDebugLogs = enableDebugLogs;
}
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
setupSpnegoAuthSchemeSupport(httpClientBuilder);
return httpClientBuilder;
}
private void setupSpnegoAuthSchemeSupport(HttpAsyncClientBuilder httpClientBuilder) {
final Lookup<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory()).build();
final GSSManager gssManager = GSSManager.getInstance();
try {
final GSSName gssUserPrincipalName = gssManager.createName(userPrincipalName, GSSName.NT_USER_NAME);
login();
final AccessControlContext acc = AccessController.getContext();
final GSSCredential credential = doAsPrivilegedWrapper(loginContext.getSubject(),
(PrivilegedExceptionAction<GSSCredential>) () -> gssManager.createCredential(gssUserPrincipalName,
GSSCredential.DEFAULT_LIFETIME, SPNEGO_OID, GSSCredential.INITIATE_ONLY),
acc);
final KerberosCredentialsProvider credentialsProvider = new KerberosCredentialsProvider();
credentialsProvider.setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.SPNEGO),
new KerberosCredentials(credential));
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
} catch (GSSException e) {
throw new RuntimeException(e);
} catch (PrivilegedActionException e) {
throw new RuntimeException(e.getCause());
}
httpClientBuilder.setDefaultAuthSchemeRegistry(authSchemeRegistry);
}
/**
* If logged in {@link LoginContext} is not available, it attempts login and
* returns {@link LoginContext}
*
* @return {@link LoginContext}
* @throws PrivilegedActionException
*/
public synchronized LoginContext login() throws PrivilegedActionException {
if (this.loginContext == null) {
AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
final Subject subject = new Subject(false, Collections.singleton(new KerberosPrincipal(userPrincipalName)),
Collections.emptySet(), Collections.emptySet());
Configuration conf = null;
final CallbackHandler callback;
if (password != null) {
conf = new PasswordJaasConf(userPrincipalName, enableDebugLogs);
callback = new KrbCallbackHandler(userPrincipalName, password);
} else {
conf = new KeytabJaasConf(userPrincipalName, keytabPath, enableDebugLogs);
callback = null;
}
loginContext = new LoginContext(CRED_CONF_NAME, subject, callback, conf);
loginContext.login();
return null;
});
}
return loginContext;
}
/**
* Privileged Wrapper that invokes action with Subject.doAs to perform work as
* given subject.
*
* @param subject {@link Subject} to be used for this work
* @param action {@link PrivilegedExceptionAction} action for performing inside
* Subject.doAs
* @param acc the {@link AccessControlContext} to be tied to the specified
* subject and action see
* {@link Subject#doAsPrivileged(Subject, PrivilegedExceptionAction, AccessControlContext)
* @return the value returned by the PrivilegedExceptionAction's run method
* @throws PrivilegedActionException
*/
static <T> T doAsPrivilegedWrapper(final Subject subject, final PrivilegedExceptionAction<T> action, final AccessControlContext acc)
throws PrivilegedActionException {
try {
return AccessController.doPrivileged((PrivilegedExceptionAction<T>) () -> Subject.doAsPrivileged(subject, action, acc));
} catch (PrivilegedActionException pae) {
if (pae.getCause() instanceof PrivilegedActionException) {
throw (PrivilegedActionException) pae.getCause();
}
throw pae;
}
}
/**
* This class matches {@link AuthScope} and based on that returns
* {@link Credentials}. Only supports {@link AuthSchemes#SPNEGO} in
* {@link AuthScope#getScheme()}
*/
private static class KerberosCredentialsProvider implements CredentialsProvider {
private AuthScope authScope;
private Credentials credentials;
@Override
public void setCredentials(AuthScope authscope, Credentials credentials) {
if (authscope.getScheme().regionMatches(true, 0, AuthSchemes.SPNEGO, 0, AuthSchemes.SPNEGO.length()) == false) {
throw new IllegalArgumentException("Only " + AuthSchemes.SPNEGO + " auth scheme is supported in AuthScope");
}
this.authScope = authscope;
this.credentials = credentials;
}
@Override
public Credentials getCredentials(AuthScope authscope) {
assert this.authScope != null && authscope != null;
return authscope.match(this.authScope) > -1 ? this.credentials : null;
}
@Override
public void clear() {
this.authScope = null;
this.credentials = null;
}
}
/**
* Jaas call back handler to provide credentials.
*/
private static class KrbCallbackHandler implements CallbackHandler {
private final String principal;
private final SecureString password;
KrbCallbackHandler(final String principal, final SecureString password) {
this.principal = principal;
this.password = password;
}
public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback) callback;
if (pc.getPrompt().contains(principal)) {
pc.setPassword(password.getChars());
break;
}
}
}
}
}
/**
* Usually we would have a JAAS configuration file for login configuration.
* Instead of an additional file setting as we do not want the options to be
* customizable we are constructing it in memory.
* <p>
* As we are using this instead of jaas.conf, this requires refresh of
* {@link Configuration} and reqires appropriate security permissions to do so.
*/
private static class PasswordJaasConf extends AbstractJaasConf {
PasswordJaasConf(final String userPrincipalName, final boolean enableDebugLogs) {
super(userPrincipalName, enableDebugLogs);
}
public void addOptions(final Map<String, String> options) {
options.put("useTicketCache", Boolean.FALSE.toString());
options.put("useKeyTab", Boolean.FALSE.toString());
}
}
/**
* Usually we would have a JAAS configuration file for login configuration. As
* we have static configuration except debug flag, we are constructing in
* memory. This avoids additional configuration required from the user.
* <p>
* As we are using this instead of jaas.conf, this requires refresh of
* {@link Configuration} and requires appropriate security permissions to do so.
*/
private static class KeytabJaasConf extends AbstractJaasConf {
private final String keytabFilePath;
KeytabJaasConf(final String userPrincipalName, final String keytabFilePath, final boolean enableDebugLogs) {
super(userPrincipalName, enableDebugLogs);
this.keytabFilePath = keytabFilePath;
}
public void addOptions(final Map<String, String> options) {
options.put("useKeyTab", Boolean.TRUE.toString());
options.put("keyTab", keytabFilePath);
options.put("doNotPrompt", Boolean.TRUE.toString());
}
}
private abstract static class AbstractJaasConf extends Configuration {
private final String userPrincipalName;
private final boolean enableDebugLogs;
AbstractJaasConf(final String userPrincipalName, final boolean enableDebugLogs) {
this.userPrincipalName = userPrincipalName;
this.enableDebugLogs = enableDebugLogs;
}
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(final String name) {
final Map<String, String> options = new HashMap<>();
options.put("principal", userPrincipalName);
options.put("isInitiator", Boolean.TRUE.toString());
options.put("storeKey", Boolean.TRUE.toString());
options.put("debug", Boolean.toString(enableDebugLogs));
addOptions(options);
return new AppConfigurationEntry[] { new AppConfigurationEntry(SUN_KRB5_LOGIN_MODULE,
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, Collections.unmodifiableMap(options)) };
}
abstract void addOptions(Map<String, String> options);
}
}
到了這里,關(guān)于elasticsearch添加kerberos認證完整操作流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!