Can't help you with AnyEvent, but i'm running my DIY webservers (private and commercial projects). So at least in that respect, i can give you a bit of information.

First of all, i'd avoid using self-signed certs, even during development. There are just too many cases where clients refuse to work (or work properly without additional workarounds) when not using a proper cert. I'd highly recommend buying a cheap domain and generating some free LetsEncrypt certificates.

If, for some reasons, you can't buy a domain, send me a private message on PerlMonks. I can get you set up for free with a hostname from one of my own domains (static IP or DynDNS, i support both) and a way to generate LE certs for your project.

I don't know exactly what your end goal is (simple network service, webserver, ...?), but depending on the complexity of your project/protocol, it might be a good design choice to separate out the TLS frontend and the protocol backend. My Net::Clacks uses an all-in-one approach, my webserver on the other hand has usually one TLS frontend running with multiple backends (connected through Unix Domain sockets), so i can properly support multiple domains with differentg backend software on a single IP.

OpenSSL Setup for "virtual hosts" (one IP, many domains) is a bit finicky. But basically, you start the connection with your default cert (must be valid), the client then tells the server "but i wanted domain foo.example.com" and you switch to that mid-flight. Take a look at the excerpt from my PageCamel WebFrontend.pm:

if($usessl) { my $defaultdomain = $self->{config}->{sslconfig}->{ssldefaultdomai +n}; my $encrypted; my $ok = 0; eval { $encrypted = IO::Socket::SSL->start_SSL($client, SSL_server => 1, SSL_key_file=> $self->{config}->{sslconfig}->{ssldomains} +->{$defaultdomain}->{sslkey}, SSL_cert_file=> $self->{config}->{sslconfig}->{ssldomains} +->{$defaultdomain}->{sslcert}, SSL_cipher_list => $self->{config}->{sslconfig}->{sslciphe +rs}, SSL_create_ctx_callback => sub { my $ctx = shift; #print STDERR "******************* CREATING NEW CONTEX +T ********************\n"; # Enable workarounds for broken clients Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_AL +L); # Disable session resumption completely Net::SSLeay::CTX_set_session_cache_mode($ctx, $SSL_SES +S_CACHE_OFF); # Disable session tickets Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_NO +_TICKET); # Load certificate chain my $defaultdomain = $self->{config}->{sslconfig}->{ssl +defaultdomain}; Net::SSLeay::CTX_use_certificate_chain_file($ctx, $sel +f->{config}->{sslconfig}->{ssldomains}->{$defaultdomain}->{sslcert}); # Check requested server name Net::SSLeay::CTX_set_tlsext_servername_callback($ctx, +sub { my $ssl = shift; my $h = Net::SSLeay::get_servername($ssl); if(!defined($h)) { #print STDERR "SSL: No Hostname given during S +SL setup\n"; return; } if(!defined($self->{config}->{sslconfig}->{ssldoma +ins}->{$h})) { #print STDERR "SSL: Hostname $h not configured +\n"; #print STDERR Dumper($self->{config}->{sslconf +ig}->{ssldomains}); return; } if(defined($self->{config}->{sslconfig}->{ssldomai +ns}->{$h}->{internal_socket})) { # This SSL connection uses a different backend $selectedbackend = $self->{config}->{sslconfig +}->{ssldomains}->{$h}->{internal_socket}; } if($h eq $self->{config}->{sslconfig}->{ssldefault +domain}) { # Already the correct CTX setting, just return return; } #print STDERR "§§§§§§§§§§§§§§§§§§§§§§§ Requested + Hostname: $h §§§\n"; my $newctx; if(defined($self->{config}->{sslconfig}->{ssldomai +ns}->{$h}->{ctx})) { $newctx = $self->{config}->{sslconfig}->{ssldo +mains}->{$h}->{ctx}; } else { $newctx = Net::SSLeay::CTX_new or croak("Can't + create new SSL CTX"); Net::SSLeay::CTX_set_cipher_list($newctx, $sel +f->{config}->{sslconfig}->{sslciphers}); Net::SSLeay::set_cert_and_key($newctx, $self-> +{config}->{sslconfig}->{ssldomains}->{$h}->{sslcert}, $self->{co +nfig}->{sslconfig}->{ssldomains}->{$h}->{sslkey}) or croak("Can't set cert and key file" +); Net::SSLeay::CTX_use_certificate_chain_file($n +ewctx, $self->{config}->{sslconfig}->{ssldomains}->{$h}->{sslcert}); #print STDERR "Cert: ", $self->{config}->{sslc +onfig}->{ssldomains}->{$h}->{sslcert}, " Key: ", $self->{config}->{ss +lconfig}->{ssldomains}->{$h}->{sslkey}, "\n"; $self->{config}->{sslconfig}->{ssldomains}->{$ +h}->{ctx} = $newctx; } Net::SSLeay::set_SSL_CTX($ssl, $newctx); }); # Prepared/tested for future ALPN needs (e.g. HTTP/ +2) ## Advertise supported HTTP versions #Net::SSLeay::CTX_set_alpn_select_cb($ctx, ['http/1.1' +, 'http/2.0']); }, ); $ok = 1; }; if(!$ok) { print "EVAL ERROR: ", $EVAL_ERROR, "\n"; $self->endprogram(); } elsif(!$ok || !defined($encrypted) || !$encrypted) { print "startSSL failed: ", $SSL_ERROR, "\n"; $self->endprogram(); } }

Basically, we start with an unencrypted connection. If that port is configured to $usessl, we use IO::Socket::SSL->start_SSL to switch over to an OpenSSL connection. We also register a callback with SSL_create_ctx_callback to make sure the SSL context is set up with the options we want to make it relatively secure without too much performance problems:

# Enable workarounds for broken clients Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_AL +L); # Disable session resumption completely Net::SSLeay::CTX_set_session_cache_mode($ctx, $SSL_SES +S_CACHE_OFF); # Disable session tickets Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_NO +_TICKET);

And we also set up the default certificate chain. We need this, because most modern certs, including LetsEncrypt, require an intermediate certificate:

Net::SSLeay::CTX_use_certificate_chain_file($ctx, $self->{config}->{sslconfig}->{ssldomains}->{$defaultdomain}->{sslcert});

The real magic (virtual hosting) happens in the CTX_set_tlsext_servername_callback callback:

Net::SSLeay::CTX_set_tlsext_servername_callback($ctx, +sub { my $ssl = shift; my $h = Net::SSLeay::get_servername($ssl); if(!defined($h)) { #print STDERR "SSL: No Hostname given during S +SL setup\n"; return; } ... # ---- THIS SELECTS THE CORRECT BACKEND ----- if(defined($self->{config}->{sslconfig}->{ssldomai +ns}->{$h}->{internal_socket})) { # This SSL connection uses a different backend $selectedbackend = $self->{config}->{sslconfig +}->{ssldomains}->{$h}->{internal_socket}; } if($h eq $self->{config}->{sslconfig}->{ssldefault +domain}) { # Already the correct CTX setting, just return return; } ... # Switch over to the new certificate key and c +hain $newctx = Net::SSLeay::CTX_new or croak("Can't + create new SSL CTX"); Net::SSLeay::CTX_set_cipher_list($newctx, $sel +f->{config}->{sslconfig}->{sslciphers}); Net::SSLeay::set_cert_and_key($newctx, $self-> +{config}->{sslconfig}->{ssldomains}->{$h}->{sslcert}, $self->{co +nfig}->{sslconfig}->{ssldomains}->{$h}->{sslkey}) or croak("Can't set cert and key file" +); Net::SSLeay::CTX_use_certificate_chain_file($n +ewctx, $self->{config}->{sslconfig}->{ssldomains}->{$h}->{sslcert}); #print STDERR "Cert: ", $self->{config}->{sslc +onfig}->{ssldomains}->{$h}->{sslcert}, " Key: ", $self->{config}->{ss +lconfig}->{ssldomains}->{$h}->{sslkey}, "\n"; $self->{config}->{sslconfig}->{ssldomains}->{$ +h}->{ctx} = $newctx; } Net::SSLeay::set_SSL_CTX($ssl, $newctx); }); ... },

Another thing to keep in mind when using non-blocking sockets (which is often a good idea) is that when writing to a socket, it may only partially succeed. Plus, you have to make sure to UTF-8 encode any wide characters. You can take a look at my WebPrint.pm helper. It's far from perfect, but so far it has worked reasonably well.

As for the "right way" of doing network stuff: There is no perfectly right way. It really depends a lot on your project requirements and the complexity of what you are trying to achieve. For simple stuff with a couple of clients, any old event loop thingy will do. When you get into complex stuff with virtual hosting plus a dozen backends, serving thousands of simultaneous connections... you might or might not have to roll your own custom event loop, just to make sure it only does the stuff you need, and in the most optimized order for your project.

You might also have to look into stuff like forking, pre-forking and all that jazz, again, depending on your project. When doing the forking stuff, it might be benefitial to load up all data you need in the application BEFORE forking to take full advantage of copy-on-write and stuff. And, oh, don't forget to re-initialize your random number generator after forking for security reasons.

I know that this post isn't exactly the answer you are probably looking for. But i thought i might post it anyway, it could give some tips on how to use the SSL stuff without having to read the (extremely confusing) documentation of Net::SSLeay and the openssl library.

PerlMonks XP is useless? Not anymore: XPD - Do more with your PerlMonks XP

In reply to Re: Using AnyEvent to create a TLS server by cavac
in thread Using AnyEvent to create a TLS server by Bodger

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.