Swift-NIO защищенный сервер веб-сокетов

Я пытаюсь создать сервер и клиент websocket в своем приложении для iOS, что мне успешно удалось сделать с помощью примера реализации здесь. ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer) - поэтому в текущей рабочей ситуации я запускаю сервер websocket при запуске приложения, а затем загружаю клиент в веб-представление, которое может подключиться к этому.

Теперь моя проблема в том, что я хочу, чтобы мой сервер был защищенным сервером веб-сокетов (в основном, подключение к серверу веб-сокетов со страницы HTTPS html)

Я новичок в сетевом программировании, и документации Swift-nio по меньшей мере не хватает. Насколько я понимаю, я мог бы использовать ( https://github.com/apple/swift-nio-transport-services)

Я нашел эту ветку, которая именно то, что мне нужно - https://github.com/apple/swift-nio-transport-services/issues/39 - я мог бы отключить аутентификацию TLS, так как мне все равно, в моем случае использования, пока я можно подключить веб-сокет.

Поэтому мой вопрос заключается в том, как расширить свой клиент ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketClient) и сервер ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer) для использования swift-nio-transport-service.

Я мог бы добавить NIOSSLContext и прочее, но я думаю, что мне нужно добавить EventLoopGroup и новый bootstrap методы. Я знаю, что ответы прямо здесь.... но я просто не могу точно определить это.

Любой указатель будет оценен.

Благодарю.

1 ответ

Чтобы перевести простой NIO Сервер к NIOTransportServices Во-первых, вам необходимо внести следующие изменения:

  1. Добавить зависимость от NIOTransportServices на ваш сервер.
  2. + Изменить MultiThreadedEventLoopGroup в NIOTSEventLoopGroup,
  3. + Изменить ClientBootstrap в NIOTSConnectionBootstrap,
  4. + Изменить ServerBootstrap в NIOTSListenerBootstrap,
  5. Создайте и запустите свой код.

Несколько ChannelOptionне работают в NIOTransportServicesНо большинство так и делает: самый простой способ подтвердить, что все работает правильно, - это быстро проверить общий поток.

Это не добавляет никаких дополнительных функциональных возможностей к вашему приложению, но дает те же функциональные возможности с использованием API-интерфейсов iOS.

Чтобы добавить TLS либо NIOTSConnectionBootstrap или же NIOTSListenerBootstrapВы используете .tlsOptions функция. Например:

NIOTSListenerBootstrap(group: group)
    .tlsOptions(myTLSOptions())

Конфигурирование NWProtocolTLS.Options это довольно сложная вещь. Вам необходимо получить SecIdentity, который требует взаимодействия с цепочкой для ключей. Куинн несколько обсуждал это здесь.

Когда у вас есть SecIdentity, вы можете использовать его так:

func myTLSOptions() -> NWProtocolTLS.Options {
    let options = NWProtocolTLS.Options()
    let yourSecIdentity = // you have to implement something here
    sec_protocol_options_set_local_identity(options.securityProtocolOptions, sec_identity_create(yourSecIdentity)
    return options
}

Как только вы написали этот код, все должно пройти гладко!


В качестве расширения, если вы хотите защитить сервер NIO в Linux, вы можете сделать это с помощью swift-nio-ssl. Это имеет отдельную конфигурацию, так как API цепочки для ключей недоступны, и поэтому вы делаете гораздо больше загрузки ключей и сертификатов из файлов.

Мне нужен был безопасный веб-сокет без использования SecIdentity или же NIOTransportServices, поэтому на основе намека @Lukasa на swift-nio-ssl Я собрал пример, который, похоже, работает правильно.

Я не знаю, правильно ли это, но я помещаю это здесь на случай, если кому-то еще может быть полезно. Обработка ошибок и прерывание, когда tryОшибка не указана для краткости.

let configuration = TLSConfiguration.forServer(certificateChain: try! NIOSSLCertificate.fromPEMFile("/path/to/your/tlsCert.pem").map { .certificate($0) }, privateKey: .file("/path/to/your/tlsKey.pem"))
let sslContext = try! NIOSSLContext(configuration: configuration)
            
let upgradePipelineHandler: (Channel, HTTPRequestHead) -> EventLoopFuture<Void> = { channel, req in
                
    WebSocket.server(on: channel) { ws in

       ws.send("You have connected to WebSocket")

       ws.onText { ws, string in
           print("Received text: \(string)")
       }
                
       ws.onBinary { ws, buffer in
           // We don't accept any Binary data
       }
                
       ws.onClose.whenSuccess { value in
           print("onClose")
       }
   }
}

self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2)
let port: Int = 5759
let promise = self.eventLoopGroup!.next().makePromise(of: String.self)
            
_ = try? ServerBootstrap(group: self.eventLoopGroup!)
                
    // Specify backlog and enable SO_REUSEADDR for the server itself
    .serverChannelOption(ChannelOptions.backlog, value: 256)
    .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
                
    .childChannelInitializer { channel in
                    
        let handler = NIOSSLServerHandler(context: sslContext)
        _ = channel.pipeline.addHandler(handler)
              
        let webSocket = NIOWebSocketServerUpgrader(
            shouldUpgrade: { channel, req in
                return channel.eventLoop.makeSucceededFuture([:])
            },
            upgradePipelineHandler: upgradePipelineHandler
        )
              
        return channel.pipeline.configureHTTPServerPipeline(
            withServerUpgrade: (
                upgraders: [webSocket],
                completionHandler: { ctx in
                     // complete
                })
        )
    }.bind(host: "0.0.0.0", port: port).wait()

_ = try! promise.futureResult.wait()
try! server.close(mode: .all).wait()
Другие вопросы по тегам