Skip to content

Commit

Permalink
Update TokenVerifier to verify authorization too (#4061)
Browse files Browse the repository at this point in the history
* tiny fix in OIDCDiscoveryConfig logger

* Remove unneeded verify method of TokenVerifier

* Move TokenVerifier to receiver package and TokenProvider to dispatcher package

* Add classes for TokenMatchers (Exact and Prefix)

* Add wrapper for EventPolicy

* Add EventPolicies to IngressProducer

* Add authorization check to TokenVerifier

* run hack/update-codegen.sh
  • Loading branch information
creydr authored Aug 22, 2024
1 parent ba49d0f commit fc37e5d
Show file tree
Hide file tree
Showing 27 changed files with 534 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.knative.eventing.kafka.broker.core.oidc;
package dev.knative.eventing.kafka.broker.dispatcher.impl.auth;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
import dev.knative.eventing.kafka.broker.contract.DataPlaneContract;
import dev.knative.eventing.kafka.broker.core.NamespacedName;
import dev.knative.eventing.kafka.broker.core.metrics.Metrics;
import dev.knative.eventing.kafka.broker.core.oidc.TokenProvider;
import dev.knative.eventing.kafka.broker.core.tracing.TracingSpan;
import dev.knative.eventing.kafka.broker.dispatcher.CloudEventSender;
import dev.knative.eventing.kafka.broker.dispatcher.impl.ResponseFailureException;
import dev.knative.eventing.kafka.broker.dispatcher.impl.auth.TokenProvider;
import dev.knative.eventing.kafka.broker.dispatcher.main.ConsumerVerticleContext;
import io.cloudevents.CloudEvent;
import io.cloudevents.http.vertx.VertxMessageFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import dev.knative.eventing.kafka.broker.contract.DataPlaneContract;
import dev.knative.eventing.kafka.broker.core.ReactiveKafkaProducer;
import dev.knative.eventing.kafka.broker.core.eventtype.EventType;
import dev.knative.eventing.kafka.broker.receiver.impl.auth.EventPolicy;
import io.cloudevents.CloudEvent;
import io.fabric8.kubernetes.client.informers.cache.Lister;
import io.vertx.core.Future;
import java.util.List;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;

Expand Down Expand Up @@ -69,4 +71,9 @@ default boolean isEventTypeAutocreateEnabled() {
* @return the OIDC audience for the ingress.
*/
String getAudience();

/**
* @return the applying EventPolicies for the ingress.
*/
List<EventPolicy> getEventPolicies();
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@
import dev.knative.eventing.kafka.broker.core.security.KafkaClientsAuth;
import dev.knative.eventing.kafka.broker.core.utils.ReferenceCounter;
import dev.knative.eventing.kafka.broker.receiver.IngressProducer;
import dev.knative.eventing.kafka.broker.receiver.impl.auth.EventPolicy;
import io.cloudevents.CloudEvent;
import io.cloudevents.core.message.Encoding;
import io.cloudevents.jackson.JsonFormat;
import io.cloudevents.kafka.CloudEventSerializer;
import io.fabric8.kubernetes.client.informers.cache.Lister;
import io.vertx.core.Future;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
Expand Down Expand Up @@ -167,7 +169,8 @@ private Future<Void> onNewIngress(
producerProps,
ingress.getEnableAutoCreateEventTypes(),
this.eventTypeListerFactory.getForNamespace(
resource.getReference().getNamespace()));
resource.getReference().getNamespace()),
EventPolicy.fromContract(ingress.getEventPoliciesList()));

if (isRootPath(ingress.getPath()) && Strings.isNullOrEmpty(ingress.getHost())) {
throw new IllegalArgumentException(
Expand Down Expand Up @@ -276,6 +279,7 @@ private static class IngressProducerImpl implements IngressProducer {
private final Properties producerProperties;
private final DataPlaneContract.Reference reference;
private final String audience;
private final List<EventPolicy> eventPolicies;

private final boolean eventTypeAutocreateEnabled;
private final Lister<EventType> eventTypeLister;
Expand All @@ -287,7 +291,8 @@ private static class IngressProducerImpl implements IngressProducer {
final String host,
final Properties producerProperties,
final boolean eventTypeAutocreateEnabled,
Lister<EventType> eventTypeLister) {
Lister<EventType> eventTypeLister,
final List<EventPolicy> eventPolicies) {
this.producer = producer;
this.topic = resource.getTopics(0);
this.reference = resource.getReference();
Expand All @@ -297,6 +302,7 @@ private static class IngressProducerImpl implements IngressProducer {
this.producerProperties = producerProperties;
this.eventTypeAutocreateEnabled = eventTypeAutocreateEnabled;
this.eventTypeLister = eventTypeLister;
this.eventPolicies = eventPolicies;
}

@Override
Expand All @@ -314,6 +320,11 @@ public String getAudience() {
return audience;
}

@Override
public List<EventPolicy> getEventPolicies() {
return eventPolicies;
}

@Override
public DataPlaneContract.Reference getReference() {
return reference;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@
import static io.netty.handler.codec.http.HttpResponseStatus.OK;

import dev.knative.eventing.kafka.broker.core.file.FileWatcher;
import dev.knative.eventing.kafka.broker.core.oidc.OIDCDiscoveryConfig;
import dev.knative.eventing.kafka.broker.core.oidc.OIDCDiscoveryConfigListener;
import dev.knative.eventing.kafka.broker.core.oidc.TokenVerifier;
import dev.knative.eventing.kafka.broker.core.oidc.TokenVerifierImpl;
import dev.knative.eventing.kafka.broker.core.reconciler.IngressReconcilerListener;
import dev.knative.eventing.kafka.broker.core.reconciler.ResourcesReconciler;
import dev.knative.eventing.kafka.broker.receiver.IngressProducer;
import dev.knative.eventing.kafka.broker.receiver.IngressRequestHandler;
import dev.knative.eventing.kafka.broker.receiver.RequestContext;
import dev.knative.eventing.kafka.broker.receiver.impl.auth.OIDCDiscoveryConfig;
import dev.knative.eventing.kafka.broker.receiver.impl.auth.OIDCDiscoveryConfigListener;
import dev.knative.eventing.kafka.broker.receiver.impl.auth.TokenVerifier;
import dev.knative.eventing.kafka.broker.receiver.impl.auth.TokenVerifierImpl;
import dev.knative.eventing.kafka.broker.receiver.impl.handler.AuthenticationHandler;
import dev.knative.eventing.kafka.broker.receiver.impl.handler.MethodNotAllowedHandler;
import dev.knative.eventing.kafka.broker.receiver.impl.handler.ProbeHandler;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright © 2018 Knative Authors ([email protected])
*
* Licensed 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 dev.knative.eventing.kafka.broker.receiver.impl.auth;

public class AuthenticationException extends Exception {

public AuthenticationException(String message) {
super(message);
}

public AuthenticationException(Exception e) {
super(e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright © 2018 Knative Authors ([email protected])
*
* Licensed 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 dev.knative.eventing.kafka.broker.receiver.impl.auth;

public class AuthorizationException extends Exception {

public AuthorizationException(String message) {
super(message);
}

public AuthorizationException(Exception e) {
super(e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright © 2018 Knative Authors ([email protected])
*
* Licensed 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 dev.knative.eventing.kafka.broker.receiver.impl.auth;

import dev.knative.eventing.kafka.broker.contract.DataPlaneContract;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class EventPolicy {
private final List<TokenMatcher> tokenMatchers;

public static EventPolicy fromContract(DataPlaneContract.EventPolicy contractEventPolicy) {
return new EventPolicy(TokenMatcher.fromContract(contractEventPolicy.getTokenMatchersList()));
}

public static List<EventPolicy> fromContract(List<DataPlaneContract.EventPolicy> contractEventPolicies) {
return contractEventPolicies.stream().map(EventPolicy::fromContract).collect(Collectors.toList());
}

public EventPolicy(List<TokenMatcher> tokenMatchers) {
this.tokenMatchers = tokenMatchers;
}

public boolean isAuthorized(Map<String, List<String>> claims) {
for (TokenMatcher matcher : tokenMatchers) {
if (matcher.match(claims)) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright © 2018 Knative Authors ([email protected])
*
* Licensed 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 dev.knative.eventing.kafka.broker.receiver.impl.auth;

import dev.knative.eventing.kafka.broker.contract.DataPlaneContract;
import java.util.List;
import java.util.Map;

class ExactTokenMatcher implements TokenMatcher {

private final Map<String, String> requiredMatches;

public static ExactTokenMatcher fromContract(DataPlaneContract.Exact exactTokenMatcher) {
return new ExactTokenMatcher(exactTokenMatcher.getAttributesMap());
}

public ExactTokenMatcher(Map<String, String> requiredMatches) {
this.requiredMatches = requiredMatches;
}

@Override
public boolean match(Map<String, List<String>> claims) {
for (var requiredMatch : requiredMatches.entrySet()) {
if (!claims.containsKey(requiredMatch.getKey())
|| !claims.get(requiredMatch.getKey()).contains(requiredMatch.getValue())) {
// as soon as one of the required claims does not match, the matcher should fail
return false;
}
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.knative.eventing.kafka.broker.core.oidc;
package dev.knative.eventing.kafka.broker.receiver.impl.auth;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -32,7 +32,7 @@

public class OIDCDiscoveryConfig {

private static final Logger logger = LoggerFactory.getLogger(TokenVerifier.class);
private static final Logger logger = LoggerFactory.getLogger(OIDCDiscoveryConfig.class);

private static final String OIDC_DISCOVERY_URL = "https://kubernetes.default.svc/.well-known/openid-configuration";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.knative.eventing.kafka.broker.core.oidc;
package dev.knative.eventing.kafka.broker.receiver.impl.auth;

import dev.knative.eventing.kafka.broker.core.features.FeaturesConfig;
import dev.knative.eventing.kafka.broker.core.file.FileWatcher;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.knative.eventing.kafka.broker.core.oidc;
package dev.knative.eventing.kafka.broker.receiver.impl.auth;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright © 2018 Knative Authors ([email protected])
*
* Licensed 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 dev.knative.eventing.kafka.broker.receiver.impl.auth;

import dev.knative.eventing.kafka.broker.contract.DataPlaneContract;
import java.util.List;
import java.util.Map;

class PrefixTokenMatcher implements TokenMatcher {
private final Map<String, String> requiredMatches;

public static PrefixTokenMatcher fromContract(DataPlaneContract.Prefix prefixTokenMatcher) {
return new PrefixTokenMatcher(prefixTokenMatcher.getAttributesMap());
}

public PrefixTokenMatcher(Map<String, String> requiredMatches) {
this.requiredMatches = requiredMatches;
}

@Override
public boolean match(Map<String, List<String>> claims) {
for (Map.Entry<String, String> requiredMatch : requiredMatches.entrySet()) {
if (!claims.containsKey(requiredMatch.getKey())
|| !claims.get(requiredMatch.getKey()).stream()
.anyMatch(s -> s.startsWith(requiredMatch.getValue()))) {
// as soon as one of the required claims does not match, the matcher should fail
return false;
}
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright © 2018 Knative Authors ([email protected])
*
* Licensed 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 dev.knative.eventing.kafka.broker.receiver.impl.auth;

import dev.knative.eventing.kafka.broker.contract.DataPlaneContract;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public interface TokenMatcher {
boolean match(Map<String, List<String>> claims);

static List<TokenMatcher> fromContract(List<DataPlaneContract.TokenMatcher> contractTokenMatchers) {
List<TokenMatcher> matchers = new ArrayList<>(contractTokenMatchers.size());

for (var contractTokenMatcher : contractTokenMatchers) {
switch (contractTokenMatcher.getMatcherCase()) {
case EXACT -> matchers.add(ExactTokenMatcher.fromContract(contractTokenMatcher.getExact()));
case PREFIX -> matchers.add(PrefixTokenMatcher.fromContract(contractTokenMatcher.getPrefix()));
}
}

return matchers;
}
}
Loading

0 comments on commit fc37e5d

Please sign in to comment.