mirror of https://github.com/apache/lucene.git
Revert "SOLR-14440 CertAuth plugin (#1463)"
Another commit accidentally snuck in
This reverts commit 7b289d6185
.
This commit is contained in:
parent
7b289d6185
commit
a5c73d39d3
|
@ -10,7 +10,7 @@ Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this r
|
||||||
|
|
||||||
New Features
|
New Features
|
||||||
---------------------
|
---------------------
|
||||||
* SOLR-14440: Introduce new Certificate Authentication Plugin to load Principal from certificate subject. (Mike Drob)
|
(No changes)
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -527,12 +527,11 @@ public class SolrMetricManager {
|
||||||
*/
|
*/
|
||||||
public enum ResolutionStrategy {
|
public enum ResolutionStrategy {
|
||||||
/**
|
/**
|
||||||
* The existing metric will be kept and the new metric will be ignored. If no metric exists, then the new metric
|
* The existing metric will be kept and the new metric will be ignored
|
||||||
* will be registered.
|
|
||||||
*/
|
*/
|
||||||
IGNORE,
|
IGNORE,
|
||||||
/**
|
/**
|
||||||
* The existing metric will be removed and replaced with the new metric.
|
* The existing metric will be removed and replaced with the new metric
|
||||||
*/
|
*/
|
||||||
REPLACE,
|
REPLACE,
|
||||||
/**
|
/**
|
||||||
|
@ -557,11 +556,13 @@ public class SolrMetricManager {
|
||||||
Map<String, Metric> existingMetrics = metricRegistry.getMetrics();
|
Map<String, Metric> existingMetrics = metricRegistry.getMetrics();
|
||||||
for (Map.Entry<String, Metric> entry : metrics.getMetrics().entrySet()) {
|
for (Map.Entry<String, Metric> entry : metrics.getMetrics().entrySet()) {
|
||||||
String fullName = mkName(entry.getKey(), metricPath);
|
String fullName = mkName(entry.getKey(), metricPath);
|
||||||
|
if (existingMetrics.containsKey(fullName)) {
|
||||||
if (strategy == ResolutionStrategy.REPLACE) {
|
if (strategy == ResolutionStrategy.REPLACE) {
|
||||||
metricRegistry.remove(fullName);
|
metricRegistry.remove(fullName);
|
||||||
} else if (strategy == ResolutionStrategy.IGNORE && existingMetrics.containsKey(fullName)) {
|
} else if (strategy == ResolutionStrategy.IGNORE) {
|
||||||
continue;
|
continue;
|
||||||
} // strategy == ERROR will fail when we try to register
|
} // strategy == ERROR will fail when we try to register later
|
||||||
|
}
|
||||||
metricRegistry.register(fullName, entry.getValue());
|
metricRegistry.register(fullName, entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -683,36 +684,28 @@ public class SolrMetricManager {
|
||||||
return registry(registry).histogram(name, histogramSupplier);
|
return registry(registry).histogram(name, histogramSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use {@link #registerMetric(SolrMetricsContext, String, Metric, ResolutionStrategy, String, String...)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void registerMetric(SolrMetricsContext context, String registry, Metric metric, boolean force, String metricName, String... metricPath) {
|
|
||||||
registerMetric(context, registry, metric, force ? ResolutionStrategy.REPLACE : ResolutionStrategy.IGNORE, metricName, metricPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an instance of {@link Metric}.
|
* Register an instance of {@link Metric}.
|
||||||
*
|
*
|
||||||
* @param registry registry name
|
* @param registry registry name
|
||||||
* @param metric metric instance
|
* @param metric metric instance
|
||||||
* @param strategy the conflict resolution strategy to use if the named metric already exists.
|
* @param force if true then an already existing metric with the same name will be replaced.
|
||||||
|
* When false and a metric with the same name already exists an exception
|
||||||
|
* will be thrown.
|
||||||
* @param metricName metric name, either final name or a fully-qualified name
|
* @param metricName metric name, either final name or a fully-qualified name
|
||||||
* using dotted notation
|
* using dotted notation
|
||||||
* @param metricPath (optional) additional top-most metric name path elements
|
* @param metricPath (optional) additional top-most metric name path elements
|
||||||
*/
|
*/
|
||||||
public void registerMetric(SolrMetricsContext context, String registry, Metric metric, ResolutionStrategy strategy, String metricName, String... metricPath) {
|
public void registerMetric(SolrMetricsContext context, String registry, Metric metric, boolean force, String metricName, String... metricPath) {
|
||||||
MetricRegistry metricRegistry = registry(registry);
|
MetricRegistry metricRegistry = registry(registry);
|
||||||
String fullName = mkName(metricName, metricPath);
|
String fullName = mkName(metricName, metricPath);
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
context.registerMetricName(fullName);
|
context.registerMetricName(fullName);
|
||||||
}
|
}
|
||||||
synchronized (metricRegistry) { // prevent race; register() throws if metric is already present
|
synchronized (metricRegistry) { // prevent race; register() throws if metric is already present
|
||||||
if (strategy == ResolutionStrategy.REPLACE) { // must remove any existing one if present
|
if (force) { // must remove any existing one if present
|
||||||
metricRegistry.remove(fullName);
|
metricRegistry.remove(fullName);
|
||||||
} else if (strategy == ResolutionStrategy.IGNORE && metricRegistry.getMetrics().containsKey(fullName)) {
|
}
|
||||||
return;
|
|
||||||
} // strategy == ERROR will fail when we try to register
|
|
||||||
metricRegistry.register(fullName, metric);
|
metricRegistry.register(fullName, metric);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -747,16 +740,8 @@ public class SolrMetricManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use {@link #registerGauge(SolrMetricsContext, String, Gauge, String, ResolutionStrategy, String, String...)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void registerGauge(SolrMetricsContext context, String registry, Gauge<?> gauge, String tag, boolean force, String metricName, String... metricPath) {
|
public void registerGauge(SolrMetricsContext context, String registry, Gauge<?> gauge, String tag, boolean force, String metricName, String... metricPath) {
|
||||||
registerGauge(context, registry, gauge, tag, force ? ResolutionStrategy.REPLACE : ResolutionStrategy.ERROR, metricName, metricPath);
|
registerMetric(context, registry, new GaugeWrapper(gauge, tag), force, metricName, metricPath);
|
||||||
}
|
|
||||||
|
|
||||||
public void registerGauge(SolrMetricsContext context, String registry, Gauge<?> gauge, String tag, ResolutionStrategy strategy, String metricName, String... metricPath) {
|
|
||||||
registerMetric(context, registry, new GaugeWrapper(gauge, tag), strategy, metricName, metricPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int unregisterGauges(String registryName, String tagSegment) {
|
public int unregisterGauges(String registryName, String tagSegment) {
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.solr.security;
|
|
||||||
|
|
||||||
import org.apache.http.HttpHeaders;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An authentication plugin that sets principal based on the certificate subject
|
|
||||||
*/
|
|
||||||
public class CertAuthPlugin extends AuthenticationPlugin {
|
|
||||||
@Override
|
|
||||||
public void init(Map<String, Object> pluginConfig) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean doAuthenticate(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws Exception {
|
|
||||||
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
|
|
||||||
if (certs == null || certs.length == 0) {
|
|
||||||
numMissingCredentials.inc();
|
|
||||||
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Certificate");
|
|
||||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "require certificate");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpServletRequest wrapped = wrapWithPrincipal(request, certs[0].getSubjectX500Principal());
|
|
||||||
numAuthenticated.inc();
|
|
||||||
filterChain.doFilter(wrapped, response);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -226,7 +226,7 @@ public class SolrDispatchFilter extends BaseSolrFilter {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
metricManager.registerGauge(null, registryName, sysprops, metricTag, SolrMetricManager.ResolutionStrategy.IGNORE, "properties", "system");
|
metricManager.registerGauge(null, registryName, sysprops, metricTag, true, "properties", "system");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Error registering JVM metrics", e);
|
log.warn("Error registering JVM metrics", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.solr.security;
|
|
||||||
|
|
||||||
import org.apache.solr.SolrTestCaseJ4;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import javax.security.auth.x500.X500Principal;
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class CertAuthPluginTest extends SolrTestCaseJ4 {
|
|
||||||
private CertAuthPlugin plugin;
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void setupMockito() {
|
|
||||||
SolrTestCaseJ4.assumeWorkingMockito();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
plugin = new CertAuthPlugin();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAuthenticateOk() throws Exception {
|
|
||||||
X500Principal principal = new X500Principal("CN=NAME");
|
|
||||||
X509Certificate certificate = mock(X509Certificate.class);
|
|
||||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
|
|
||||||
when(certificate.getSubjectX500Principal()).thenReturn(principal);
|
|
||||||
when(request.getAttribute(any())).thenReturn(new X509Certificate[] { certificate });
|
|
||||||
|
|
||||||
FilterChain chain = (req, rsp) -> assertEquals(principal, ((HttpServletRequest) req).getUserPrincipal());
|
|
||||||
assertTrue(plugin.doAuthenticate(request, null, chain));
|
|
||||||
|
|
||||||
assertEquals(1, plugin.numAuthenticated.getCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAuthenticateMissing() throws Exception {
|
|
||||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
|
||||||
when(request.getAttribute(any())).thenReturn(null);
|
|
||||||
|
|
||||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
|
||||||
|
|
||||||
assertFalse(plugin.doAuthenticate(request, response, null));
|
|
||||||
verify(response).sendError(eq(401), anyString());
|
|
||||||
|
|
||||||
assertEquals(1, plugin.numMissingCredentials.getCount());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
= Configuring Authentication, Authorization and Audit Logging
|
= Configuring Authentication, Authorization and Audit Logging
|
||||||
:page-children: basic-authentication-plugin, hadoop-authentication-plugin, kerberos-authentication-plugin, jwt-authentication-plugin, cert-authentication-plugin, rule-based-authorization-plugin, audit-logging
|
:page-children: basic-authentication-plugin, hadoop-authentication-plugin, kerberos-authentication-plugin, jwt-authentication-plugin, rule-based-authorization-plugin, audit-logging
|
||||||
// Licensed to the Apache Software Foundation (ASF) under one
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
// or more contributor license agreements. See the NOTICE file
|
// or more contributor license agreements. See the NOTICE file
|
||||||
// distributed with this work for additional information
|
// distributed with this work for additional information
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
= Certificate Authentication Plugin
|
|
||||||
// Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
// or more contributor license agreements. See the NOTICE file
|
|
||||||
// distributed with this work for additional information
|
|
||||||
// regarding copyright ownership. The ASF licenses this file
|
|
||||||
// to you under the Apache License, Version 2.0 (the
|
|
||||||
// "License"); you may not use this file except in compliance
|
|
||||||
// with the License. You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing,
|
|
||||||
// software distributed under the License is distributed on an
|
|
||||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
// KIND, either express or implied. See the License for the
|
|
||||||
// specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
Solr can support extracting the user principal out of the client's certificate with the use of the CertAuthPlugin.
|
|
||||||
|
|
||||||
== Enable Certificate Authentication
|
|
||||||
|
|
||||||
For Certificate authentication, the `security.json` file must have an `authentication` part which defines the class being used for authentication.
|
|
||||||
|
|
||||||
An example `security.json` is shown below:
|
|
||||||
|
|
||||||
[source,json]
|
|
||||||
----
|
|
||||||
{
|
|
||||||
"authentication": {
|
|
||||||
"class":"solr.CertAuthPlugin"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
=== Certificate Validation
|
|
||||||
|
|
||||||
Parts of certificate validation, including verifying the trust chain and peer hostname/ip address will be done by the web servlet container before the request ever reaches the authentication plugin.
|
|
||||||
These checks are described in the <<enabling-ssl.adoc#enabling-ssl,Enabling SSL>> section.
|
|
||||||
|
|
||||||
This plugin provides no additional checking beyond what has been configured via SSL properties.
|
|
||||||
|
|
||||||
=== User Principal Extraction
|
|
||||||
|
|
||||||
This plugin will configure the user principal for the request based on the X500 subject present in the client certificate.
|
|
||||||
Authorization plugins will need to accept and handle the full subject name, for example:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
CN=Solr User,OU=Engineering,O=Example Inc.,C=US
|
|
||||||
----
|
|
||||||
|
|
||||||
A list of possible tags that can be present in the subject name is available in https://tools.ietf.org/html/rfc5280#section-4.1.2.4[RFC-5280, Section 4.1.2.4]. Values may have spaces, punctuation, and other characters.
|
|
||||||
|
|
||||||
It is best practice to verify the actual contents of certificates issued by your trusted certificate authority before configuring authorization based on the contents.
|
|
||||||
|
|
||||||
== Using Certificate Auth with Clients (including SolrJ)
|
|
||||||
|
|
||||||
With certificate authentication enabled, all client requests must include a valid certificate.
|
|
||||||
This is identical to the <<enabling-ssl.adoc#example-client-actions,client requirements>> when using SSL.
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ Authentication makes sure you know the identity of your users. The authenticatio
|
||||||
* <<basic-authentication-plugin.adoc#basic-authentication-plugin,Basic Authentication Plugin>>
|
* <<basic-authentication-plugin.adoc#basic-authentication-plugin,Basic Authentication Plugin>>
|
||||||
* <<hadoop-authentication-plugin.adoc#hadoop-authentication-plugin,Hadoop Authentication Plugin>>
|
* <<hadoop-authentication-plugin.adoc#hadoop-authentication-plugin,Hadoop Authentication Plugin>>
|
||||||
* <<jwt-authentication-plugin.adoc#jwt-authentication-plugin,JWT Authentication Plugin>>
|
* <<jwt-authentication-plugin.adoc#jwt-authentication-plugin,JWT Authentication Plugin>>
|
||||||
* <<cert-authentication-plugin.adoc#cert-authentication-plugin,Certificate Authentication Plugin>>
|
|
||||||
// end::list-of-authentication-plugins[]
|
// end::list-of-authentication-plugins[]
|
||||||
|
|
||||||
=== Authorization Plugins
|
=== Authorization Plugins
|
||||||
|
|
|
@ -47,7 +47,7 @@ solrAdminApp.controller('LoginController',
|
||||||
sessionStorage.setItem("auth.scheme", authScheme);
|
sessionStorage.setItem("auth.scheme", authScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportedSchemes = ['Basic', 'Bearer', 'Negotiate', 'Certificate'];
|
var supportedSchemes = ['Basic', 'Bearer', 'Negotiate'];
|
||||||
$scope.authSchemeSupported = supportedSchemes.includes(authScheme);
|
$scope.authSchemeSupported = supportedSchemes.includes(authScheme);
|
||||||
|
|
||||||
if (authScheme === 'Bearer') {
|
if (authScheme === 'Bearer') {
|
||||||
|
|
|
@ -77,23 +77,6 @@ WWW-Authenticate: {{wwwAuthHeader}}</pre>
|
||||||
<hr/>
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-show="authScheme === 'Certificate'">
|
|
||||||
<h1>Certificate Authentication</h1>
|
|
||||||
<p>Your browser did not provide the required information to authenticate using PKI Certificates.
|
|
||||||
Please check that your computer has a valid PKI certificate for communicating with Solr,
|
|
||||||
and that your browser is properly configured to provide that certificate when required.
|
|
||||||
For more information, consult
|
|
||||||
<a href="https://lucene.apache.org/solr/guide/cert-authentication-plugin.html">
|
|
||||||
Solr's Certificate Authentication documentation
|
|
||||||
</a>.
|
|
||||||
</p>
|
|
||||||
The response from the server was:
|
|
||||||
<hr/>
|
|
||||||
<pre>HTTP 401 {{statusText}}
|
|
||||||
WWW-Authenticate: {{wwwAuthHeader}}</pre>
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-show="authScheme === 'Bearer'">
|
<div ng-show="authScheme === 'Bearer'">
|
||||||
<h1>OpenID Connect (JWT) authentication</h1>
|
<h1>OpenID Connect (JWT) authentication</h1>
|
||||||
<div class="login-error" ng-show="statusText || authParamsError || error">
|
<div class="login-error" ng-show="statusText || authParamsError || error">
|
||||||
|
|
Loading…
Reference in New Issue