From 044bddc661e321fefbd060be4bdfbc5eec3dab10 Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Sat, 4 Nov 2023 14:04:05 +0100 Subject: [PATCH] keycloak: Polishing example for Network based authentication step --- .../custom/auth/net/NetworkAuthenticator.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/keycloak/extensions/src/main/java/com/github/thomasdarimont/keycloak/custom/auth/net/NetworkAuthenticator.java b/keycloak/extensions/src/main/java/com/github/thomasdarimont/keycloak/custom/auth/net/NetworkAuthenticator.java index 3d7e2b2b..0d049cd9 100644 --- a/keycloak/extensions/src/main/java/com/github/thomasdarimont/keycloak/custom/auth/net/NetworkAuthenticator.java +++ b/keycloak/extensions/src/main/java/com/github/thomasdarimont/keycloak/custom/auth/net/NetworkAuthenticator.java @@ -83,7 +83,8 @@ public void authenticate(AuthenticationFlowContext flowContext) { var allowedNetworks = resolveAllowedNetworks(flowContext.getAuthenticatorConfig(), client); if (allowedNetworks == null) { // skip check since we don't have any network restrictions configured - log.debugf("Skip check for source IP based on network. realm=%s, client=%s, IP=%s", realm.getName(), client.getClientId(), remoteIp); + log.debugf("Skip check for source IP based on network. realm=%s, client=%s, IP=%s", // + realm.getName(), client.getClientId(), remoteIp); flowContext.success(); return; } @@ -92,8 +93,12 @@ public void authenticate(AuthenticationFlowContext flowContext) { for (String allowedNetwork : allowedNetworks.split(",")) { ipAllowed = isRemoteIpAllowed(allowedNetwork, remoteIp); if (ipAllowed) { - log.debugf("Allowed source IP based on network. realm=%s, client=%s, IP=%s, network=%s", realm.getName(), client.getClientId(), remoteIp, allowedNetwork); + log.debugf("Allowed source IP based on network. realm=%s, client=%s, IP=%s, network=%s", // + realm.getName(), client.getClientId(), remoteIp, allowedNetwork); break; + } else { + log.tracef("Rejected source IP based on allowed network. realm=%s, client=%s, IP=%s, network=%s", // + realm.getName(), client.getClientId(), remoteIp, allowedNetwork); } } @@ -102,7 +107,8 @@ public void authenticate(AuthenticationFlowContext flowContext) { return; } - log.debugf("Rejected source IP based on allowed networks. realm=%s, client=%s, IP=%s", realm.getName(), client.getClientId(), remoteIp); + log.debugf("Rejected source IP based on allowed networks. realm=%s, client=%s, IP=%s", // + realm.getName(), client.getClientId(), remoteIp); var challengeResponse = errorResponse(flowContext, Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_request", "Access denied", authSession.getAuthNote("auth_type")); flowContext.failure(AuthenticationFlowError.ACCESS_DENIED, challengeResponse); @@ -172,23 +178,28 @@ String getAllowedNetworksForClient(ClientModel client) { @VisibleForTesting boolean isRemoteIpAllowed(String allowedNetwork, String remoteIp) { - boolean matches = false; + boolean allowed = false; if (allowedNetwork.contains("/")) { - // CIDR notation + /* + CIDR notation, e.g: + 192.168.178.0/24 - Allow access from a subnet + 192.168.178.10/32 - Allow access from a single IP + */ var ipAndCidrRange = allowedNetwork.split("/"); var ip = ipAndCidrRange[0]; int cidrRange = Integer.parseInt(ipAndCidrRange[1]); var rule = new IpSubnetFilterRule(ip, cidrRange, IpFilterRuleType.ACCEPT); - matches = rule.matches(new InetSocketAddress(remoteIp, 1 /* unsed */)); + allowed = rule.matches(new InetSocketAddress(remoteIp, 1 /* unsed */)); } else { - // explicit IP addresses - if (remoteIp.equals(allowedNetwork.trim())) { - matches = true; - } + /* + explicit IP addresses, e.g: + 192.168.178.10 - Allow access from a single IP + */ + allowed = remoteIp.equals(allowedNetwork.trim()); } - return matches; + return allowed; } @VisibleForTesting @@ -268,12 +279,6 @@ public static class Factory implements AuthenticatorFactory { static final List CONFIG_PROPERTIES; - static final String DISPLAY_NAME = "Acme: Network Authenticator"; - - static final String REFERENCE_CATEGORY = "network"; - - static final String HELP_TEXT = "Controls access by checking the network address of the incoming request."; - static { var list = ProviderConfigurationBuilder.create() // .property().name(REMOTE_IP_HEADER_PROPERTY) // @@ -290,7 +295,6 @@ public static class Factory implements AuthenticatorFactory { .helpText("Comma separated list of allowed networks. This supports CIDR network ranges and single IP adresses. If left empty ALL networks are allowed. Configuration can be overriden via client attribute acmeAllowedNetworks. Examples: 192.168.178.0/24, 192.168.178.12/32, 192.168.178.13") // .add() // - .build(); CONFIG_PROPERTIES = Collections.unmodifiableList(list);