forked from elastic/elasticsearch
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PKI realm authentication delegation (elastic#45906)
This commit introduces PKI realm delegation. This feature supports the PKI authentication feature in Kibana. In essence, this creates a new API endpoint which Kibana must call to authenticate clients that use certificates in their TLS connection to Kibana. The API call passes to Elasticsearch the client's certificate chain. The response contains an access token to be further used to authenticate as the client. The client's certificates are validated by the PKI realms that have been explicitly configured to permit certificates from the proxy (Kibana). The user calling the delegation API must have the delegate_pki privilege. Closes elastic#34396
- Loading branch information
1 parent
b249e25
commit 1ebee5b
Showing
58 changed files
with
2,875 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
...vel/src/main/java/org/elasticsearch/client/security/DelegatePkiAuthenticationRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* | ||
* Licensed to Elasticsearch under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch 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.elasticsearch.client.security; | ||
|
||
import org.elasticsearch.client.Validatable; | ||
import org.elasticsearch.client.ValidationException; | ||
import org.elasticsearch.common.xcontent.ToXContentObject; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
|
||
import java.io.IOException; | ||
import java.security.cert.CertificateEncodingException; | ||
import java.security.cert.X509Certificate; | ||
import java.util.Base64; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
|
||
import static java.util.Collections.unmodifiableList; | ||
|
||
public final class DelegatePkiAuthenticationRequest implements Validatable, ToXContentObject { | ||
|
||
private final List<X509Certificate> x509CertificateChain; | ||
|
||
public DelegatePkiAuthenticationRequest(final List<X509Certificate> x509CertificateChain) { | ||
if (x509CertificateChain == null || x509CertificateChain.isEmpty()) { | ||
throw new IllegalArgumentException("certificate chain must not be empty or null"); | ||
} | ||
this.x509CertificateChain = unmodifiableList(x509CertificateChain); | ||
} | ||
|
||
@Override | ||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
builder.startObject().startArray("x509_certificate_chain"); | ||
try { | ||
for (X509Certificate cert : x509CertificateChain) { | ||
builder.value(Base64.getEncoder().encodeToString(cert.getEncoded())); | ||
} | ||
} catch (CertificateEncodingException e) { | ||
throw new IOException(e); | ||
} | ||
return builder.endArray().endObject(); | ||
} | ||
|
||
public List<X509Certificate> getCertificateChain() { | ||
return this.x509CertificateChain; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
final DelegatePkiAuthenticationRequest that = (DelegatePkiAuthenticationRequest) o; | ||
return Objects.equals(x509CertificateChain, that.x509CertificateChain); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(x509CertificateChain); | ||
} | ||
|
||
@Override | ||
public Optional<ValidationException> validate() { | ||
ValidationException validationException = new ValidationException(); | ||
if (false == isOrderedCertificateChain(x509CertificateChain)) { | ||
validationException.addValidationError("certificates chain must be an ordered chain"); | ||
} | ||
return validationException.validationErrors().isEmpty() ? Optional.empty() : Optional.of(validationException); | ||
} | ||
|
||
/** | ||
* Checks that the {@code X509Certificate} list is ordered, such that the end-entity certificate is first and it is followed by any | ||
* certificate authorities'. The check validates that the {@code issuer} of every certificate is the {@code subject} of the certificate | ||
* in the next array position. No other certificate attributes are checked. | ||
*/ | ||
private static boolean isOrderedCertificateChain(List<X509Certificate> chain) { | ||
for (int i = 1; i < chain.size(); i++) { | ||
X509Certificate cert = chain.get(i - 1); | ||
X509Certificate issuer = chain.get(i); | ||
if (false == cert.getIssuerX500Principal().equals(issuer.getSubjectX500Principal())) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
} |
88 changes: 88 additions & 0 deletions
88
...el/src/main/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* Licensed to Elasticsearch under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch 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.elasticsearch.client.security; | ||
|
||
import org.elasticsearch.common.ParseField; | ||
import org.elasticsearch.common.unit.TimeValue; | ||
import org.elasticsearch.common.xcontent.ConstructingObjectParser; | ||
import org.elasticsearch.common.xcontent.XContentParser; | ||
|
||
import java.io.IOException; | ||
import java.util.Objects; | ||
|
||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; | ||
|
||
public final class DelegatePkiAuthenticationResponse { | ||
|
||
private final String accessToken; | ||
private final String type; | ||
private final TimeValue expiresIn; | ||
|
||
public DelegatePkiAuthenticationResponse(String accessToken, String type, TimeValue expiresIn) { | ||
this.accessToken = accessToken; | ||
this.type = type; | ||
this.expiresIn = expiresIn; | ||
} | ||
|
||
public String getAccessToken() { | ||
return accessToken; | ||
} | ||
|
||
public String getType() { | ||
return type; | ||
} | ||
|
||
public TimeValue getExpiresIn() { | ||
return expiresIn; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
final DelegatePkiAuthenticationResponse that = (DelegatePkiAuthenticationResponse) o; | ||
return Objects.equals(accessToken, that.accessToken) && | ||
Objects.equals(type, that.type) && | ||
Objects.equals(expiresIn, that.expiresIn); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(accessToken, type, expiresIn); | ||
} | ||
|
||
private static final ConstructingObjectParser<DelegatePkiAuthenticationResponse, Void> PARSER = new ConstructingObjectParser<>( | ||
"delegate_pki_response", true, | ||
args -> new DelegatePkiAuthenticationResponse((String) args[0], (String) args[1], TimeValue.timeValueSeconds((Long) args[2]))); | ||
|
||
static { | ||
PARSER.declareString(constructorArg(), new ParseField("access_token")); | ||
PARSER.declareString(constructorArg(), new ParseField("type")); | ||
PARSER.declareLong(constructorArg(), new ParseField("expires_in")); | ||
} | ||
|
||
public static DelegatePkiAuthenticationResponse fromXContent(XContentParser parser) throws IOException { | ||
return PARSER.parse(parser, null); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.