Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication failure on CRAM-MD5 #144

Open
SaeedZhiany opened this issue Jan 7, 2024 · 0 comments
Open

Authentication failure on CRAM-MD5 #144

SaeedZhiany opened this issue Jan 7, 2024 · 0 comments

Comments

@SaeedZhiany
Copy link

SaeedZhiany commented Jan 7, 2024

Hello @killme2008

I'm using this library to connect a Memcached node with CRAM-MD5 SASL authentication. but I'm getting an authentication error.

I should mention that I tried similar code using the library net.spy.memcached and its client can successfully connect and fetch the data.

the versions I'm using:
this library: 2.4.8
library net.spy.memcached: 2.12.3

here is a reproducible code. just remember I created and ran 3 different Memcached nodes on 3 different ports on my local PC to test these two libraries against all the authentication methods (no authentication, PLAIN, and CRAM-MD5). both libraries successfully connect and fetch data using no authentication and PLAIN methods. but when I'm trying CRAM-MD5, only net.spy.memcached client can pass the authentication and get the data. Xmemcached client failed to authenticate. I'm wondering why. it seems I'm using closely similar configurations in both libraries. you can create similar nodes on your machine and then test the code below yourself.

My question is what should I do to make the Xmemcached client pass the CRAM-MD5 authentication? I prefer using your library due to its synchronized methods. I'm using Memcached in an environment that does not need more than one node and also one client. so I don't want to struggle with the multithreading problems.

Thanks for your help.

import com.google.code.yanf4j.core.Session;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClient;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.auth.AuthInfo;
import net.rubyeye.xmemcached.auth.PlainCallbackHandler;
import net.rubyeye.xmemcached.command.BinaryCommandFactory;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.networking.MemcachedSession;
import net.spy.memcached.*;
import net.spy.memcached.auth.AuthDescriptor;
import net.spy.memcached.protocol.TCPMemcachedNodeImpl;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeoutException;

public class App {
    public enum MemcachedAuthenticationMethod {
        NONE(null, null, null, 11213),
        PLAIN("PLAIN", "username", "password", 11211),
        CRAM_MD5("CRAM-MD5", "username", "123456", 11212);
        private final String value;
        private final String username;
        private final String password;
        private final Integer port;

        MemcachedAuthenticationMethod(String value, String username, String password, Integer port) {
            this.value = value;
            this.username = username;
            this.password = password;
            this.port = port;
        }

        public String getValue() {
            return value;
        }

        public String getUsername() {
            return username;
        }

        public String getPassword() {
            return password;
        }

        public Integer getPort() {
            return port;
        }
    }

    public static final MemcachedAuthenticationMethod memcachedAuthMethod = MemcachedAuthenticationMethod.CRAM_MD5;
    public static final InetSocketAddress inetSocketAddress = new InetSocketAddress("memcached.obj.com", memcachedAuthMethod.getPort());

    public static void showStatInfo(Map<String, String> generalStats, Map<String, String> settingsStats) throws Exception {
        if (generalStats == null || generalStats.isEmpty()) {
            throw new Exception("general stats is empty");
        }
        System.out.println(inetSocketAddress);
        System.out.println("version: " + generalStats.get("version"));
        System.out.println("pid: " + generalStats.get("pid"));
        System.out.println("accepting_conns: " + generalStats.get("accepting_conns"));
        System.out.println("tcpport: " + settingsStats.get("tcpport"));
        System.out.println("udpport: " + settingsStats.get("udpport"));
        System.out.println("evictions: " + settingsStats.get("evictions"));
        System.out.println("item_size_max: " + settingsStats.get("item_size_max"));
        System.out.println("cas_enabled: " + settingsStats.get("cas_enabled"));
        System.out.println("maxconns_fast: " + settingsStats.get("maxconns_fast"));
        System.out.println("lru_crawler: " + settingsStats.get("lru_crawler"));
        System.out.println("proxy_enabled: " + settingsStats.get("proxy_enabled"));
    }

    public static void main(String[] args) {
        spyMain();
        System.out.println();
        xMain();
    }

    public static void spyMain() {
        System.out.println("**************************** Fetch using the library 'net.spy.memcached' *****************************");
        MemcachedClient memcachedClient = null;
        try {
            ConnectionFactory connectionFactory = new DefaultConnectionFactory();
            if (memcachedAuthMethod.getValue() != null) {
                System.setProperty("net.spy.memcached.auth.AuthThreshold", "1");
                final AuthDescriptor ad = new AuthDescriptor(
                        new String[]{memcachedAuthMethod.getValue()},
                        new net.spy.memcached.auth.PlainCallbackHandler(memcachedAuthMethod.getUsername(), memcachedAuthMethod.getPassword())
                );
                connectionFactory = new ConnectionFactoryBuilder()
                        .setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
                        .setAuthDescriptor(ad)
                        .setAuthWaitTime(5000)
                        .build();
            }

            memcachedClient = new MemcachedClient(connectionFactory, Collections.singletonList(inetSocketAddress));

            MemcachedNode node = memcachedClient.getNodeLocator().getAll().stream().findFirst().orElse(null);
            if (node == null) {
                return;
            } else if (node instanceof TCPMemcachedNodeImpl && !node.isAuthenticated()) {
                return;
            } else if (!node.isActive()) {
                return;
            }

            Map<String, String> generalStats = memcachedClient.getStats().get(inetSocketAddress);
            Map<String, String> settingsStats = memcachedClient.getStats("settings").get(inetSocketAddress);

            showStatInfo(generalStats, settingsStats);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            System.out.println("Exception discovering Memcached object on " + e);
            throw new RuntimeException(e);
        } finally {
            if (memcachedClient != null) {
                memcachedClient.shutdown();
            }
        }
    }

    public static void xMain() {
        System.out.println("**************************** Fetch using the library 'Xmemcached' *****************************");
        XMemcachedClient memcachedClient = null;
        try {
            MemcachedClientBuilder builder = new XMemcachedClientBuilder(Collections.singletonList(inetSocketAddress));
            if (memcachedAuthMethod.getValue() != null) {
                System.setProperty("net.rubyeye.xmemcached.auth_max_attempts", "0");
                final AuthInfo ad = new AuthInfo(
                        new PlainCallbackHandler(memcachedAuthMethod.getUsername(), memcachedAuthMethod.getPassword()),
                        new String[]{memcachedAuthMethod.getValue()}
                );
                builder.addAuthInfo(inetSocketAddress, ad);
                builder.setCommandFactory(new BinaryCommandFactory());
                builder.setConnectTimeout(5000);
                builder.setOpTimeout(5000);
            }

            memcachedClient = (XMemcachedClient) builder.build();

            Session session = memcachedClient.getConnector().getSessionSet().stream().findFirst().orElse(null);
            if (session == null) {
                return;
            } else if (session instanceof MemcachedSession && ((MemcachedSession) session).isAuthFailed()) {
                return;
            } else if (session.isClosed()) {
                return;
            }

            Map<String, String> generalStats = memcachedClient.getStats().get(inetSocketAddress);
            Map<String, String> settingsStats = memcachedClient.getStatsByItem("settings").get(inetSocketAddress);

            showStatInfo(generalStats, settingsStats);
        } catch (InterruptedException | MemcachedException | TimeoutException | IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            System.out.println("Exception discovering Memcached object on " + e);
            throw new RuntimeException(e);
        } finally {
            if (memcachedClient != null) {
                try {
                    memcachedClient.shutdown();
                } catch (IOException ignored) {
                }
            }
        }
    }
}

and here is the console result:

**************************** Fetch using the library 'net.spy.memcached' *****************************
2024-01-07 14:56:57.784 INFO net.spy.memcached.MemcachedConnection:  Setting retryQueueSize to -1
2024-01-07 14:56:57.790 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=memcached.obj.com/20.150.30.16:11212, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2024-01-07 14:56:58.033 INFO net.spy.memcached.auth.AuthThread:  Authenticated to memcached.obj.com/20.150.30.16:11212
2024-01-07 14:56:58.037 INFO net.spy.memcached.MemcachedConnection:  Shut down memcached client
2024-01-07 14:56:58.037 WARN net.spy.memcached.auth.AuthThreadMonitor:  Connection shutdown in progress - interrupting waiting authentication thread.
2024-01-07 14:56:58.037 WARN net.spy.memcached.auth.AuthThread:  Authentication failed to memcached.obj.com/20.150.30.16:11212, Status: {OperationStatus success=false:  cancelled}
memcached.obj.com/20.150.30.16:11212
version: 1.6.22
pid: 1
accepting_conns: 1
tcpport: 11211
udpport: 0
evictions: on
item_size_max: 1048576
cas_enabled: yes
maxconns_fast: yes
lru_crawler: yes
proxy_enabled: null

**************************** Fetch using the library 'Xmemcached' *****************************
[main] INFO net.rubyeye.xmemcached.XMemcachedClient - XMemcachedClient is using Binary protocol
[main] INFO com.google.code.yanf4j.nio.impl.SelectorManager - Creating 8 reactors...
[main] INFO com.google.code.yanf4j.core.impl.AbstractController - The Controller started at localhost/127.0.0.1:0 ...
[Thread-12] ERROR net.rubyeye.xmemcached.auth.AuthTask - Authentication failed to memcached.obj.com/20.150.30.16:11212
[Thread-12] WARN net.rubyeye.xmemcached.auth.AuthTask - Reopen connection to memcached.obj.com/20.150.30.16:11212,beacause auth fail
[Xmemcached-Reactor-0] INFO com.google.code.yanf4j.core.impl.AbstractController - Add a session: 20.150.30.16:11212
[main] INFO com.google.code.yanf4j.core.impl.AbstractController - Controller has been stopped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant