Skip to content
This repository has been archived by the owner on May 10, 2022. It is now read-only.

feat(security): implement AuthProtocol and Credential classes #139

Merged
merged 41 commits into from
Nov 6, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
425c9e4
feat(security): add configurations of keyTab and principal
levy5307 Sep 25, 2020
485375b
fix
levy5307 Sep 25, 2020
a6dda3e
Merge branch 'master' into jaas-config
levy5307 Oct 16, 2020
f7b9c74
fix
levy5307 Oct 28, 2020
d5a59df
fix
levy5307 Oct 28, 2020
b0883f1
fix
levy5307 Nov 2, 2020
3cd6a79
refactor
levy5307 Nov 3, 2020
cc89c4b
fix
levy5307 Nov 3, 2020
44117fd
fix
levy5307 Nov 3, 2020
efad163
fix
levy5307 Nov 3, 2020
ed74c30
add license
levy5307 Nov 3, 2020
5ed212d
fix
levy5307 Nov 3, 2020
ceedd52
fix
levy5307 Nov 3, 2020
651fe2c
fix
levy5307 Nov 3, 2020
4a9f8f9
fix
levy5307 Nov 4, 2020
71e83e6
Revert "fix"
levy5307 Nov 4, 2020
f1fa270
fix
levy5307 Nov 4, 2020
c0ffe87
fix
levy5307 Nov 4, 2020
dabaf81
fix
levy5307 Nov 4, 2020
1cf8f76
fix
levy5307 Nov 4, 2020
e671c2f
fix
levy5307 Nov 4, 2020
24118d7
fix
levy5307 Nov 4, 2020
db021b4
add annotiate
levy5307 Nov 4, 2020
c24b3bf
fix
levy5307 Nov 4, 2020
6d200f8
Revert "fix"
levy5307 Nov 4, 2020
448441c
Revert "add annotiate"
levy5307 Nov 4, 2020
e7a135a
fix by review
levy5307 Nov 5, 2020
91b3379
fix
levy5307 Nov 5, 2020
eeed25e
fix
levy5307 Nov 5, 2020
3d1feb0
fix
levy5307 Nov 5, 2020
cc32925
fix
levy5307 Nov 5, 2020
bc5200f
fix
levy5307 Nov 5, 2020
7d545d7
fix
levy5307 Nov 5, 2020
9a780ec
fix
levy5307 Nov 5, 2020
9a36694
fix
levy5307 Nov 5, 2020
2d80a9a
fix
levy5307 Nov 5, 2020
3fa4d29
fix
levy5307 Nov 5, 2020
c8bfc83
fix
levy5307 Nov 5, 2020
c766389
fix
levy5307 Nov 5, 2020
f099f4a
fix
levy5307 Nov 5, 2020
29186d7
fix
levy5307 Nov 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions configuration/pegasus.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ push_counter_interval_secs = 10
meta_query_timeout = 5000
service_name = ""
service_fqdn = ""
keytab = ""
principal = ""
68 changes: 66 additions & 2 deletions src/main/java/com/xiaomi/infra/pegasus/client/ClientOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public class ClientOptions {
public static final String PEGASUS_ENABLE_AUTH_KEY = "enable_auth";
public static final String PEGASUS_SERVICE_NAME_KEY = "service_name";
public static final String PEGASUS_SERVICE_FQDN_KEY = "service_fqdn";
public static final String PEGASUS_KEYTAB_KEY = "keytab";
public static final String PEGASUS_PRINCIPAL_KEY = "principal";

public static final String DEFAULT_META_SERVERS =
"127.0.0.1:34601,127.0.0.1:34602,127.0.0.1:34603";
Expand All @@ -61,6 +63,8 @@ public class ClientOptions {
public static final boolean DEFAULT_ENABLE_AUTH = false;
neverchanje marked this conversation as resolved.
Show resolved Hide resolved
public static final String DEFAULT_SERVICE_NAME = "";
public static final String DEFAULT_SERVICE_FQDN = "";
public static final String DEFAULT_KEYTAB = "";
public static final String DEFAULT_PRINCIPAL = "";

private final String metaServers;
private final Duration operationTimeout;
Expand All @@ -73,6 +77,8 @@ public class ClientOptions {
private final boolean enableAuth;
private final String serviceName;
private final String serviceFQDN;
private final String keyTab;
private final String principal;

protected ClientOptions(Builder builder) {
this.metaServers = builder.metaServers;
Expand All @@ -86,6 +92,8 @@ protected ClientOptions(Builder builder) {
this.enableAuth = builder.enableAuth;
this.serviceName = builder.serviceName;
this.serviceFQDN = builder.serviceFQDN;
this.keyTab = builder.keyTab;
this.principal = builder.principal;
}

protected ClientOptions(ClientOptions original) {
Expand All @@ -100,6 +108,8 @@ protected ClientOptions(ClientOptions original) {
this.enableAuth = original.isEnableAuth();
this.serviceName = original.getServiceName();
this.serviceFQDN = original.getServiceFQDN();
this.keyTab = original.getKeyTab();
this.principal = original.getPrincipal();
}

/**
Expand Down Expand Up @@ -162,6 +172,8 @@ public static ClientOptions create(String configPath) throws PException {
boolean enableAuth = config.getBoolean(PEGASUS_ENABLE_AUTH_KEY, DEFAULT_ENABLE_AUTH);
String serviceName = config.getString(PEGASUS_SERVICE_NAME_KEY, DEFAULT_SERVICE_NAME);
String serviceFQDN = config.getString(PEGASUS_SERVICE_FQDN_KEY, DEFAULT_SERVICE_FQDN);
String keyTab = config.getString(PEGASUS_KEYTAB_KEY, DEFAULT_KEYTAB);
String principal = config.getString(PEGASUS_PRINCIPAL_KEY, DEFAULT_PRINCIPAL);

return ClientOptions.builder()
.metaServers(metaList)
Expand All @@ -174,6 +186,8 @@ public static ClientOptions create(String configPath) throws PException {
.enableAuth(enableAuth)
.serviceName(serviceName)
.serviceFQDN(serviceFQDN)
.keyTab(keyTab)
.principal(principal)
.build();
}

Expand All @@ -194,7 +208,9 @@ public boolean equals(Object options) {
&& this.metaQueryTimeout.toMillis() == clientOptions.metaQueryTimeout.toMillis()
&& this.enableAuth == clientOptions.enableAuth
&& this.serviceName == clientOptions.serviceName
&& this.serviceFQDN == clientOptions.serviceFQDN;
&& this.serviceFQDN == clientOptions.serviceFQDN
&& this.keyTab == clientOptions.keyTab
&& this.principal == clientOptions.principal;
}
return false;
}
Expand Down Expand Up @@ -226,6 +242,10 @@ public String toString() {
+ serviceName
+ ", serviceFQDN="
+ serviceFQDN
+ ", keyTab="
+ keyTab
+ ", principal="
+ principal
+ '}';
}

Expand All @@ -242,6 +262,8 @@ public static class Builder {
private boolean enableAuth = DEFAULT_ENABLE_AUTH;
private String serviceName = DEFAULT_SERVICE_NAME;
private String serviceFQDN = DEFAULT_SERVICE_FQDN;
private String keyTab = DEFAULT_KEYTAB;
private String principal = DEFAULT_PRINCIPAL;

protected Builder() {}

Expand Down Expand Up @@ -379,6 +401,28 @@ public Builder serviceFQDN(String serviceFQDN) {
return this;
}

/**
* kerberos keytab. Defaults to {@literal ""}, see {@link #DEFAULT_KEYTAB}.
*
* @param keyTab
* @return {@code this}
*/
public Builder keyTab(String keyTab) {
this.keyTab = keyTab;
return this;
}

/**
* kerberos principal. Defaults to {@literal ""}, see {@link #DEFAULT_PRINCIPAL}.
*
* @param principal
* @return {@code this}
*/
public Builder principal(String principal) {
this.principal = principal;
return this;
}
levy5307 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Create a new instance of {@link ClientOptions}.
*
Expand Down Expand Up @@ -409,7 +453,9 @@ public ClientOptions.Builder mutate() {
.metaQueryTimeout(getMetaQueryTimeout())
.enableAuth(isEnableAuth())
.serviceName(getServiceName())
.serviceFQDN(getServiceFQDN());
.serviceFQDN(getServiceFQDN())
.keyTab(getKeyTab())
.principal(getPrincipal());
return builder;
}

Expand Down Expand Up @@ -516,4 +562,22 @@ public String getServiceName() {
public String getServiceFQDN() {
return serviceFQDN;
}

/**
* kerberos principal. Defaults to {@literal ""}.
*
* @return kerberos principal.
*/
public String getPrincipal() {
return principal;
}

/**
* kerberos keytab. Defaults to {@literal ""}.
*
* @return kerberos keytab.
*/
public String getKeyTab() {
return keyTab;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class ReplicaSessionInterceptorManager {
public ReplicaSessionInterceptorManager(ClientOptions options) {
if (options.isEnableAuth()) {
ReplicaSessionInterceptor securityInterceptor =
new SecurityReplicaSessionInterceptor(options.getServiceName(), options.getServiceFQDN());
new SecurityReplicaSessionInterceptor(options);
interceptors.add(securityInterceptor);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@
package com.xiaomi.infra.pegasus.rpc.interceptor;

import com.sun.security.auth.callback.TextCallbackHandler;
import com.xiaomi.infra.pegasus.client.ClientOptions;
import com.xiaomi.infra.pegasus.rpc.async.Negotiation;
import com.xiaomi.infra.pegasus.rpc.async.ReplicaSession;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.slf4j.Logger;
Expand All @@ -32,22 +37,34 @@ public class SecurityReplicaSessionInterceptor implements ReplicaSessionIntercep

private String serviceName;
private String serviceFqdn;

// Subject is a JAAS internal class, Ref:
// https://docs.oracle.com/javase/7/docs/technotes/guides/security/jaas/JAASRefGuide.html
//
// To authorize access to resources, applications first need to authenticate the source of the
// request. The JAAS framework defines the term "subject" to represent the source of a request. A
// subject may be any entity, such as a person or a service.
private Subject subject;
// The LoginContext class provides the basic methods used to authenticate subjects, and provides a
// way to develop an application independent of the underlying authentication technology
private LoginContext loginContext;

public SecurityReplicaSessionInterceptor(String serviceName, String serviceFqdn)
throws IllegalArgumentException {
this.serviceName = serviceName;
this.serviceFqdn = serviceFqdn;
public SecurityReplicaSessionInterceptor(ClientOptions options) throws IllegalArgumentException {
this.serviceName = options.getServiceName();
this.serviceFqdn = options.getServiceFQDN();

try {
loginContext = new LoginContext("client", new TextCallbackHandler());
// actual authentication: authenticate the Subject
levy5307 marked this conversation as resolved.
Show resolved Hide resolved
// A LoginModule uses a CallbackHandler to communicate with the user to obtain authentication
// information.
subject = new Subject();
loginContext =
new LoginContext(
"pegasus-client",
subject,
new TextCallbackHandler(),
neverchanje marked this conversation as resolved.
Show resolved Hide resolved
getLoginContextConfiguration(options));
loginContext.login();

subject = loginContext.getSubject();
if (subject == null) {
throw new LoginException("subject is null");
}
} catch (LoginException le) {
throw new IllegalArgumentException("login failed", le);
}
Expand All @@ -59,4 +76,32 @@ public void onConnected(ReplicaSession session) {
Negotiation negotiation = new Negotiation(session, subject, serviceName, serviceFqdn);
negotiation.start();
}

private static Configuration getLoginContextConfiguration(ClientOptions clientOptions) {
return new Configuration() {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
Map<String, String> options = new HashMap<>();
// TGT is obtained from the ticket cache.
options.put("useTicketCache", "true");
// get the principal's key from the the keytab
options.put("useKeyTab", "true");
// renew the TGT
options.put("renewTGT", "true");
// keytab or the principal's key to be stored in the Subject's private credentials.
options.put("storeKey", "true");
// the file name of the keytab to get principal's secret key.
options.put("keyTab", clientOptions.getKeyTab());
// the name of the principal that should be used
options.put("principal", clientOptions.getPrincipal());

return new AppConfigurationEntry[] {
new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options)
};
}
};
}
}