{"id":2167,"date":"2018-02-13T15:35:09","date_gmt":"2018-02-13T06:35:09","guid":{"rendered":"https:\/\/www.codedojo.com\/?p=2167"},"modified":"2018-06-22T10:51:12","modified_gmt":"2018-06-22T01:51:12","slug":"how-to-get-your-unity-llapi-websocket-webgl-app-to-run-under-https-with-autossl-stunnel","status":"publish","type":"post","link":"https:\/\/www.codedojo.com\/?p=2167","title":{"rendered":"How to get your Unity LLAPI\/WebSocket WebGL app to run under https with AutoSSL &#038; stunnel"},"content":{"rendered":"<p><em>&lt;continuing my &#8220;blog about whatever random issue I last dealt with in the hopes that some poor soul with the same issue will google it one day&#8221; series&gt;<\/em><\/p>\n<p><a href=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi_secure.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2170\" src=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi_secure.jpg\" alt=\"\" width=\"422\" height=\"180\" srcset=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi_secure.jpg 422w, https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi_secure-300x128.jpg 300w\" sizes=\"auto, (max-width: 422px) 100vw, 422px\" \/><\/a><\/p>\n<h2>The problem<\/h2>\n<p>So you made your new Unity webGL game using the LLAPI and it works fine from a http:\/\/ address.\u00a0 But when you try with https, even with a valid https cert being installed, you get this error:<\/p>\n<p><a href=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi_ssl_error.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2169\" src=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi_ssl_error.jpg\" alt=\"\" width=\"543\" height=\"404\" srcset=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi_ssl_error.jpg 543w, https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi_ssl_error-300x223.jpg 300w\" sizes=\"auto, (max-width: 543px) 100vw, 543px\" \/><\/a><\/p>\n<p><strong>&#8220;Uncaught SecurityError: Failed to construct &#8216;WebSocket&#8217;: An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.&#8221;<\/strong><\/p>\n<p>This is your browser saying &#8220;Look, the website is https, but don&#8217;t let that fool you; it&#8217;s using a normal old web socket to send data under the hood which isn&#8217;t encrypted, so don&#8217;t trust this thing with your credit card numbers&#8221;.<\/p>\n<p>Unity (at the time of this writing) has no internal support for what we really need to be using:\u00a0 a Secure Web Socket.\u00a0 So where http has https, ws has wss.\u00a0 So how do we connect securely if our unity-based server binary can&#8217;t serve wss directly?<\/p>\n<h2>A little background info about CPanel &amp; AutoSSL<\/h2>\n<p><em>Note: I&#8217;m using CentOS 7 on a dedicated server with WHM\/CPanel<\/em><\/p>\n<p>Setting up your website for proper SSL so it can have that wonderful green padlock used to be a painful and sometimes expensive ordeal.<\/p>\n<p>But no longer!\u00a0 Enter the magic of CPanel&#8217;s AutoSSL.\u00a0 (I think it&#8217;s using Let&#8217;s Encrypt under the hood as a plugin?)\u00a0 Behind the scenes, it will handle domain validation and setup everything for you.\u00a0 While it does need to renew your cert every three months, it&#8217;s free and automatic.\u00a0 Add four new domains?\u00a0 They will all get valid certs within a day or so, it&#8217;s great.<\/p>\n<p>We can use this same cert to make your websockets secure as long as they are hosted at the same domain.<\/p>\n<h2>Setting up stunnel<\/h2>\n<p>This is an open source utility that is likely already included on your linux server box, if it isn&#8217;t, go install it with yum or something.<\/p>\n<p>It allows you to convert any socket into a secure socket.\u00a0 For example, if you have a telnet port at 1000, you could setup stunnel to listen at 1001 securely and relay all information back to 1000.<\/p>\n<p>The telnet connection has no idea what&#8217;s happening and sees no difference, but as long as the outside user can only access 1001, plain text information isn&#8217;t sent along the wire and one or both sides can be sure of the identity of who&#8217;s connecting.<\/p>\n<p>Depending on the stunnel settings, it might be setup like https where the client doesn&#8217;t have to have any certain keys (what we want here), or it could be like a ssh where the client DOES need a whitelisted key.<\/p>\n<p>A way to test a SSL port is to use OpenSSL from the command line on the host server via ssh.\u00a0 For example (keep in mind 443 is the standard https port your website is probably using):<\/p>\n<pre>&lt;at ssh prompt&gt; openssl s_client -connect localhost:443\r\n\r\n&lt;info snipped&gt;\r\nsubject=\/OU=Domain Control Validated\/OU=PositiveSSL\/CN=host.toolfish.com\r\nissuer=\/C=US\/ST=TX\/L=Houston\/O=cPanel, Inc.\/CN=cPanel, Inc. Certification Authority\r\n---\r\nNo client certificate CA names sent\r\nPeer signing digest: SHA512\r\nServer Temp Key: ECDH, P-256, 256 bits\r\n---\r\nSSL handshake has read 4946 bytes and written 415 bytes\r\n---\r\nNew, TLSv1\/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384\r\nServer public key is 2048 bit\r\nSecure Renegotiation IS supported\r\nCompression: NONE\r\nExpansion: NONE\r\nNo ALPN negotiated\r\nSSL-Session:\r\n Protocol : TLSv1.2\r\n&lt;info snipped&gt;\r\nStart Time: 1518495864\r\n Timeout : 300 (sec)\r\n Verify return code: 0 (ok)<\/pre>\n<p>Hitting enter after that will probably cause the website to an html error message because we didn&#8217;t send a valid request. That&#8217;s ok, it shows your website&#8217;s existing SSL stuff is working so we can move on.<\/p>\n<p>So first edit your \/etc\/stunnel\/stunnel.conf to something like this:<\/p>\n<pre>pid = \/etc\/stunnel\/stunnel.pid\r\n\r\n#we won't screw with changing this because we don't want to relocate\/change permissions on our files right now\r\n#setuid = nobody\r\n#setgid = nobody\r\n\r\nsslVersion = all\r\noptions = NO_SSLv2\r\n\r\n#for testing purposes.. these should be removed later:\r\noutput = \/etc\/stunnel\/log.txt\r\nforeground = yes\r\ndebug = 7\r\n\r\n[websitename1]\r\naccept = 29000\r\nconnect = 80\r\ncert = \/var\/cpanel\/ssl\/apache_tls\/oversi.io\/combined\r\n\r\n[websitename2]\r\naccept = 30000\r\nconnect = 20000\r\ncert = \/var\/cpanel\/ssl\/apache_tls\/oversi.io\/combined<\/pre>\n<p>Next, still from the ssh prompt, run stunnel by typing <strong>stunnel<\/strong>.<\/p>\n<p>Because we have foreground=yes set above it will run it in the shell, showing us all output directly, instead of in the background like it normally would. (Ctrl-C to cause stunnel to stop and quit)<\/p>\n<p>Look for any issues or errors it reports.\u00a0 The .conf file I listed aboveshows how to set it up for two or more tunnels at once, you likely only need one of those settings.<\/p>\n<p>The &#8220;websitename1&#8221; part doesn&#8217;t matter or have to match anything.<\/p>\n<p>The SSL cert is the most important setting.\u00a0 You need to give it your private &amp; public &amp; CA info in\u00a0 the same file.<\/p>\n<p>Now, initially, you might try to setup your keys using the files in ~\/ssl\/keys and ~\/ssl\/certs but they seem to not have everything all in one nice file including the CA certs.\u00a0 I figured out &#8216;bundled&#8217; ones already exist in a cpanel directory so I linked straight to them there.\u00a0 (replace oversi.io with your website name)<\/p>\n<p>If stuff worked, you should be able to test your SSL&#8217;ed port with OpenSSL again.\u00a0 In the example above under &#8220;websitename1&#8221; I told it to listen at\u00a029000 and send to port 80, for no good reason.<\/p>\n<p>So to test from a remote computer we can do:<\/p>\n<p><em> (you did open those ports in your firewall so outside people can connect, right?) <\/em><\/p>\n<pre>C:\\Users\\Seth&gt;openssl s_client -connect oversi.io:29000\r\nLoading 'screen' into random state - done\r\nCONNECTED(00000270)\r\ndepth=2 \/C=GB\/ST=Greater Manchester\/L=Salford\/O=COMODO CA Limited\/CN=COMODO RSA Certification Authority\r\nverify error:num=20:unable to get local issuer certificate\r\nverify return:0\r\n---\r\nCertificate chain\r\n 0 s:\/CN=oversi.io\r\n i:\/C=US\/ST=TX\/L=Houston\/O=cPanel, Inc.\/CN=cPanel, Inc. Certification Authority\r\n 1 s:\/C=US\/ST=TX\/L=Houston\/O=cPanel, Inc.\/CN=cPanel, Inc. Certification Authority\r\n i:\/C=GB\/ST=Greater Manchester\/L=Salford\/O=COMODO CA Limited\/CN=COMODO RSA Certification Authority\r\n 2 s:\/C=GB\/ST=Greater Manchester\/L=Salford\/O=COMODO CA Limited\/CN=COMODO RSA Certification Authority\r\n i:\/C=SE\/O=AddTrust AB\/OU=AddTrust External TTP Network\/CN=AddTrust External CA Root\r\n---\r\nServer certificate\r\n-----BEGIN CERTIFICATE-----\r\n&lt;snipped&gt;\r\n-----END CERTIFICATE-----\r\nsubject=\/CN=oversi.io\r\nissuer=\/C=US\/ST=TX\/L=Houston\/O=cPanel, Inc.\/CN=cPanel, Inc. Certification Authority\r\n---\r\nNo client certificate CA names sent\r\n---\r\nSSL handshake has read 5129 bytes and written 453 bytes\r\n---\r\nNew, TLSv1\/SSLv3, Cipher is DHE-RSA-AES256-SHA\r\nServer public key is 2048 bit\r\nSecure Renegotiation IS supported\r\nCompression: NONE\r\nExpansion: NONE\r\nSSL-Session:\r\n Protocol : TLSv1\r\n&lt;snipped&gt;\r\n Key-Arg : None\r\n Start Time: 1518497616\r\n Timeout : 300 (sec)\r\n Verify return code: 20 (unable to get local issuer certificate)\r\nread:errno=10093<\/pre>\n<p>Despite the errno=11093 and return code 20 errors, it&#8217;s working and properly sending our CA info (&#8220;cPanel, Inc. Certification Authority&#8221;).<\/p>\n<p>Or, easier, let&#8217;s just use the browser instead for this one since we&#8217;re connecting to port 80 if it works in this case:<\/p>\n<p>https:\/\/oversi.io:29000<\/p>\n<p><a href=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/ssl_redirect.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2168\" src=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/ssl_redirect.png\" alt=\"\" width=\"815\" height=\"569\" srcset=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/ssl_redirect.png 815w, https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/ssl_redirect-300x209.png 300w, https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/ssl_redirect-768x536.png 768w, https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/ssl_redirect-624x436.png 624w\" sizes=\"auto, (max-width: 815px) 100vw, 815px\" \/><\/a><\/p>\n<p>It worked, see the green padlock?\u00a0 Oh, ignore the error the website is sending, I assume that&#8217;s apache freaking out because the URL request is different from what it&#8217;s expecting (http vs https or the port difference?) so it can&#8217;t match up the virtual domain.<\/p>\n<p>From here, you should probably remove the debug options in the .conf (including the foreground=yes) and set it up to run automatically.\u00a0 I just placed &#8220;stunnel&#8221; in my\u00a0\/etc\/rc.d\/rc.local file. (this gets run at boot)<\/p>\n<h2>Actually connecting using the Unity LLAPI<\/h2>\n<p>Congratulations, everything is setup on the server and you&#8217;re sure your web socket port is listening and ready to go.<\/p>\n<p>While your server binary doesn&#8217;t need to change anything, your webgl client does.<\/p>\n<p>You now need to connect to WSS instead of WS.\u00a0 Example:<\/p>\n<pre>try\r\n {\r\n   _connectionID = NetworkTransport.Connect(_hostID, \"wss:\/\/oversi.io\", portNum, 0, out error);\r\n }\r\n catch (System.Exception ex)\r\n {\r\n   Debug.Log(\"RTNetworkClient.Connect&gt; \" + ex.Message);\r\n }<\/pre>\n<p>That&#8217;s pretty much it.\u00a0 If someone doesn&#8217;t care about https and decides to play over http, it still works fine. (internally the websocket code will still connect via wss)<\/p>\n<div style=\"width: 362px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-2167-1\" width=\"362\" height=\"312\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi.mp4?_=1\" \/><a href=\"https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi.mp4\">https:\/\/www.codedojo.com\/wp-content\/uploads\/2018\/02\/oversi.mp4<\/a><\/video><\/div>\n<p>If you want to see it in action, check out my webgl llapi multiplayer test project <a href=\"https:\/\/www.oversi.io\">https:\/\/www.oversi.io<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&lt;continuing my &#8220;blog about whatever random issue I last dealt with in the hopes that some poor soul with the same issue will google it one day&#8221; series&gt; The problem So you made your new Unity webGL game using the LLAPI and it works fine from a http:\/\/ address.\u00a0 But when you try with https, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,21],"tags":[],"class_list":["post-2167","post","type-post","status-publish","format-standard","hentry","category-tech-tips","category-unity"],"_links":{"self":[{"href":"https:\/\/www.codedojo.com\/index.php?rest_route=\/wp\/v2\/posts\/2167","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.codedojo.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.codedojo.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.codedojo.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.codedojo.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2167"}],"version-history":[{"count":11,"href":"https:\/\/www.codedojo.com\/index.php?rest_route=\/wp\/v2\/posts\/2167\/revisions"}],"predecessor-version":[{"id":2228,"href":"https:\/\/www.codedojo.com\/index.php?rest_route=\/wp\/v2\/posts\/2167\/revisions\/2228"}],"wp:attachment":[{"href":"https:\/\/www.codedojo.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2167"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.codedojo.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2167"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.codedojo.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2167"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}