Spring cloud eureka client to multiple eureka servers - spring-cloud

I am able to get the eureka server to operate in a peer to peer mode. But one thing I am curious about is how do I get a service discovery client to register to multiple eureka servers.
My use case is this:
Say I have a service registering to one of the eureka servers (e.g. server A) and that registration is replicated to its peer. The service is actually pointing at server A. If server A goes down, and the client expects to renew with server A, how do the renewal work if server A is no longer present.
Do I need to register with both and if not then how does the renewal happen if the client cannot communicate with server A. Does it have some knowledge of server B (from its initial and/or subsequent comms with A) and fail over to do its registration renewal there? That is not clear in any of the docs and I need to verify
So based on the answer, I added the following to my application.yml
eureka:
# these are settings for the client that gets services
client:
# enable these two settings if you want discovery to work
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:8762/eureka/, http://localhost:8761/eureka/
It only registers to the first in the comma separated list. If I switch them around the registration flips between eureka servers.
I can see that it does separate these based on comma but my guess is that Eureka does not use this underneath (from EurekaClientConfigBean.java)
#Override
public List<String> getEurekaServerServiceUrls(String myZone) {
String serviceUrls = this.serviceUrl.get(myZone);
if (serviceUrls == null || serviceUrls.isEmpty()) {
serviceUrls = this.serviceUrl.get(DEFAULT_ZONE);
}
if (serviceUrls != null) {
return Arrays.asList(serviceUrls.split(","));
}
return new ArrayList<>();
}

I just reviewed the source code for Eureka 1.1.147. It works differently that i expected but at least I know now.
You can put multiple service urls in the set
serviceUrl:
defaultZone: http://localhost:8762/eureka/, http://localhost:8761/eureka/
But the register action only uses the first one to register. There remaining are used only if attempting to contact the first fails.
from (DiscoveryClient.java)
/**
* Register with the eureka service by making the appropriate REST call.
*/
void register() {
logger.info(PREFIX + appPathIdentifier + ": registering service...");
ClientResponse response = null;
try {
response = makeRemoteCall(Action.Register);
isRegisteredWithDiscovery = true;
logger.info(PREFIX + appPathIdentifier + " - registration status: "
+ (response != null ? response.getStatus() : "not sent"));
} catch (Throwable e) {
logger.error(PREFIX + appPathIdentifier + " - registration failed"
+ e.getMessage(), e);
} finally {
if (response != null) {
response.close();
}
}
}
which calls
private ClientResponse makeRemoteCall(Action action) throws Throwable {
return makeRemoteCall(action, 0);
}
It only calls the backup when an exception is thrown in the above makeRemoteCall(action, 0) call
} catch (Throwable t) {
closeResponse(response);
String msg = "Can't get a response from " + serviceUrl + urlPath;
if (eurekaServiceUrls.get().size() > (++serviceUrlIndex)) {
logger.warn(msg, t);
logger.warn("Trying backup: " + eurekaServiceUrls.get().get(serviceUrlIndex));
SERVER_RETRY_COUNTER.increment();
return makeRemoteCall(action, serviceUrlIndex);
} else {
ALL_SERVER_FAILURE_COUNT.increment();
logger.error(
msg
+ "\nCan't contact any eureka nodes - possibly a security group issue?",
t);
throw t;
}
So you can't really register to two eureka servers simultaneously from this code. Unless I missed something.

Your client application should be provided a list of Eureka URLs. The URLs are comma separated.

Yes, as per the documentation, the flow is:
client registers to the first available eureka server
registrant information is replicated between eureka server nodes.
So multiple registration is not only not needed but should be avioded.

Please add below property in application.property or application.yml file
eureka.client.service-url.defaultZone = http://localhost:8761/eureka,http://localhost:8762/eureka
Services will be registered with both eureka server.If one eureka server is down then request will be served from other eureka server.

If you want to a workaround to register on multiple eureka servers.
please review my answer on similar question: https://stackoverflow.com/a/60714917/7982168

Related

MqttBrowserClient fails to connect due to missing conack package

I am trying to make webapp over flutter which will connect to HIVE broker. I took the broker name from the official website, set the port number to 8000 just like mentioned there and still get the error message as below:
error is mqtt-client::NoConnectionException: The maximum allowed connection attempts ({1}) were exceeded. The broker is not responding to the connection request message (Missing Connection Acknowledgement?
I really have no clue how to proceed. Can someone please help?
Below is my code:
MqttBrowserClient mq = MqttBrowserClient(
'wss://broker.mqttdashboard.com:8000', '',
maxConnectionAttempts: 1);
/*
MqttBrowserClient mq = MqttBrowserClient('ws://test.mosquitto.org', 'client-1',
maxConnectionAttempts: 1);
*/
class mqttService {
Future<MqttBrowserClient?> connectToServer() async {
try {
final connMess = MqttConnectMessage()
.withClientIdentifier('clientz5tWzoydVL')
.authenticateAs('a14guguliye', 'z5tWzoydVL')
.withWillTopic('willtopic')
.withWillMessage('My Will message')
.startClean() // Non persistent session for testing
.withWillQos(MqttQos.atLeastOnce);
mq.port = 1883;
mq.keepAlivePeriod = 50;
mq.connectionMessage = connMess;
mq.websocketProtocols = MqttClientConstants.protocolsSingleDefault;
mq.onConnected = onConnected;
var status = await mq.connect();
return mq;
} catch (e) {
print("error is " + e.toString());
mq.disconnect();
return null;
}
}
}
That port 8000 may be open but the HiveMQ broker may not be listening.
Make sure that the broker is fully booted and binds to that IP:Port combo.
In the HiveMQ broker startup output, you should see something similar to:
Started Websocket Listener on address 0.0.0.0 and on port 8000
If needed, the HiveMQ Broker configuration documentation is here.
You can use the public HiveMQ MQTT Websocket demo client to test your connection to make sure it's not a local code issue.
As a last option, use Wireshark to monitor MQTT traffic with a filter of tcp.port == 8000 and mqtt

Can netty establish a connect connection with an https link?

I want to complete a function with netty
http send data to netty -> netty proxy data to target https
I start a client to connet target link.
I tried proxy to http ,it's ok
But when i use https link , future.isSuccess() return false
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(ctx.channel().getClass())
.handler(new HttpProxyInitializer(ctx.channel()));
ChannelFuture f = b.connect("https://xxxx",443);
outboundChannel = f.channel();
f.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
future.channel().writeAndFlush(msg);
} else {
ctx.channel().close();
}
}
});
}
}
So is netty connet https not feasible?
How you connect does not look correct.
You would use:
b.connect("hostname", 443)
Ensure your HttpProxyInitializer also add the SslHandler to the ChannelPipeline.
Also if things not work you should inspect the future.cause() as it will contain a Throwable which will provide details about why the operation failed.

Is it possible to secure a ColdFusion 11 REST Service with HTTP BASIC Authentication?

I am setting up a simple REST Service in ColdFusion 11. The web server is IIS 8.5 on Windows Server 2012R2.
This REST Service needs to be secured to prevent unauthorized users from accessing/writing data. For the time being, there will be only one authorized user, so I want to keep authentication/authorization as simple as possible. My initial thought is to use HTTP BASIC Authentication.
Here's the setup for the REST Service:
Source Directory: C:\web\site1\remoteapi\
REST path: inventory
To implement this, I configured the source directory of the REST Service in IIS to authorize only one user, disable Anonymous authentication, and enable Basic authentication.
When I call the source directory directly in a browser (i.e. http://site1/remoteapi/inventory.cfc?method=read), I am presented with the Basic authentication dialog.
However, when I attempt to request the REST path (http://site1/rest/inventory/), I am not challenged at all.
How can I implement HTTP BASIC authentication on the REST path?
So, due to the need to get this done without much delay, I went ahead and using some principles from Ben Nadel's website, I wrote my own authentication into the onRequestStart() method of the REST Service's Application.cfc. Here is the basic code, though it uses hard-coded values in the VARIABLES scope to validate the username and password and also does not include any actual "authorization" setting:
public boolean function onRequestStart(required string targetPage) {
LOCAL.Response = SUPER.onRequestStart(ARGUMENTS.targetpage);
if (!StructKeyExists(GetHTTPRequestData().Headers, "Authorization")) {
cfheader(
name="WWW-Authenticate",
value="Basic realm=""REST API Access"""
);
LOCAL.RESTResponse = {
status = 401,
content = {Message = "Unauthorized"}
};
restSetResponse(LOCAL.RESTResponse);
}
else {
LOCAL.IsAuthenticated = true;
LOCAL.EncodedCredentials =
GetToken( GetHTTPRequestData().Headers.Authorization, 2, " " );
// Credential string is not Base64
if ( !ArrayLen(
REMatch(
"^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$",
LOCAL.EncodedCredentials
)
)
) {
LOCAL.IsAuthenticated = false;
}
else {
// Convert Base64 to String
LOCAL.Credentials =
ToString(ToBinary( LOCAL.EncodedCredentials ));
LOCAL.Username = GetToken( LOCAL.Credentials, 1, ":" );
LOCAL.Password = GetToken( LOCAL.Credentials, 2, ":" );
if ( LOCAL.Username != VARIABLES.CREDENTIALS.Username
|| LOCAL.Password != VARIABLES.CREDENTIALS.Password
) {
LOCAL.IsAuthenticated = false;
}
}
if (!LOCAL.IsAuthenticated) {
LOCAL.Response = {
status = 403,
content = {Message = "Forbidden"}
};
restSetResponse(LOCAL.Response);
}
}
return LOCAL.Response;
}

LDAP Authentication not working with Online LDAP Test Server

I am using the below configuration to connect to Online LDAP Test Server
<className>com.worklight.core.auth.ext.LdapLoginModule</className>
<parameter name="ldapProviderUrl" value="ldap://forumsys.com:389"/>
<parameter name="ldapTimeoutMs" value="2000"/>
<parameter name="ldapSecurityAuthentication" value="simple"/>
<parameter name="validationType" value="searchPattern"/>
<parameter name="ldapSecurityPrincipalPattern" value="uid={username},ou=mathematicians,dc=example,dc=com"/>
<parameter name="ldapSearchFilterPattern" value="(uid={username})"/>
<parameter name="ldapSearchBase" value="dc=example,dc=com"/>
But i am getting
"FWLSE4014W: LdapLoginModule authentication failed. Reason 'javax.naming.CommunicationException: forumsys.com:389 [Root exception is java.net.SocketTimeoutException: connect timed out]" error.
Is there anything wrong with the settings ?
that server is pretty old and the LDAP service is no longer active
This exception is not unique to MobileFirst and so I remove this information from the question.
See here: http://www-01.ibm.com/support/docview.wss?uid=swg21599197.
I suggest that you will follow the same solution steps, which are to verify that you are attempting to correctly connect to your LDAP server.
Recommended but optional: Download a third-party tool (such as the ldapsearch tool) that can verify your server(s) are able to communicate with the LDAP server independent of the Portal ConfigEngine configuration task. Run the tool directly from the Portal server (and Deployment Manager if clustered) to verify all servers can communicate with the LDAP server.
In this particular use case, a network firewall was configured to block all traffic to the LDAP server except from IP addresses that were explicitly whitelisted / permitted to connect. The primary Portal server had been configured in the network firewall to communicate with the LDAP server, but the Deployment Manager had not been configured. Adding the Deployment Manager IP address to the firewall rules allowed the configuration task to complete successfully.
I recently used ForumSys LDAP Test server mentioned using KeyCloak successfully:
You can find all the configuration here.
If you are looking for an easily configurable dockerized test LDAP authentication server.
try
https://hub.docker.com/r/upekshejay/simple-ldap-test-server
I used ForumSys LDAP Test Server
, here is fully tested sample used to check if a user is already inside an active directory.
public class ActiveDirectoryManagerTwo {
final static String domainTree = "dc=example,dc=com";
public static void main(String[] args) {
System.out.println(isInsideActiveDirectory("tesla"));
}
public static String isInsideActiveDirectory(String userName) {
String isFound = "NO";
String rootDN = "cn=read-only-admin,dc=example,dc=com";
String rootPWD = "password";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, "ldap://ldap.forumsys.com :389");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, rootDN);
environment.put(Context.SECURITY_CREDENTIALS, rootPWD);
DirContext dirContext = null;
NamingEnumeration<?> results = null;
try {
System.out.println("inside try");
dirContext = new InitialDirContext(environment);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(uid=" + userName + "))";
results = dirContext.search(domainTree, filter, controls);
if (results.hasMore()) {
isFound = "YES";
} else {
}
} catch (NamingException e) {
System.out.println("inside catch");
e.printStackTrace();
} finally {
if (results != null) {
try {
results.close();
} catch (Exception e) {
}
}
if (dirContext != null) {
try {
dirContext.close();
} catch (Exception e) {
}
}
}
return isFound;
}
}

fulljid is empty after connection to BOSH service with XMPHP

I am trying to pre-bind an XMPP session via XMPHP and pass the rid/sid/jid to a strophe client to attach to the session.
connection code here:
$conn = new CIRCUIT_BOSH('server.com', 7070, $username, $pass, $resource, 'server.com', $printlog=true, $loglevel=XMPPHP_Log::LEVEL_VERBOSE);
$conn->autoSubscribe();
try{
$conn->connect('http://xmpp.server.com/http-bind', 1, true);
$log->lwrite('Connected!');
}catch(XMPPHP_Exception $e){
die($e->getMessage());
}
I am getting the rid and sid but the fulljid in the $conn object stays empty and I cant see a session started on my openfire admin console.
If I create the jid manually by using the given resource and passing jid/rid/sid to strophe to use in attach, I get the ATTACHED status and I see calls from the client to the BOSH ip but I still dont see a session and I cant use the connection.
Strophe Client Code:
Called on document ready:
var sid = $.cookie('sid');
var rid = $.cookie('rid');
var jid = $.cookie('jid');
$(document).trigger('attach', {
sid: sid,
rid: rid,
jid: jid,
});
$(document).bind('attach', function (ev, data) {
var conn = new Strophe.Connection(
"http://xmpp.server.com/http-bind");
conn.attach(data.jid, data.sid, data.rid, function (status) {
if (status === Strophe.Status.CONNECTED) {
$(document).trigger('connected');
} else if (status === Strophe.Status.DISCONNECTED) {
$(document).trigger('disconnected');
} else if (status === Strophe.Status.ATTACHED){
$(document).trigger('attached');
}
});
Object.connection = conn;
});
I think the problem starts on the XMPPHP side which is not creating the session properly.
'attached' is triggered but never 'connected', is status 'connected' supposed to be sent?
What am I missing?
Ok, solved, I saw that XMPPHP lib didn't create a session at all on the openfire server, so I wrote a simple test for the XMPP class which was good and created the session, and for the XMPP_BOSH class that didn't manage create one. Then I saw the issue report here: http://code.google.com/p/xmpphp/issues/detail?id=47 comment no.9 worked, it fixed the issue by copying the processUntil() function from the XMLStream.php to BOSH.php, still can't figure out why this is working. Then I found I had an overlapping bug also with some of the passwords set for users on the openfire server. These passwords contained these ! # % ^ characters, for some reason the XMPP_BOSH is sending the password corrupted or changed so I got Auth Failed exception. Changing the password fixed the issue and I can now attach to the session XMPPHP created with the Strophe.js library.