Нетти - как выжить в DDOS? - PullRequest
       35

Нетти - как выжить в DDOS?

4 голосов
/ 06 марта 2019

Я использую netty 4.1 в качестве сервера сокетов NIO для игры MMORPG.Он работал отлично в течение многих лет, но в последнее время мы страдаем от DDOS-атак.Я боролся с этим долгое время, но в настоящее время у меня нет больше идей о том, как я могу улучшить это.Ddoser спамит новыми подключениями от тысяч ips со всего мира.Это трудно сократить на сетевом уровне, потому что атаки выглядят очень похоже на обычных игроков.Атаки не очень велики по сравнению с атаками на 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 групп:

  • bootstrap bossGroup - для новых подключений
  • bootstrap workerGroup - для доставки сообщений
  • normalGroup - для большинства сообщений
  • loginGroup - для интенсивного процесса входа в систему
  • группа команд - для некоторой сложной логики

Я отслеживаю количество новых подключений и сообщений, чтобы сразу узнать, есть лиидет атака.Во время атаки я больше не принимаю новые подключения: я возвращаю false в пользовательском брандмауэре (AbstractRemoteAddressFilter).

protected boolean accept(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) throws Exception {
    if(ddosDetected())
       return false;
    else
        return true;
}

Но даже из-за того, что я сбрасываю новые соединения, моя рабочая группа перегружена.PendingTasks для рабочей группы (все остальные группы в порядке) растут, что приводит к более длительной и продолжительной связи для нормальных игроков и, наконец, они получают удовольствие от socket_timeouts.Я не уверен, почему это случилось.При обычном использовании сервера самыми загруженными группами являются логин и обычная группа.На сетевом уровне сервер в порядке - он использует только ~ 10% от своей пропускной способности.Использование ЦП и ОЗУ также не очень высоко во время атаки.Но после нескольких минут такой атаки все мои игроки вышвырнуты из игры и больше не могут подключаться.

Есть ли лучший способ мгновенно отбросить все входящие соединения и защитить пользователей, которые являются постоянными?связано?

1 Ответ

1 голос
/ 07 марта 2019

Я думаю, вам нужно будет "исправить это" на уровне ядра, например, через iptables. В противном случае вы можете закрыть соединение только после того, как уже приняли его, что в данном случае звучит недостаточно хорошо.

...