Netty-如何应对DDOS攻击?

5
我正在使用Netty 4.1作为MMORPG游戏的NIO套接字服务器。多年来,它一直运行得很完美,但最近我们遭受了DDOS攻击。我已经长时间地与之斗争,但目前我没有更多的想法来改进它。攻击者正在使用来自世界各地成千上万个IP的新连接进行垃圾邮件轰炸。由于攻击看起来非常类似于正常玩家,因此在网络层面上很难切断它。与HTTP服务器遭受的攻击相比,这些攻击并不是很大,但足以使我们的游戏崩溃。

我如何使用Netty:

public void startServer() {

    bossGroup = new NioEventLoopGroup(1);
    workerGroup = new NioEventLoopGroup();

    try {
        int timeout = (Settings.SOCKET_TIMEOUT*1000);
        bootstrap = new ServerBootstrap();

        int bufferSize = 65536;
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childOption(ChannelOption.SO_TIMEOUT, timeout)
                .childOption(ChannelOption.SO_RCVBUF, bufferSize)
                .childOption(ChannelOption.SO_SNDBUF, bufferSize)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new CustomInitalizer(sslCtx));


        ChannelFuture bind = bootstrap.bind(DrServerAdmin.port);
        bossChannel = bind.sync();

    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

初始化器:

public class CustomInitalizer extends ChannelInitializer<SocketChannel> {

    public static  DefaultEventExecutorGroup normalGroup = new DefaultEventExecutorGroup(16);
    public static  DefaultEventExecutorGroup loginGroup = new DefaultEventExecutorGroup(8);
    public static  DefaultEventExecutorGroup commandsGroup = new DefaultEventExecutorGroup(4);

    private final SslContext sslCtx;

    public CustomInitalizer(SslContext sslCtx) {
        this.sslCtx = sslCtx;
    }

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        if (sslCtx != null) {
            pipeline.addLast(sslCtx.newHandler(ch.alloc()));
        }

        pipeline.addLast(new CustomFirewall()); //it is AbstractRemoteAddressFilter<InetSocketAddress>
        int limit = 32768;        
        pipeline.addLast(new DelimiterBasedFrameDecoder(limit, Delimiters.nulDelimiter()));
        pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));

        pipeline.addLast(new CustomReadTimeoutHandler(Settings.SOCKET_TIMEOUT));

        int id = DrServerNetty.getDrServer().getIdClient();
        CustomHandler normalHandler = new CustomHandler();
        FlashClientNetty client = new FlashClientNetty(normalHandler,id);
        normalHandler.setClient(client);

        pipeline.addLast(normalGroup,"normalHandler",normalHandler);

        CustomLoginHandler loginHandler = new CustomLoginHandler(client);
        pipeline.addLast(loginGroup,"loginHandler",loginHandler);


        CustomCommandsHandler commandsHandler = new CustomCommandsHandler(loginHandler.client);
        pipeline.addLast(commandsGroup, "commandsHandler", commandsHandler);

    }
}

我正在使用5个组:
  • 引导 bossGroup - 用于新连接
  • 引导 workerGroup - 用于传递消息
  • normalGroup - 用于大多数消息
  • loginGroup - 用于繁重的登录过程
  • commands group - 用于某些繁重的逻辑
我会监控新连接和消息的数量,以便立即发现是否存在攻击。在攻击期间,我将不再接受新连接:在自定义防火墙中( AbstractRemoteAddressFilter )返回 false。
protected boolean accept(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) throws Exception {
    if(ddosDetected())
       return false;
    else
        return true;
}

尽管我立即断开了新连接,我的工作组仍然过载。工作组的PendingTasks(其他所有组都正常)正在增加,这导致普通玩家之间的通信越来越长,并最终被socket_timeouts踢出。我不确定为什么会发生这种情况。在正常服务器使用期间,最繁忙的组是登录和普通组。在网络层面上,服务器很好-它只使用了其带宽限制的约10%。攻击期间,CPU和RAM使用率也不是很高。但经过几分钟的这样的攻击后,所有玩家都将从游戏中踢出并无法再次连接。

有没有更好的方法可以立即断开所有传入连接并保护已连接的用户?


1
有点不相关,但也许你应该考虑使用外部服务,例如CloudFlare,根据DDOS的规模来决定。 - Karol Dowbecki
上次我研究这个主题时,我找不到任何支持TCP保护的服务。但现在我可以看到Cloudflare有类似的东西:https://www.cloudflare.com/products/cloudflare-spectrum/。谢谢,我会去查一下的。 - drygu
1个回答

1

我认为你需要通过例如iptables在内核级别上“修复”这个问题。否则,在接受连接后才能关闭连接,这在这种情况下听起来还不够好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接