Skip to content

Commit

Permalink
No tests but everything else
Browse files Browse the repository at this point in the history
Signed-off-by: Stephen Crawford <[email protected]>
  • Loading branch information
stephen-crawford committed Sep 14, 2023
1 parent c3c7743 commit e2370ca
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,16 @@

import java.nio.file.Path;
import java.security.AccessController;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.JwtParserBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.WeakKeyException;
import org.apache.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand All @@ -44,6 +36,7 @@
import org.opensearch.core.rest.RestStatus;
import org.opensearch.security.auth.HTTPAuthenticator;
import org.opensearch.security.user.AuthCredentials;
import org.opensearch.security.util.KeyUtils;

public class HTTPJwtAuthenticator implements HTTPAuthenticator {

Expand All @@ -64,45 +57,7 @@ public class HTTPJwtAuthenticator implements HTTPAuthenticator {
public HTTPJwtAuthenticator(final Settings settings, final Path configPath) {
super();

final JwtParserBuilder _jwtParserBuilder = Jwts.parserBuilder();

try {
String signingKey = settings.get("signing_key");

if (signingKey == null || signingKey.length() == 0) {
log.error("signingKey must not be null or empty. JWT authentication will not work");
} else {

signingKey = signingKey.replace("-----BEGIN PUBLIC KEY-----\n", "");
signingKey = signingKey.replace("-----END PUBLIC KEY-----", "");

byte[] decoded = Decoders.BASE64.decode(signingKey);
Key key = null;

try {
key = getPublicKey(decoded, "RSA");
} catch (Exception e) {
log.debug("No public RSA key, try other algos ({})", e.toString());
}

try {
key = getPublicKey(decoded, "EC");
} catch (Exception e) {
log.debug("No public ECDSA key, try other algos ({})", e.toString());
}

if (key != null) {
_jwtParserBuilder.setSigningKey(key);
} else {
_jwtParserBuilder.setSigningKey(decoded);
}

}
} catch (Throwable e) {
log.error("Error creating JWT authenticator. JWT authentication will not work", e);
throw new RuntimeException(e);
}

String signingKey = settings.get("signing_key");
jwtUrlParameter = settings.get("jwt_url_parameter");
jwtHeaderName = settings.get("jwt_header", HttpHeaders.AUTHORIZATION);
isDefaultAuthHeader = HttpHeaders.AUTHORIZATION.equalsIgnoreCase(jwtHeaderName);
Expand All @@ -111,28 +66,20 @@ public HTTPJwtAuthenticator(final Settings settings, final Path configPath) {
requireAudience = settings.get("required_audience");
requireIssuer = settings.get("required_issuer");

if (requireAudience != null) {
_jwtParserBuilder.requireAudience(requireAudience);
}

if (requireIssuer != null) {
_jwtParserBuilder.requireIssuer(requireIssuer);
}

final SecurityManager sm = System.getSecurityManager();

if (sm != null) {
sm.checkPermission(new SpecialPermission());
}
JwtParserBuilder jwtParserBuilder = KeyUtils.createJwtParserBuilderFromSigningKey(signingKey, log);
if (jwtParserBuilder == null) {
jwtParser = null;
} else {
if (requireAudience != null) {
jwtParserBuilder = jwtParserBuilder.require("aud", requireAudience);
}

JwtParser parser = AccessController.doPrivileged(new PrivilegedAction<JwtParser>() {
@Override
public JwtParser run() {
return _jwtParserBuilder.build();
if (requireIssuer != null) {
jwtParserBuilder = jwtParserBuilder.require("iss", requireIssuer);
}
});

jwtParser = parser;
jwtParser = jwtParserBuilder.build();
}
}

@Override
Expand Down Expand Up @@ -296,11 +243,4 @@ protected String[] extractRoles(final Claims claims, final RestRequest request)
return roles;
}

private static PublicKey getPublicKey(final byte[] keyBytes, final String algo) throws NoSuchAlgorithmException,
InvalidKeySpecException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance(algo);
return kf.generatePublic(spec);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
import org.opensearch.indices.IndicesService;
import org.opensearch.indices.SystemIndexDescriptor;
import org.opensearch.plugins.ClusterPlugin;
import org.opensearch.plugins.ExtensionAwarePlugin;
import org.opensearch.plugins.MapperPlugin;
import org.opensearch.repositories.RepositoriesService;
import org.opensearch.rest.RestController;
Expand All @@ -117,6 +118,7 @@
import org.opensearch.search.query.QuerySearchResult;
import org.opensearch.security.action.configupdate.ConfigUpdateAction;
import org.opensearch.security.action.configupdate.TransportConfigUpdateAction;
import org.opensearch.security.action.onbehalf.CreateOnBehalfOfTokenAction;
import org.opensearch.security.action.whoami.TransportWhoAmIAction;
import org.opensearch.security.action.whoami.WhoAmIAction;
import org.opensearch.security.auditlog.AuditLog;
Expand Down Expand Up @@ -190,7 +192,7 @@
import org.opensearch.transport.TransportService;
import org.opensearch.watcher.ResourceWatcherService;

public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin implements ClusterPlugin, MapperPlugin {
public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin implements ClusterPlugin, MapperPlugin, ExtensionAwarePlugin {

private static final String KEYWORD = ".keyword";
private static final Logger actionTrace = LogManager.getLogger("opendistro_security_action_trace");
Expand All @@ -210,6 +212,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin
private volatile ClusterService cs;
private volatile AtomicReference<DiscoveryNode> localNode = new AtomicReference<>();
private volatile AuditLog auditLog;
private volatile DynamicConfigFactory dcf;
private volatile BackendRegistry backendRegistry;
private volatile SslExceptionHandler sslExceptionHandler;
private volatile Client localClient;
Expand Down Expand Up @@ -537,6 +540,9 @@ public List<RestHandler> getRestHandlers(
principalExtractor
)
);
CreateOnBehalfOfTokenAction cobot = new CreateOnBehalfOfTokenAction(settings, threadPool, Objects.requireNonNull(cs));
dcf.registerDCFListener(cobot);
handlers.add(cobot);
handlers.addAll(
SecurityRestApiActions.getHandler(
settings,
Expand Down Expand Up @@ -948,7 +954,7 @@ public Collection<Object> createComponents(
// Register opensearch dynamic settings
transportPassiveAuthSetting.registerClusterSettingsChangeListener(clusterService.getClusterSettings());

final ClusterInfoHolder cih = new ClusterInfoHolder();
final ClusterInfoHolder cih = new ClusterInfoHolder(this.cs.getClusterName().value());
this.cs.addListener(cih);
this.salt = Salt.from(settings);

Expand Down Expand Up @@ -1036,7 +1042,7 @@ public Collection<Object> createComponents(
compatConfig
);

final DynamicConfigFactory dcf = new DynamicConfigFactory(cr, settings, configPath, localClient, threadPool, cih);
dcf = new DynamicConfigFactory(cr, settings, configPath, localClient, threadPool, cih);
dcf.registerDCFListener(backendRegistry);
dcf.registerDCFListener(compatConfig);
dcf.registerDCFListener(irr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import org.opensearch.security.auth.blocking.ClientBlockRegistry;
import org.opensearch.security.auth.internal.NoOpAuthenticationBackend;
import org.opensearch.security.configuration.AdminDNs;
import org.opensearch.security.http.OnBehalfOfAuthenticator;
import org.opensearch.security.http.XFFResolver;
import org.opensearch.security.securityconf.DynamicConfigModel;
import org.opensearch.security.ssl.util.Utils;
Expand Down Expand Up @@ -605,7 +606,12 @@ private User impersonate(final RestRequest request, final User originalUser) thr
final boolean isDebugEnabled = log.isDebugEnabled();
// loop over all http/rest auth domains
for (final AuthDomain authDomain : restAuthDomains) {

final AuthenticationBackend authenticationBackend = authDomain.getBackend();
// Skip over the OnBehalfOfAuthenticator since it is not compatible for user impersonation
if (authDomain.getHttpAuthenticator() instanceof OnBehalfOfAuthenticator) {
continue;
}
final User impersonatedUser = checkExistsAndAuthz(
restImpersonationCache,
new User(impersonatedUserHeader),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@ public String getType() {

@Override
public User authenticate(final AuthCredentials credentials) {
return new User(credentials.getUsername(), credentials.getBackendRoles(), credentials);
User user = new User(credentials.getUsername(), credentials.getBackendRoles(), credentials);
user.addSecurityRoles(credentials.getSecurityRoles());
return user;
}


@Override
public boolean exists(User user) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public class ClusterInfoHolder implements ClusterStateListener {
private volatile DiscoveryNodes nodes = null;
private volatile Boolean isLocalNodeElectedClusterManager = null;
private volatile boolean initialized;
private final String clusterName;

public ClusterInfoHolder(String clusterName) {
this.clusterName = clusterName;
}

@Override
public void clusterChanged(ClusterChangedEvent event) {
Expand Down Expand Up @@ -80,6 +85,10 @@ public void clusterChanged(ClusterChangedEvent event) {
isLocalNodeElectedClusterManager = event.localNodeClusterManager() ? Boolean.TRUE : Boolean.FALSE;
}

public String getClusterName() {
return this.clusterName;
}

public Boolean getHas6xNodes() {
return has6xNodes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package org.opensearch.security.securityconf;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -37,6 +39,8 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
Expand All @@ -57,6 +61,7 @@
import org.opensearch.core.common.transport.TransportAddress;
import org.opensearch.common.util.set.Sets;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.security.DefaultObjectMapper;
import org.opensearch.security.resolver.IndexResolverReplacer.Resolved;
import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration;
import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7;
Expand Down Expand Up @@ -563,6 +568,53 @@ private boolean containsDlsFlsConfig() {
}
}

public static class OnBehalfOfSettings {
@JsonProperty("enabled")
private Boolean oboEnabled = Boolean.TRUE;
@JsonProperty("signing_key")
private String signingKey;
@JsonProperty("encryption_key")
private String encryptionKey;

@JsonIgnore
public String configAsJson() {
try {
return DefaultObjectMapper.writeValueAsString(this, false);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}

public Boolean getOboEnabled() {
return oboEnabled;
}

public void setOboEnabled(Boolean oboEnabled) {
this.oboEnabled = oboEnabled;
}

public String getSigningKey() {
return signingKey;
}

public void setSigningKey(String signingKey) {
this.signingKey = signingKey;
}

public String getEncryptionKey() {
return encryptionKey;
}

public void setEncryptionKey(String encryptionKey) {
this.encryptionKey = encryptionKey;
}

@Override
public String toString() {
return "OnBehalfOfSettings [ enabled=" + oboEnabled + ", signing_key=" + signingKey + ", encryption_key=" + encryptionKey + "]";
}
}

public static class SecurityRole {
private final String name;
private final Set<IndexPattern> ipatterns;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public final static SecurityDynamicConfiguration<?> addStatics(SecurityDynamicCo
private final Settings opensearchSettings;
private final Path configPath;
private final InternalAuthenticationBackend iab = new InternalAuthenticationBackend();
private final ClusterInfoHolder cih;

SecurityDynamicConfiguration<?> config;

Expand All @@ -142,6 +143,7 @@ public DynamicConfigFactory(
this.cr = cr;
this.opensearchSettings = opensearchSettings;
this.configPath = configPath;
this.cih = cih;

if (opensearchSettings.getAsBoolean(ConfigConstants.SECURITY_UNSUPPORTED_LOAD_STATIC_RESOURCES, true)) {
try {
Expand Down Expand Up @@ -278,7 +280,7 @@ public void onChange(Map<CType, SecurityDynamicConfiguration<?>> typeToConfig) {
);

// rebuild v7 Models
dcm = new DynamicConfigModelV7(getConfigV7(config), opensearchSettings, configPath, iab);
dcm = new DynamicConfigModelV7(getConfigV7(config), opensearchSettings, configPath, iab, cih);
ium = new InternalUsersModelV7(
(SecurityDynamicConfiguration<InternalUserV7>) internalusers,
(SecurityDynamicConfiguration<RoleV7>) roles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.opensearch.common.settings.Settings;
import org.opensearch.security.auth.AuthDomain;
import org.opensearch.security.auth.AuthFailureListener;
import org.opensearch.security.auth.AuthorizationBackend;
Expand Down Expand Up @@ -104,6 +105,8 @@ public abstract class DynamicConfigModel {

public abstract Multimap<String, ClientBlockRegistry<String>> getAuthBackendClientBlockRegistries();

public abstract Settings getDynamicOnBehalfOfSettings();

protected final Map<String, String> authImplMap = new HashMap<>();

public DynamicConfigModel() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ public Multimap<String, ClientBlockRegistry<String>> getAuthBackendClientBlockRe
return Multimaps.unmodifiableMultimap(authBackendClientBlockRegistries);
}

@Override
public Settings getDynamicOnBehalfOfSettings() {
return Settings.EMPTY;
}

private void buildAAA() {

final SortedSet<AuthDomain> restAuthDomains0 = new TreeSet<>();
Expand Down
Loading

0 comments on commit e2370ca

Please sign in to comment.