-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #52 from jpkrohling/HAWKULAR-265-HostSynonyms
HAWKULAR-265 - Better solution for the host synonyms
- Loading branch information
Showing
7 changed files
with
456 additions
and
19 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
49 changes: 49 additions & 0 deletions
49
common/src/main/java/org/hawkular/accounts/common/AuthServerHostSynonymService.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,49 @@ | ||
/* | ||
* Copyright 2015 Red Hat, Inc. and/or its affiliates | ||
* and other contributors as indicated by the @author tags. | ||
* | ||
* 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 org.hawkular.accounts.common; | ||
|
||
/** | ||
* @author Juraci Paixão Kröhling | ||
*/ | ||
public interface AuthServerHostSynonymService { | ||
/** | ||
* Determines if the given host is a synonym for any of the hosts that our auth server runs on. The rules are: | ||
* <ul> | ||
* <li> | ||
* If the system property org.hawkular.accounts.auth.host.synonyms is specified, then the host is matched | ||
* against this list. This list should include all IPs and hostnames that are considered synonyms. | ||
* </li> | ||
* <li> | ||
* If the system property is not set, we assume that the auth server is running on the same | ||
* application server instance as Hawkular. We then build a list of local IPs. If the parameter | ||
* {@param host} is an IP address, it's matched with this list. If it's a host, then the host is resolved | ||
* to an IP, which is then matched with the list. The list of IPs is derived from the property | ||
* jboss.bind.address as follows: | ||
* <ul> | ||
* <li>If it's not defined, then 127.0.0.1 is assumed (based on Wildfly's defaults)</li> | ||
* <li>If it's 0.0.0.0, then all available NICs are queried and its IPs are added to a cache</li> | ||
* <li>If it's not an IP, then the hostname is resolved into an IP</li> | ||
* <li>If it's an IP, it's added as the single entry to the list.</li> | ||
* </ul> | ||
* </li> | ||
* </ul> | ||
* | ||
* @param host the host to check if it's a synonym for the local host. | ||
* @return whether or not the requested host is a synonym for the auth server being used by this instance | ||
*/ | ||
boolean isHostSynonym(String host); | ||
} |
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
188 changes: 188 additions & 0 deletions
188
...src/main/java/org/hawkular/accounts/common/internal/AuthServerHostSynonymServiceImpl.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,188 @@ | ||
/* | ||
* Copyright 2015 Red Hat, Inc. and/or its affiliates | ||
* and other contributors as indicated by the @author tags. | ||
* | ||
* 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 org.hawkular.accounts.common.internal; | ||
|
||
import java.net.InetAddress; | ||
import java.net.NetworkInterface; | ||
import java.net.SocketException; | ||
import java.net.UnknownHostException; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.Enumeration; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
import javax.annotation.PostConstruct; | ||
import javax.enterprise.context.ApplicationScoped; | ||
|
||
import org.hawkular.accounts.common.AuthServerHostSynonymService; | ||
|
||
/** | ||
* @author Juraci Paixão Kröhling | ||
*/ | ||
@ApplicationScoped | ||
public class AuthServerHostSynonymServiceImpl implements AuthServerHostSynonymService { | ||
private MsgLogger logger = MsgLogger.LOGGER; | ||
private static final String WILDCARD_ADDRESS = "0.0.0.0"; | ||
private static final String DEFAULT_ADDRESS = "127.0.0.1"; | ||
|
||
/** | ||
* For this service, we have two algorithms: the first is just a match between the specified system property | ||
* and the value that is to be checked. The second is here called as 'best effort', and | ||
* we try our best to tell if a given host is a synonym for the local host. | ||
*/ | ||
private boolean bestEffort = true; | ||
|
||
private Set<String> hostSynonyms; | ||
private Map<String, Boolean> hostnameCache; | ||
private Set<InetAddress> localIPs; | ||
private String synonyms; | ||
private String boundToAddress; | ||
|
||
public AuthServerHostSynonymServiceImpl() { | ||
synonyms = System.getProperty("org.hawkular.accounts.auth.host.synonyms"); | ||
boundToAddress = System.getProperty("jboss.bind.address"); | ||
} | ||
|
||
public AuthServerHostSynonymServiceImpl(String synonyms, String boundToAddress) { | ||
this.synonyms = synonyms; | ||
this.boundToAddress = boundToAddress; | ||
} | ||
|
||
@PostConstruct | ||
void determineHostSynonyms() { | ||
if (synonyms != null && !synonyms.isEmpty()) { | ||
hostSynonyms = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(synonyms.split(",")))); | ||
bestEffort = false; | ||
logger.listOfSynonymsProvided(); | ||
return; | ||
} | ||
|
||
// we have not found the system property, proceed with trying to get the local IPs | ||
localIPs = new HashSet<>(); | ||
hostnameCache = new HashMap<>(); | ||
|
||
// if we are not bound to a specific address, then we are implicitly bound to 127.0.0.1 | ||
if (boundToAddress == null || boundToAddress.isEmpty()) { | ||
boundToAddress = DEFAULT_ADDRESS; | ||
} | ||
|
||
// if we are bound to 0.0.0.0, then we are bound to all local addresses (not 100% true, but true for our case) | ||
if (WILDCARD_ADDRESS.equals(boundToAddress)) { | ||
logger.allLocalAddressesForSynonyms(); | ||
determineLocalAddresses(); | ||
try { | ||
localIPs.add(InetAddress.getByName(WILDCARD_ADDRESS)); | ||
} catch (UnknownHostException e) { | ||
// should *not* happen, but if it does, then let's just ignore... this is a "plus" | ||
logger.cannotDetermineIPForWildcardHost(WILDCARD_ADDRESS); | ||
} | ||
return; | ||
} | ||
|
||
// at this point, we are either bound to a specific address, or to a hostname | ||
// if we are bound to a hostname, let's try to find all IPs for it, otherwise, we just add the IP to the list | ||
InetAddress[] addressForHost; | ||
try { | ||
addressForHost = InetAddress.getAllByName(boundToAddress); | ||
} catch (UnknownHostException e) { | ||
// how can that be? we are bound to a host which is not known to us? it happens, but should be very rare | ||
logger.cannotDetermineIPForHost(WILDCARD_ADDRESS, e); | ||
return; | ||
} | ||
|
||
Collections.addAll(localIPs, addressForHost); | ||
} | ||
|
||
@Override | ||
public boolean isHostSynonym(String host) { | ||
if (!bestEffort) { | ||
return hostSynonyms.contains(host); | ||
} | ||
|
||
// first, check the cache | ||
if (hostnameCache.containsKey(host)) { | ||
return hostnameCache.get(host); | ||
} | ||
|
||
return checkNewHost(host); | ||
} | ||
|
||
boolean checkNewHost(String host) { | ||
// not in the cache, so, proceed | ||
InetAddress[] addressForHost; | ||
try { | ||
addressForHost = InetAddress.getAllByName(host); | ||
} catch (UnknownHostException e) { | ||
// no, this host is not known at all, it can't be us... | ||
return false; | ||
} | ||
|
||
boolean found = false; | ||
for (InetAddress address : addressForHost) { | ||
if (localIPs.contains(address)) { | ||
found = true; | ||
} | ||
} | ||
|
||
hostnameCache.put(host, found); | ||
return found; | ||
} | ||
|
||
void determineLocalAddresses() { | ||
// Networking is a complex matter, and I'm not even trying to get this algorithm into a state where it will | ||
// be correct most of the times, rather, I'm trying to provide a good out-of-the-box experience. | ||
// For production environments, we should really try to document somewhere that admins *should* set the | ||
// system property. The reason is: on production machines, we might or might not want to assume that some | ||
// NICs and/or virtual interfaces are synonyms. One situation where we *don't* want is for bridged interfaces | ||
// (like docker ones), which usually refers to a container, not the host. Other situations that we don't even | ||
// try to handle here: firewalls and frontend-proxies. For instance, a company might have a load balancer or | ||
// frontend-proxy with a public IP address and hostname, but all we (backend) see is a list of private | ||
// addresses. | ||
Enumeration<NetworkInterface> networkInterfaceEnumeration = getNetworkInterfaces(); | ||
|
||
while (networkInterfaceEnumeration.hasMoreElements()) { | ||
NetworkInterface networkInterface = networkInterfaceEnumeration.nextElement(); | ||
Enumeration<InetAddress> addressEnumeration = networkInterface.getInetAddresses(); | ||
while (addressEnumeration.hasMoreElements()) { | ||
InetAddress address = addressEnumeration.nextElement(); | ||
localIPs.add(address); | ||
} | ||
} | ||
} | ||
|
||
Set<InetAddress> getLocalIPs() { | ||
if (null == localIPs) { | ||
return null; | ||
} | ||
return Collections.unmodifiableSet(localIPs); | ||
} | ||
|
||
Enumeration<NetworkInterface> getNetworkInterfaces() { | ||
try { | ||
return NetworkInterface.getNetworkInterfaces(); | ||
} catch (SocketException e) { | ||
// uh-oh, we are in trouble... we don't have the system property and we are bound to "all" addresses | ||
// we don't have much choice other than to re-throw it as a runtime exception | ||
logger.cannotDetermineLocalIPs(e); | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
} |
54 changes: 54 additions & 0 deletions
54
common/src/main/java/org/hawkular/accounts/common/internal/MsgLogger.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,54 @@ | ||
/* | ||
* Copyright 2015 Red Hat, Inc. and/or its affiliates | ||
* and other contributors as indicated by the @author tags. | ||
* | ||
* 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 org.hawkular.accounts.common.internal; | ||
|
||
import org.jboss.logging.Logger; | ||
import org.jboss.logging.annotations.Cause; | ||
import org.jboss.logging.annotations.LogMessage; | ||
import org.jboss.logging.annotations.Message; | ||
import org.jboss.logging.annotations.MessageLogger; | ||
import org.jboss.logging.annotations.ValidIdRange; | ||
|
||
/** | ||
* @author Juraci Paixão Kröhling | ||
*/ | ||
@MessageLogger(projectCode = "HAWKACC") | ||
@ValidIdRange(min = 150000, max = 159999) | ||
public interface MsgLogger { | ||
MsgLogger LOGGER = Logger.getMessageLogger(MsgLogger.class, MsgLogger.class.getPackage().getName()); | ||
|
||
@LogMessage(level = Logger.Level.DEBUG) | ||
@Message(id = 150000, value = "List of host synonyms provided. Using the list instead of guessing.") | ||
void listOfSynonymsProvided(); | ||
|
||
@LogMessage(level = Logger.Level.DEBUG) | ||
@Message(id = 150001, value = "Bound to wildcard address 0.0.0.0, getting a list of all local IPs for Synonyms") | ||
void allLocalAddressesForSynonyms(); | ||
|
||
@LogMessage(level = Logger.Level.DEBUG) | ||
@Message(id = 150002, value = "Could not process what's the IP for the wildcard host: [%s]") | ||
void cannotDetermineIPForWildcardHost(String host); | ||
|
||
@LogMessage(level = Logger.Level.WARN) | ||
@Message(id = 150003, value = "Could not process what's the IP for the host: [%s]") | ||
void cannotDetermineIPForHost(String host, @Cause Throwable t); | ||
|
||
@LogMessage(level = Logger.Level.FATAL) | ||
@Message(id = 150004, value = "Could not process what are our IPs. Host synonyms will *not* work properly") | ||
void cannotDetermineLocalIPs(@Cause Throwable t); | ||
|
||
} |
Oops, something went wrong.