1

でクライアントを検証することを選択できることはわかっていますが--ssl-verify、使用するCAチェーンを指定するにはどうすればよいですか?私は(curl--cacertやWEBrickのよう:SSLCACertificateFileに)ファイルを提供することに慣れているので、ファイルを用意しましたが、ファイルをに渡す方法に関するドキュメントが見つからないようですthin

4

1 に答える 1

4

簡単な答え:できません。

長い答え:可能ですが、SSL接続を構築するEventMachineのC ++拡張機能を更新し、認証局ファイルを渡すためにEventMachineとThinを介して呼び出しスタックを更新する必要があります。

私がこれを見つけた方法:ソースコード!すべてgithubにあります

  • thinのコマンドラインoptsはで解析されますthin:lib/thin/runner.rb

    opts.separator "SSL options:"
    
    opts.on(      "--ssl", "Enables SSL")                                           { @options[:ssl] = true }
    opts.on(      "--ssl-key-file PATH", "Path to private key")                     { |path| @options[:ssl_key_file] = path }
    opts.on(      "--ssl-cert-file PATH", "Path to certificate")                    { |path| @options[:ssl_cert_file] = path }
    opts.on(      "--ssl-verify", "Enables SSL certificate verification")           { @options[:ssl_verify] = true }
    
  • その後、コントローラーを作成するために使用されます

    controller = case
    when cluster? then Controllers::Cluster.new(@options)
    when service? then Controllers::Service.new(@options)
    else               Controllers::Controller.new(@options)
    end
    
  • sslではthin:lib/controllers/controller.rb、オプションがプルバックされ、サーバーオブジェクトとともに保存されます。

    # ssl support
    if @options[:ssl]
      server.ssl = true
      server.ssl_options = { :private_key_file => @options[:ssl_key_file], :cert_chain_file => @options[:ssl_cert_file], :verify_peer => @options[:ssl_verify] }
    end
    
  • そして最後にクライアントへの接続を初期化するために使用されます

    def initialize_connection(connection)
      connection.backend                 = self
      connection.app                     = @server.app
      connection.comm_inactivity_timeout = @timeout
      connection.threaded                = @threaded
    
      if @ssl
        connection.start_tls(@ssl_options)
      end
    
  • この接続は、EventMachine::Connectionで定義されていeventmachine:lib/em/connection.rbます。 EventMachine::Connection#start_tlsパラメータをに渡しますEventMachine::set_tls_parms

    def start_tls args={}
      priv_key, cert_chain, verify_peer = args.values_at(:private_key_file, :cert_chain_file, :verify_peer)
    
      [priv_key, cert_chain].each do |file|
        next if file.nil? or file.empty?
        raise FileNotFoundException,
        "Could not find #{file} for start_tls" unless File.exists? file
      end 
    
      EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer)
      EventMachine::start_tls @signature
    end 
    
  • EventMachine::set_tls_parmsはC++拡張機能の一部でありeventmachine:ext/rubymain.cpp、5つの引数のC関数として定義されています。t_set_tls_parms

    rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 4);
    
  • またt_set_tls_parms、同じファイルの他の場所で定義されている場合は、sslオプションをに渡すだけevma_set_tls_parmsです。

    static VALUE t_set_tls_parms (VALUE self, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer)
    {
      /* set_tls_parms takes a series of positional arguments for specifying such things
       * as private keys and certificate chains.
       * It's expected that the parameter list will grow as we add more supported features.
       * ALL of these parameters are optional, and can be specified as empty or NULL strings.
       */
      evma_set_tls_parms (NUM2ULONG (signature), StringValuePtr (privkeyfile), StringValuePtr (certchainfile), (verify_peer == Qtrue ? 1 : 0));
      return Qnil;
    }
    
  • バニラC関数evma_set_tls_parmsはで定義されていeventmachine:ext/cmain.cppます。sslオプションをEventableDescriptorSetTlsParmsメソッドに渡します。

    extern "C" void evma_set_tls_parms (const unsigned long binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer)
    {
      ensure_eventmachine("evma_set_tls_parms");
      EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
      if (ed)
        ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false));
    } 
    
  • そのSetTlsParmsインスタンスメソッドはで定義されておりeventmachine:ed.cpp、実際に行われるのは、いくつかのインスタンス変数にsslオプションをキャッシュすることだけです。

    void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer)
    {
      #ifdef WITH_SSL
      if (SslBox)
        throw std::runtime_error ("call SetTlsParms before calling StartTls");
      if (privkey_filename && *privkey_filename)
        PrivateKeyFilename = privkey_filename;
      if (certchain_filename && *certchain_filename)
        CertChainFilename = certchain_filename;
      bSslVerifyPeer = verify_peer;
      #endif
    
      #ifdef WITHOUT_SSL
      throw std::runtime_error ("Encryption not available on this event-machine");
      #endif
    }
    
  • これらのインスタンス変数は、後でStartTlsインスタンスメソッド(同じファイルで定義されている)で使用され、新しいインスタンスを初期化するために渡されますSslBox_t

    void ConnectionDescriptor::StartTls()
    {
      #ifdef WITH_SSL
      if (SslBox)
        throw std::runtime_error ("SSL/TLS already running on connection");
    
      SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, GetBinding());
      _DispatchCiphertext();
      #endif
    
  • SslBox_tコンストラクターはで定義され、eventmachine:ext/ssl.cppsslオプションを使用して新しいを初期化しますSslContext_t

    SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding):
      bIsServer (is_server),
      bHandshakeCompleted (false),
      bVerifyPeer (verify_peer),
      pSSL (NULL),
      pbioRead (NULL),
      pbioWrite (NULL)
    {
      /* TODO someday: make it possible to re-use SSL contexts so we don't have to create
       * a new one every time we come here.
       */
    
      Context = new SslContext_t (bIsServer, privkeyfile, certchainfile);
      assert (Context);
    
  • コンストラクターは、SslContext_t標準のOpenSSLCバインディングでこれらのオプションを使用する同じファイルで定義されます。

    // The SSL_CTX calls here do NOT allocate memory.
    int e;
    if (privkeyfile.length() > 0)
      e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
    else
      e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey);
    if (e <= 0) ERR_print_errors_fp(stderr);
    assert (e > 0);
    
    if (certchainfile.length() > 0)
      e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
    else
      e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
    if (e <= 0) ERR_print_errors_fp(stderr);
    assert (e > 0);
    

これで、sslオプションの使用方法がわかりました。コールチェーンを変更して、CAファイル名を残りの部分とともにこの時点まで渡すように変更した場合、たとえば、としてconst string &certauthfile、さらに2、3回のOpenSSL呼び出しを使用して、典拠ファイルを追加できます。

if (certauthfile.length() > 0)
  e = SSL_CTX_load_verify_locations(pCtx, certauthfile.c_str(), NULL);
else
  ;// no default necessary
if (e <= 0) ERR_print_errors_fp(stderr);
assert (e > 0);

これを行うためのパッチを提出することは、十分な意欲のある人のための演習として残されています。

于 2013-01-23T15:39:05.410 に答える