Jekyll2020-02-01T22:49:03-08:00https://timothybasanov.com/feed.xmlTimothy BasanovTimothy's projects and instructions for tricky edge cases that I encountered either at home or at work
TimothySSL server certificate via Keychain Access in macOS Catalina2020-01-20T00:00:00-08:002020-01-20T00:00:00-08:00https://timothybasanov.com/2020/01/20/keychain-access-catalina-ssl-server<p>If you ever tried to create a CA and sign an SSL server certificate using
macOS Keychain Access you’d be surprised to find that resulting certificate
is not a valid SSL certificate anymore due to new
<a href="https://support.apple.com/en-us/HT210176">requirements in iOS 13 and macOS 10.15</a>.</p>
<!--more-->
<h1 id="issuing-a-ca-certificate">Issuing a CA Certificate</h1>
<ul>
<li>Open <em>Keychain Access, Certificate Assistant, Create a Certificate Authority…</em></li>
<li>Update <em>Name</em></li>
<li>Change <em>User Certificate</em> to <em>SSL Server</em> and do not override defaults</li>
</ul>
<blockquote>
<p>You may need to clean up
<code class="language-plaintext highlighter-rouge">~/Library/Application\ Support/Certificate\ Authority</code> as macOS expects
generated CAs to have unique names.</p>
</blockquote>
<h2 id="overriding-defaults">Overriding defaults</h2>
<p>It’s not recommended to override defaults, it’s way too easy to create a CA
that macOS would not like. But in case you want to do it anyway:</p>
<blockquote>
<p>Note: While this set of settings works, it’s unclear which parts of this
instruction are important and which are not. Try on your own peril.</p>
</blockquote>
<ul>
<li><em>Certificate Information</em>
<ul>
<li><em>Validity Period</em> has to be ≤824</li>
<li><em>Sign your invitation</em> should not be used</li>
<li>You can set any custom <em>Name (Common Name)</em> or other fields there</li>
</ul>
</li>
<li><em>Key Usage Extension For This CA</em>
<ul>
<li>Check <em>This extension is critical</em></li>
<li>Check <em>Key Encipherment</em></li>
</ul>
</li>
<li><em>Key Usage Extension For Users of This CA</em>
<ul>
<li>Uncheck</li>
</ul>
</li>
<li><em>Extended Key Usage Extension For This CA</em>
<ul>
<li>Check <em>This extension is critical</em></li>
<li>Check <em>SSL Server Authentication</em></li>
</ul>
</li>
<li><em>Extended Key Usage Extension For Users of This CA</em>
<ul>
<li>Uncheck</li>
</ul>
</li>
<li><em>Basic Constraints Extension For This CA</em>
<ul>
<li>Check <em>Use this certificate as a certificate authority</em></li>
</ul>
</li>
<li><em>Subject Alternate Name Extension For This CA</em>
<ul>
<li>Uncheck</li>
</ul>
</li>
<li><em>Subject Alternate Name For Users of This CA</em>
<ul>
<li>Uncheck</li>
</ul>
</li>
<li><em>Specify a Location for The Certificate</em>
<ul>
<li><em>Keychain</em> should be <em>local</em></li>
<li>Check <em>Trust certificates signed by this CA</em></li>
</ul>
</li>
</ul>
<h2 id="double-check-ca-trust">Double-check CA trust</h2>
<p>If your newly created certificate is not trusted (blue plus icon),
you need to open it and set <em>Trust | When using this</em> to <em>Always Trust</em>.</p>
<h1 id="issuing-an-ssl-certificate">Issuing an SSL Certificate</h1>
<ul>
<li>Open <em>Keychain Access, Certificate Assistant, Create a Certificate…</em></li>
<li>Update <em>Name</em></li>
<li>Change <em>Identity Type</em> to <em>Leaf</em></li>
<li>Change <em>Certificate Type</em> to <em>SSL Server</em> and click <em>Override defaults</em></li>
</ul>
<h2 id="overriding-defaults-1">Overriding defaults</h2>
<p>You <em>have</em> to override defaults to make this certificate compatible with macOS
Catalina.</p>
<blockquote>
<p>Note: While this set of settings works, it’s unclear which parts of this
instruction are important and which are not. Try on your own peril.</p>
</blockquote>
<ol>
<li><em>Certificate Information</em>
<ul>
<li><em>Validity Period</em> can not be ≥825</li>
<li>You can set any custom <em>Name (Common Name)</em> or other fields there</li>
</ul>
</li>
<li><em>Choose An Issuer</em>
<ul>
<li>Pick newly created CA (should be the default one)</li>
</ul>
</li>
<li><em>Key Usage Extension</em>
<ul>
<li>You can uncheck <em>Include Key Usage Extension</em>, but it’s not required</li>
</ul>
</li>
<li><em>Extended Key Usage Extension</em>
<ul>
<li>Check <em>Include Key Usage Extension</em></li>
<li>You can uncheck <em>This extension is critical</em>, but it’s not required</li>
<li>Don’t check any other <em>Capabilities</em> except <em>SSL Server Authentication</em></li>
</ul>
</li>
<li><em>Basic Constraints Extension</em>
<ul>
<li>Leave unchecked</li>
</ul>
</li>
<li><em>Subject Altenate Name Extension</em>
<ul>
<li>You can uncheck <em>This extension is critical</em>, but it’s not required</li>
<li>Input your DNS names, may include <code class="language-plaintext highlighter-rouge">*</code> at the beginning for wild cards
and can have many domains comma-separated. This is a required field.</li>
<li>Do not use <em>iPAddress</em> field</li>
</ul>
</li>
<li><em>Keychain</em>
<ul>
<li><em>login</em></li>
</ul>
</li>
</ol>TimothyIf you ever tried to create a CA and sign an SSL server certificate using macOS Keychain Access you’d be surprised to find that resulting certificate is not a valid SSL certificate anymore due to new requirements in iOS 13 and macOS 10.15.Opening ports 80 and 443 on a Synology NAS via UI2018-12-10T00:00:00-08:002018-12-10T00:00:00-08:00https://timothybasanov.com/2018/12/10/synology-nas-port-80-443<p>Synology NAS’s OS DSM 6.x does not allow one to directly use
ports 80 and 443. There are several different articles
flowing around that try to cover it, but they often
require SSH access to a box and may not be compatible
with future versions of DSM.
Turns out there is “one weird trick” to make it all work
via built-in Nginx server relying on “Host” headers from HTTP requests.</p>
<p>Verified on DS718+ DSM 5.2.1 with Ubiquity mFI controller
and Homebridge (for HomeKit) running within Docker containers on Synology.
As a side effect now I can use DSM without ugly port numbers (e.g. :5001)
via a redirection below.</p>
<!--more-->
<blockquote>
<p>I did not have Web Station installed and I did not have Synology-managed
Apache/PHP servers. I have a hope that this would work for most other
Synology setups, but I have not tried it myself. Redirecting Synology’s
own host to a different port (:5000) may break applications like Photo Station,
but I do not use them and have not verified if they were broken.
I have only tried iOS <em>DS Finder</em> app and it worked.</p>
</blockquote>
<h2 id="synology-nas-ports--applications">Synology NAS Ports & Applications</h2>
<p>These are main ports of interest:</p>
<ul>
<li>80: Runs Synology-controlled Nginx to redirect HTTP users to :5000</li>
<li>443: Same Nginx to redirect HTTPS users to :5001</li>
<li>5000: Main Synology DSM UI on HTTP</li>
<li>5001: Main Synology DSM UI on HTTPS</li>
</ul>
<blockquote>
<p>There is a lot of things going on with :80 and :443 on Synology.
Some “applications” (e.g. photo station and friends) may use this port
instead of :5000 or :5001. I’ve tried to reconfigure it via SSH
and Nginx config file, but there
are many auto-generated config files so it’s too fragile for my taste.</p>
</blockquote>
<blockquote>
<p>If you noticed that you’re still being redirected to port :5001, check
that you disabled <em>Automatically redirect HTTP connections to HTTPS
_Control Panel | Network | DSM Settings</em>. While you’re connecting to
port :443, Nginx proxies your connection to :5000, which may force DSM to send
you a 302 redirect to :5001.</p>
</blockquote>
<h3 id="using-reverse-proxy-to-control-nginx-on-80443">Using Reverse Proxy to Control Nginx on :80/:443</h3>
<ul>
<li>Open <em>Control Panel | Application Portal | Reverse Proxy</em></li>
<li>Create a new proxy rule</li>
<li>Set <em>General</em> options:
<ul>
<li>Source Protocol: <em>HTTPS</em></li>
<li>Source Hostname: <code class="language-plaintext highlighter-rouge">your-synology-hostname-you-use-in-a-browser</code></li>
<li>Source Port: <code class="language-plaintext highlighter-rouge">443</code></li>
<li>Destination Protocol: <em>HTTP</em></li>
<li>Destination Hostname: <code class="language-plaintext highlighter-rouge">localhost</code></li>
<li>Destination Port: <code class="language-plaintext highlighter-rouge">5000</code></li>
</ul>
</li>
<li>Set <em>Custom Header</em> options:
<ul>
<li>Choose <em>Create | ▼ | WebSocket</em> to create: <code class="language-plaintext highlighter-rouge">Upgrade</code> <code class="language-plaintext highlighter-rouge">$http_upgrade</code>
and <code class="language-plaintext highlighter-rouge">Connection</code> <code class="language-plaintext highlighter-rouge">$connection_upgrade</code></li>
</ul>
</li>
<li>Check that <em>Advanced Settings</em> has <em>HTTP 1.1</em> enabled</li>
</ul>
<blockquote>
<p>If you don’t specify <em>Source Hostname</em> it shows a misleading error:
“This port number is reserved for system use only. Please enter a different
number.” It’s only allowed to redirect traffic via a Host header.</p>
</blockquote>
<blockquote>
<p>I recommend to have a separate mapping for port 80 as well, even if you
would never use it. Otherwise if some application redirects you to HTTP
version of a web site it would load default contents, which is DSM Web UI
and this is super-confusing.</p>
</blockquote>
<p>Surprisingly this effectively updates Nginx configuration
in <code class="language-plaintext highlighter-rouge">/var/tmp/nginx/app.d/server.ReverseProxy.conf</code> with (simplified):</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">server</span> <span class="p">{</span>
<span class="nx">listen</span> <span class="mi">443</span> <span class="nx">ssl</span><span class="p">;</span>
<span class="nx">server_name</span> <span class="nx">your</span><span class="o">-</span><span class="nx">synology</span><span class="o">-</span><span class="nx">hostname</span><span class="o">-</span><span class="nx">you</span><span class="o">-</span><span class="nx">use</span><span class="o">-</span><span class="k">in</span><span class="o">-</span><span class="nx">a</span><span class="o">-</span><span class="nx">browser</span><span class="p">;</span>
<span class="nx">ssl_certificate</span> <span class="o">/</span><span class="nx">usr</span><span class="o">/</span><span class="nx">syno</span><span class="o">/</span><span class="nx">etc</span><span class="o">/</span><span class="nx">certificate</span><span class="o">/</span><span class="nx">ReverseProxy</span><span class="o">/</span><span class="nx">$GUID</span><span class="o">/</span><span class="nx">fullchain</span><span class="p">.</span><span class="nx">pem</span><span class="p">;</span>
<span class="nx">ssl_certificate_key</span> <span class="o">/</span><span class="nx">usr</span><span class="o">/</span><span class="nx">syno</span><span class="o">/</span><span class="nx">etc</span><span class="o">/</span><span class="nx">certificate</span><span class="o">/</span><span class="nx">ReverseProxy</span><span class="o">/</span><span class="nx">$GUID</span><span class="o">/</span><span class="nx">privkey</span><span class="p">.</span><span class="nx">pem</span><span class="p">;</span>
<span class="nx">location</span> <span class="o">/</span> <span class="p">{</span>
<span class="nx">proxy_pass</span> <span class="na">http</span><span class="p">:</span><span class="c1">//localhost:5000;</span>
<span class="nx">proxy_set_header</span> <span class="nx">Host</span> <span class="nx">$host</span><span class="p">;</span>
<span class="nx">proxy_set_header</span> <span class="nx">Upgrade</span> <span class="nx">$http_upgrade</span><span class="p">;</span>
<span class="nx">proxy_set_header</span> <span class="nx">Connection</span> <span class="dl">"</span><span class="s2">upgrade</span><span class="dl">"</span><span class="p">;</span>
<span class="nx">proxy_http_version</span> <span class="mf">1.1</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<blockquote>
<p>You only need <code class="language-plaintext highlighter-rouge">Upgrade</code> and <code class="language-plaintext highlighter-rouge">Connection</code> headers if web application uses
web sockets, I’m adding it just in case. You also need to explicitly
have <code class="language-plaintext highlighter-rouge">Host</code> header, otherwise some apps tend to redirect you incorrectly.</p>
</blockquote>
<h3 id="adding-https-certificates">Adding HTTPS Certificates</h3>
<p>You may have noticed that above a <code class="language-plaintext highlighter-rouge">fullchain.pem</code> and <code class="language-plaintext highlighter-rouge">privkey.pem</code> files
are mentioned. They are managed separately in
<em>Control panel | Security | Certificate</em> panel.</p>
<p>You need to add your own certificate and <em>Configure</em> to connect
<em>your-synology-hostname-you-use-in-a-browser</em> with a cert you’ve uploaded.
This way you can support as many virtual hosts on a Synology NAS as you need
as long as you’ve configured your DNS to point to it.</p>
<p>Synology is very picky about certificates:</p>
<ul>
<li>Only a certificate, a key and an (optional) intermediate CA are supported</li>
<li>Exactly one key and/or certificate per file
<ul>
<li>This prevents you from using more than one intermediate CA and makes it
difficult to use your own CA authority if you have complex chains</li>
<li>To make things harder when something goes wrong error messages are cryptic
at best</li>
</ul>
</li>
<li>Certificates and keys are only accepted in X.509 PEM form
<ul>
<li>Use <code class="language-plaintext highlighter-rouge">openssl x509 -inform der -in certificate.cer</code> to convert
certificate from X.509 DER form (<code class="language-plaintext highlighter-rouge">*.cer</code>) produced by
Keychain Access in macOS</li>
<li>Keys are only accepted in X.509 PEM form, use
<code class="language-plaintext highlighter-rouge">openssl pkcs12 -nodes -in key.p12</code> to convert from PKCS#12 DER
form produced by Keychain Access in macOS</li>
<li><a href="https://timothybasanov.com/2017/09/24/ubiquiti-nvr-unifi-mfi-nginx-ssl-proxy.html">Read my other post on Nginx/Certs/SSL</a></li>
</ul>
</li>
</ul>TimothySynology NAS’s OS DSM 6.x does not allow one to directly use ports 80 and 443. There are several different articles flowing around that try to cover it, but they often require SSH access to a box and may not be compatible with future versions of DSM. Turns out there is “one weird trick” to make it all work via built-in Nginx server relying on “Host” headers from HTTP requests. Verified on DS718+ DSM 5.2.1 with Ubiquity mFI controller and Homebridge (for HomeKit) running within Docker containers on Synology. As a side effect now I can use DSM without ugly port numbers (e.g. :5001) via a redirection below.Hardware-Accelerated h264 Encoding on Synology NAS2018-12-08T00:00:00-08:002018-12-08T00:00:00-08:00https://timothybasanov.com/2018/12/08/hardware-accelerated-h264-encoding-synology-nas<blockquote>
<p>Updated after publishing: I’ve got reports of verified support on DS218+ and DS418play.
I’ve added Debian Stretch-specific instructions. Added a disclaimer. Opened a pull request
<a href="https://github.com/gozoinks/homebridge-camera-ffmpeg-ufv/pull/30">#30 for homebridge-camera-ffmpeg-ufv</a>
to add support for VAAPI-based video transcoding.</p>
</blockquote>
<blockquote>
<p>Disclaimer: I know very little about <em>ffmpeg</em> and video encoding. I played
around for several days to figure out how to make hardware video transcoding
to work and just wrote down my findings. I’d be happy if somebody who knows
knows about these things would help me to better understand why things
behave the way they do.</p>
</blockquote>
<p>Many Synology NAS do have an Intel CPU that supports hardware-accelerated
h264 encoding, which Intel calls QuickSync for marketing purposes.
You would get around 10x improvement and most importantly real-time
video transcoding with low latency.
Surprisingly they seemingly do not use it themselves internally, but it’s
possible to use it manually. This easily works from within Docker as well.</p>
<h2 id="synology-nas-models-with-hardware-h264">Synology NAS Models with Hardware h264</h2>
<p>These instructions were verified on
<a href="https://www.synology.com/en-us/products/DS718+">Synology NAS DiskStation DS718+</a>
which uses
<a href="https://www.intel.com/content/www/us/en/products/processors/celeron/j3455.html">Intel Celeron J3455</a>.
It’s the same CPU as DS918+, so this should apply to that model as
well. Similar
<a href="https://ark.intel.com/products/95597/Intel-Celeron-Processor-J3355-2M-Cache-up-to-2-5-GHz-">Intel Celeron J3355</a>
also has QuickSync, all of this applies to DS218+
(<a href="https://www.reddit.com/r/synology/comments/a4jboo/you_can_use_hardwareaccelerated_h264_encoding_on/ebg9hsg/">verified by ArtisanalCollabo</a>)
and DS418play (verified by Arsen Vartapetov).</p>
<!--more-->
<p>CPUs that do not support QuickSync and do not support hardware acceleration:</p>
<ul>
<li><a href="https://ark.intel.com/products/97929/Intel-Atom-Processor-C3538-8M-Cache-up-to-2-10-GHz-">Intel Atom C3538</a>
and
<a href="https://ark.intel.com/products/77981/Intel-Atom-Processor-C2538-2M-Cache-2-40-GHz-">Intel Atom C2538</a>
which are used for DS1517+, DS1618+, DS1817+, DS1819+, DS2415+,
RS818+, RS1219+, RS2418+, RS2418RP+, RS2819RP+ Synology NAS models</li>
<li><a href="https://ark.intel.com/products/91199/Intel-Xeon-Processor-D-1541-12M-Cache-2-10-GHz-">Intel Xeon D-1541</a>,
<a href="https://ark.intel.com/products/91203/Intel-Xeon-Processor-D-1531-9M-Cache-2-20-GHz-">Intel Xeon D-1531</a>,
<a href="https://ark.intel.com/products/91202/Intel-Xeon-Processor-D-1521-6M-Cache-2-40-GHz-">Intel Xeon D-1521</a>,
<a href="https://ark.intel.com/products/91195/Intel-Xeon-Processor-D-1527-6M-Cache-2-20-GHz-">Intel Xeon D-1527</a>
which are used for for RS18017xs+, RS3618xs, RS4017xs+, DS3617xs,
RS1619xs+, RS3617RPxs, RS3617xs+ Synology NAS models</li>
<li><a href="https://ark.intel.com/products/91558/Intel-Pentium-Processor-D1508-3M-Cache-2-20-GHz-">Intel Pentium D1508</a>
which are used in FS1018, DS3018xs Synology NAS models</li>
</ul>
<p>All other NASes from Synology as of 2018 use Realtek CPUs,
I do not know if they support it or not, but I lean heavily on a “no” side.</p>
<h2 id="different-flavors-of-hardware-accelerated-ffmpeg">Different Flavors of Hardware-Accelerated <em>ffmpeg</em></h2>
<p><em>ffmpeg</em> supports
<a href="https://trac.ffmpeg.org/wiki/HWAccelIntro"><em>many</em> different types of accelerated encoding</a>.
Luckly for us only <em>libmfx</em>, <em>OpenCL</em>, and <em>VAAPI</em> are supported
by Intel CPUs on Linux. <em>OpenCL</em> implementation does not support hardware
encoding, and
<a href="https://trac.ffmpeg.org/wiki/HWAccelIntro#libmfx"><em>libmfx</em> is very hard to use on Linux</a>
which leaves us with only one possibility: <em>VAAPI</em>.</p>
<blockquote>
<p>If you see <code class="language-plaintext highlighter-rouge">h264_qsv</code> recommended somewhere it would use <em>libmfx</em> under the
hood. I have not found a simple way to make it work.</p>
</blockquote>
<h3 id="synologys-own-ffmpeg">Synology’s Own <em>ffmpeg</em></h3>
<p>I was surprised to find <em>ffmpeg</em> version 2.7, which misses some of the hardware
acceleration implementations and was released back in 2015:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ffmpeg 2>&1 | <span class="nb">head</span> <span class="nt">-n2</span>
ffmpeg version 2.7.1 Copyright <span class="o">(</span>c<span class="o">)</span> 2000-2015 the FFmpeg developers
built with gcc 4.9.3 <span class="o">(</span>crosstool-NG 1.20.0<span class="o">)</span> 20150311 <span class="o">(</span>prerelease<span class="o">)</span>
</code></pre></div></div>
<p>Which means it does not support any hardware implementations:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ffmpeg <span class="nt">-buildconf</span> 2>/dev/null | <span class="nb">grep</span> <span class="s1">'vaapi\|hw'</span>
<span class="nt">--disable-vaapi</span>
</code></pre></div></div>
<blockquote>
<p>What’s weird is that <code class="language-plaintext highlighter-rouge">/dev/dri/*</code> devices are present and initialized,
which hints that Synology can somehow use hardware encoding.
Most likely I’m just looking into the wrong place.</p>
</blockquote>
<h3 id="docker-based-ffmpeg-and-vaapi">Docker-based <em>ffmpeg</em> and <em>VAAPI</em></h3>
<blockquote>
<p>Check <a href="https://trac.ffmpeg.org/wiki/Hardware/VAAPI"><em>VAAPI</em> documentation</a>
for all the internal details, I would only show a very short summary.</p>
</blockquote>
<p><em>VAAPI</em> is a magical API that allows <em>ffmpeg</em> to use hardware acceleration
for different video-related operations and works across different hardware.
It’s always one of <code class="language-plaintext highlighter-rouge">/dev/dri/*</code> devices that can be used to talk to
the underlying hardware. We only need one for our purposes:
<code class="language-plaintext highlighter-rouge">/dev/dri/renderD128</code> (literally, <code class="language-plaintext highlighter-rouge">D128</code> is the same across platforms).</p>
<p>Options to add to enable <em>VAAPI</em>:</p>
<ul>
<li>Enable <em>VAAPI</em> <code class="language-plaintext highlighter-rouge">-hwaccel vaapi</code></li>
<li>Make frame buffer format conversion to make hardware codec happy:
<code class="language-plaintext highlighter-rouge">-hwaccel_output_format vaapi</code> or
<code class="language-plaintext highlighter-rouge">-vf 'format=nv12,hwupload'</code> or <code class="language-plaintext highlighter-rouge">-vf 'scale_vaapi=w=1280:h=720'</code></li>
<li>Actually use h264-codec with <em>VAAPI</em>: <code class="language-plaintext highlighter-rouge">-c:v h264_vaapi</code></li>
</ul>
<p>Simplest command to verify your encoding performance
using an example video <a href="http://bbb3d.renderfarming.net">Big Buck Bunny</a>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
jrottenberg/ffmpeg:vaapi <span class="se">\</span>
<span class="nt">-hwaccel</span> vaapi <span class="nt">-hwaccel_output_format</span> vaapi <span class="se">\</span>
<span class="nt">-i</span> http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 <span class="se">\</span>
<span class="nt">-c</span>:v h264_vaapi <span class="se">\</span>
/tmp/example.mp4
</code></pre></div></div>
<blockquote>
<p>Check a longer example in a performance section below.</p>
</blockquote>
<h3 id="debian-stretch-based-ffmpeg-issues">Debian Stretch-based <em>ffmpeg</em> issues</h3>
<p>Debian Stretch-based <em>ffmpeg</em> 3.2 differs from Docker defaults and
require additional tweaks to make it work.
I’ve only verified it from within Debian-based
Docker image with <em>ffmpeg</em> installed via <code class="language-plaintext highlighter-rouge">apt-get install ffmpeg</code>, so your
results may differ.</p>
<ul>
<li>VAAPI-based surface format is not supported, so we can not use
<code class="language-plaintext highlighter-rouge">-hwaccel_output_format vaapi</code> directly</li>
<li>This means we need to download decoded frames into memory and upload
them back via <code class="language-plaintext highlighter-rouge">-vf 'format=nv12,hwupload'</code></li>
<li>We need to explicitly specify device to upload frames to via
<code class="language-plaintext highlighter-rouge">-vaapi_device /dev/dri/renderD128</code></li>
<li>Overall it’s much slower than full-speed hardware encoding,
but it’s still much faster than a software one</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
debian:stretch-slim <span class="se">\</span>
/bin/sh <span class="nt">-c</span> <span class="s2">"
apt-get update
apt-get install --assume-yes ffmpeg
ffmpeg </span><span class="se">\</span><span class="s2">
-hwaccel vaapi </span><span class="se">\</span><span class="s2">
-vaapi_device /dev/dri/renderD128 </span><span class="se">\</span><span class="s2">
-i http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 </span><span class="se">\</span><span class="s2">
-vf 'format=nv12,hwupload' </span><span class="se">\</span><span class="s2">
-c:v h264_vaapi </span><span class="se">\</span><span class="s2">
/tmp/example.mp4
"</span>
</code></pre></div></div>
<ul>
<li>In some cases you can use this format without crashes
<code class="language-plaintext highlighter-rouge">-hwaccel_output_format vaapi -vf 'format=nv12|vaapi,hwupload'</code>
this variant has the same performance as hardware variant,
but I’m not sure how portable it is</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
debian:stretch-slim <span class="se">\</span>
/bin/sh <span class="nt">-c</span> <span class="s2">"
apt-get update
apt-get install --assume-yes ffmpeg
ffmpeg </span><span class="se">\</span><span class="s2">
-hwaccel vaapi -hwaccel_output_format vaapi </span><span class="se">\</span><span class="s2">
-vaapi_device /dev/dri/renderD128 </span><span class="se">\</span><span class="s2">
-i http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 </span><span class="se">\</span><span class="s2">
-vf 'format=nv12|vaapi,hwupload' </span><span class="se">\</span><span class="s2">
-c:v h264_vaapi </span><span class="se">\</span><span class="s2">
/tmp/example.mp4
"</span>
</code></pre></div></div>
<blockquote>
<p>You may get this warning: <em>Hardware accelerated decoding with frame
threading is known to be unstable and its use is discouraged.</em>
The way I read it it should be fixed if I specify <code class="language-plaintext highlighter-rouge">-threads 1</code>, but it does
not fix it. This transcode was stable enough for my purposes, so I just
ignored it.</p>
</blockquote>
<h2 id="synology-configs--docker">Synology Configs & Docker</h2>
<p>Normally I’d do something like this:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">--device</span> /dev/dri:/dev/dri jrottenberg/ffmpeg:vaapi ...
</code></pre></div></div>
<p>Synology’s OS DSM 6.x uses its own configuration format for Docker and
does not easily allow one to override <code class="language-plaintext highlighter-rouge">docker run</code> command’s command line
parameters. I have not found a documented way to configure it, but if you
configure a Docker container via a web UI and and “export” config into a file
you can add this into a plain JSON to configure devices mount:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w"> </span><span class="nl">"devices"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"CgroupPermissions"</span><span class="p">:</span><span class="w"> </span><span class="s2">"rwm"</span><span class="p">,</span><span class="w">
</span><span class="nl">"PathInContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\/</span><span class="s2">dev/dri"</span><span class="p">,</span><span class="w">
</span><span class="nl">"PathOnHost"</span><span class="p">:</span><span class="w"> </span><span class="s2">"</span><span class="se">\/</span><span class="s2">dev</span><span class="se">\/</span><span class="s2">dri"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="err">,</span><span class="w">
</span></code></pre></div></div>
<blockquote>
<p>Note: If you run your Dockerized app under non-priviledged user, don’t
forget to give access to your devices: <code class="language-plaintext highlighter-rouge">chmod 777 /dev/dri/renderD128</code></p>
</blockquote>
<p>There is no simple way of calling Dockerized <em>ffmpeg</em> from
an another Docker image, but if you use Debian-based docker image chances
are it would be as easy as:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt-get <span class="nb">install </span>ffmpeg
</code></pre></div></div>
<p>This may pull in the up-to-date version of <em>ffmpeg</em> with all the right bindings
and devices.</p>
<h2 id="transcoding-performance-results-on-ds718">Transcoding Performance Results on DS718+</h2>
<h3 id="big-buck-bunny-scaling-from-1080p-into-720p">Big Buck Bunny: scaling from 1080p into 720p</h3>
<table>
<thead>
<tr>
<th style="text-align: right"> </th>
<th>fps</th>
<th>CPU%</th>
<th>fps/CPU core</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right">Software:</td>
<td>30</td>
<td>380%</td>
<td>8</td>
</tr>
<tr>
<td style="text-align: right">Mixed1:</td>
<td>40</td>
<td>70%</td>
<td>60</td>
</tr>
<tr>
<td style="text-align: right">Mixed2:<sup>†</sup></td>
<td>60</td>
<td>70%</td>
<td>85</td>
</tr>
<tr>
<td style="text-align: right">Hardware:</td>
<td>110</td>
<td>85%</td>
<td>130</td>
</tr>
<tr>
<td style="text-align: right"><em>Improvement:</em></td>
<td>3x</td>
<td>5x</td>
<td>15x</td>
</tr>
</tbody>
</table>
<blockquote>
<p><sup>†</sup> For some reason hardware-only surface formats are not supported
on Debian and one needs to copy data between decoder and encoder via a main
memory. This is not Debian-specific, but it only affected my Debian-based
Docker images for some reason. I may be mistaken and it could be that
either encoder or decoder are run in software.</p>
</blockquote>
<p>Software transcoding example command:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
jrottenberg/ffmpeg:vaapi <span class="se">\</span>
<span class="nt">-i</span> http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 <span class="se">\</span>
<span class="nt">-vf</span> <span class="s1">'scale=1280:720'</span> <span class="se">\</span>
/tmp/example.mp4
</code></pre></div></div>
<p>Mixed1: Transcoding that uses hardware decoder and encoder, but copies data
over through a main memory between them. This is what you get by default
on Debian Stretch. Example command:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
debian:stretch-slim <span class="se">\</span>
/bin/sh <span class="nt">-c</span> <span class="s2">"
apt-get update
apt-get install --assume-yes ffmpeg
ffmpeg </span><span class="se">\</span><span class="s2">
-hwaccel vaapi </span><span class="se">\</span><span class="s2">
-vaapi_device /dev/dri/renderD128 </span><span class="se">\</span><span class="s2">
-i http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 </span><span class="se">\</span><span class="s2">
-vf 'format=nv12,hwupload,scale_vaapi=w=1280:h=720' </span><span class="se">\</span><span class="s2">
-c:v h264_vaapi </span><span class="se">\</span><span class="s2">
/tmp/example.mp4
"</span>
</code></pre></div></div>
<p>Mixed2: Just like Mixed1, but does one additional hack with <code class="language-plaintext highlighter-rouge">nv12|vaapi</code>.
Example command:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
debian:stretch-slim <span class="se">\</span>
/bin/sh <span class="nt">-c</span> <span class="s2">"
apt-get update
apt-get install --assume-yes ffmpeg
ffmpeg </span><span class="se">\</span><span class="s2">
-hwaccel vaapi -hwaccel_output_format vaapi </span><span class="se">\</span><span class="s2">
-vaapi_device /dev/dri/renderD128 </span><span class="se">\</span><span class="s2">
-i http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 </span><span class="se">\</span><span class="s2">
-vf 'format=nv12|vaapi,hwupload,scale_vaapi=w=1280:h=720' </span><span class="se">\</span><span class="s2">
-c:v h264_vaapi </span><span class="se">\</span><span class="s2">
/tmp/example.mp4
"</span>
</code></pre></div></div>
<p>Hardware-accelerated transcoding example command:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
jrottenberg/ffmpeg:vaapi <span class="se">\</span>
<span class="nt">-hwaccel</span> vaapi <span class="nt">-hwaccel_output_format</span> vaapi <span class="se">\</span>
<span class="nt">-i</span> http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4 <span class="se">\</span>
<span class="nt">-vf</span> <span class="s1">'scale_vaapi=w=1280:h=720'</span> <span class="se">\</span>
<span class="nt">-c</span>:v h264_vaapi <span class="se">\</span>
/tmp/example.mp4
</code></pre></div></div>
<h3 id="unifi-video-transcoding-into-apple-homekit-via-homebridge">UniFi Video: transcoding into Apple HomeKit via HomeBridge</h3>
<table>
<thead>
<tr>
<th style="text-align: right"> </th>
<th>fps</th>
<th>CPU%</th>
<th>fps/CPU core</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right">Software:</td>
<td>15</td>
<td>300%</td>
<td>5</td>
</tr>
<tr>
<td style="text-align: right">Hardware:</td>
<td>30<sup>†</sup></td>
<td>20%</td>
<td>150</td>
</tr>
<tr>
<td style="text-align: right"><em>Improvement:</em></td>
<td>2x</td>
<td>15x</td>
<td>30x</td>
</tr>
</tbody>
</table>
<blockquote>
<p><sup>†</sup> Original real-timee stream is 30fps and transcoding is done
in real time. It can not go any faster than 30fps.</p>
</blockquote>
<p>Software transcoding example command:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
jrottenberg/ffmpeg:vaapi <span class="se">\</span>
<span class="nt">-rtsp_transport</span> http <span class="nt">-re</span> <span class="nt">-i</span> <span class="nv">$UNIFI_VIDEO_CAMERA_RSTP_URL</span>?apiKey<span class="o">=</span><span class="nv">$UNIFI_VIDEO_USER_API_KEY</span> <span class="se">\</span>
<span class="nt">-threads</span> 0 <span class="nt">-vcodec</span> libx264 <span class="nt">-an</span> <span class="nt">-pix_fmt</span> yuv420p <span class="nt">-r</span> 30 <span class="nt">-f</span> rawvideo <span class="nt">-tune</span> zerolatency <span class="se">\</span>
<span class="nt">-vf</span> <span class="s1">'scale=1920:1080'</span> <span class="se">\</span>
/tmp/example.mp4
</code></pre></div></div>
<p>Hardware-accelerated transcoding example command:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>docker run <span class="nt">--rm</span> <span class="se">\</span>
<span class="nt">--device</span> /dev/dri:/dev/dri <span class="se">\</span>
jrottenberg/ffmpeg:vaapi <span class="se">\</span>
<span class="nt">-hwaccel</span> vaapi <span class="nt">-hwaccel_output_format</span> vaapi <span class="se">\</span>
<span class="nt">-rtsp_transport</span> http <span class="nt">-re</span> <span class="nt">-i</span> <span class="nv">$UNIFI_VIDEO_CAMERA_RSTP_URL</span>?apiKey<span class="o">=</span><span class="nv">$UNIFI_VIDEO_USER_API_KEY</span> <span class="se">\</span>
<span class="nt">-threads</span> 0 <span class="nt">-vcodec</span> libx264 <span class="nt">-an</span> <span class="nt">-pix_fmt</span> yuv420p <span class="nt">-r</span> 30 <span class="nt">-f</span> rawvideo <span class="nt">-tune</span> zerolatency <span class="se">\</span>
<span class="nt">-vf</span> <span class="s1">'scale_vaapi=w=1920:h=1080'</span> <span class="se">\</span>
<span class="nt">-c</span>:v h264_vaapi <span class="se">\</span>
/tmp/example.mp4
</code></pre></div></div>TimothyUpdated after publishing: I’ve got reports of verified support on DS218+ and DS418play. I’ve added Debian Stretch-specific instructions. Added a disclaimer. Opened a pull request #30 for homebridge-camera-ffmpeg-ufv to add support for VAAPI-based video transcoding. Disclaimer: I know very little about ffmpeg and video encoding. I played around for several days to figure out how to make hardware video transcoding to work and just wrote down my findings. I’d be happy if somebody who knows knows about these things would help me to better understand why things behave the way they do. Many Synology NAS do have an Intel CPU that supports hardware-accelerated h264 encoding, which Intel calls QuickSync for marketing purposes. You would get around 10x improvement and most importantly real-time video transcoding with low latency. Surprisingly they seemingly do not use it themselves internally, but it’s possible to use it manually. This easily works from within Docker as well. Synology NAS Models with Hardware h264 These instructions were verified on Synology NAS DiskStation DS718+ which uses Intel Celeron J3455. It’s the same CPU as DS918+, so this should apply to that model as well. Similar Intel Celeron J3355 also has QuickSync, all of this applies to DS218+ (verified by ArtisanalCollabo) and DS418play (verified by Arsen Vartapetov).Marrying Byobu/tmux and Marathono2017-11-24T00:00:00-08:002017-11-24T00:00:00-08:00https://timothybasanov.com/2017/11/24/byobu-tmux-marathono<p><a href="https://gitlab.com/marathono/marathono">Marathono</a> is a great services
manager for macOS. It natively integrates with <em>launchd</em> and works well overall.
Unfortunately due to <a href="https://gitlab.com/marathono/marathono/issues/29">#29</a>
it always uses login interactive shell with a TTY attached. This triggers
<code class="language-plaintext highlighter-rouge">.zprofile</code> and friends and <a href="http://byobu.co">Byobu</a>/tmux interactive mode
and prevents scripts from being executed. There is a simple solution,
detect Marathono in <code class="language-plaintext highlighter-rouge">.zprofile</code> and skip Byobu/tmux when detected:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps <span class="nt">-fp</span> <span class="nv">$PPID</span> | <span class="nb">grep </span>Marathono <span class="o">||</span> <span class="se">\</span>
<span class="nv">_byobu_sourced</span><span class="o">=</span>1 <span class="nb">.</span> /usr/local/bin/byobu-launch 2>/dev/null <span class="o">||</span> <span class="nb">true</span>
</code></pre></div></div>TimothyMarathono is a great services manager for macOS. It natively integrates with launchd and works well overall. Unfortunately due to #29 it always uses login interactive shell with a TTY attached. This triggers .zprofile and friends and Byobu/tmux interactive mode and prevents scripts from being executed. There is a simple solution, detect Marathono in .zprofile and skip Byobu/tmux when detected: ps -fp $PPID | grep Marathono || \ _byobu_sourced=1 . /usr/local/bin/byobu-launch 2>/dev/null || trueFronting Ubiquiti’s UniFi Controllers with Nginx with SSL2017-09-24T00:00:00-07:002017-09-24T00:00:00-07:00https://timothybasanov.com/2017/09/24/ubiquiti-nvr-unifi-mfi-nginx-ssl-proxy<p>All Ubiquiti controllers (mFI, UniFi Controller and UniFi Video Controller)
insist on using HTTPS for all connections, which may break the experience
in Safari when one uses default out-of-the-box self-signed certificates.</p>
<p>Solution is simple: use Nginx to deal with SSL for most of the cases,
the rest is to use SSL via Java to make secure sockets work.</p>
<blockquote>
<p>File extensions are <em>very</em> confusing when one is dealing with certificates.
In short if you use <em>OpenSSL</em>:</p>
<ul>
<li><em>PEM</em> means ASCII plaintext, <em>DER</em> means binary format, these only
apply to <code class="language-plaintext highlighter-rouge">-inform</code> and <code class="language-plaintext highlighter-rouge">-outform</code> parameters
(quoting <a href="http://info.ssl.com/article.aspx?id=12149">this article</a>)</li>
<li><a href="https://wiki.openssl.org/index.php/Manual:Pkcs8(1)">PKCS#8</a>
is a format for private keys, Java can read its <em>DER</em> form. Use
<code class="language-plaintext highlighter-rouge">-topk8</code> (to PK8 i.e. to PKCS#8) to output it. Use <code class="language-plaintext highlighter-rouge">-nocrypt</code>
to disable key encryption enabled by default.</li>
<li><a href="https://wiki.openssl.org/index.php/Manual:Pkcs12(1)">PKCS#12</a>
is a format for a key bag, where several keys and
certificates may be stored. Output is controlled with
<code class="language-plaintext highlighter-rouge">-nodes</code> (no DES i.e. no encryption),
<code class="language-plaintext highlighter-rouge">-nocerts</code> (skips public keys), and <code class="language-plaintext highlighter-rouge">-nokeys</code> (skips private keys)</li>
<li><a href="https://wiki.openssl.org/index.php/Manual:X509(1)">X.509</a>
is the default format for keys and certificates, can be read by Nginx.
Can be directly concatenated in its <em>PEM</em> form</li>
</ul>
</blockquote>
<!--more-->
<h2 id="installing-nginx-and-configuring-ssl">Installing Nginx and Configuring SSL</h2>
<blockquote>
<p>I assume that one uses NVR Ubiquiti box for all the controllers. That box
uses Debian, but most of these instructions would just apply to Ubuntu as well.</p>
</blockquote>
<p>NVR has <code class="language-plaintext highlighter-rouge">nvr-webui</code> deb package, which has a dependency on <code class="language-plaintext highlighter-rouge">nginx-light</code>,
which has all the facilities to make all of this work. Otherwise one would
<code class="language-plaintext highlighter-rouge">apt-get install nginx</code> to get one on the box.</p>
<p>The easiest way to make Nginx to work is to add all the required keys and
certificates into a single decrypted X.509 PEM file, where private key
goes first.
I had <code class="language-plaintext highlighter-rouge">NVR_SSL.p12</code> PKCS#12 SSL certificate keys file and
<code class="language-plaintext highlighter-rouge">SSL_CA.cer</code> X.509 intermediate CA certificate (both binary files).</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>
openssl pkcs12 <span class="nt">-nodes</span> <span class="nt">-in</span> NVR_SSL.p12
openssl x509 <span class="nt">-inform</span> der <span class="nt">-in</span> SSL_CA.cer
<span class="o">)</span> <span class="o">></span> /etc/nginx/NVR_SSL_Chain.pem
</code></pre></div></div>
<blockquote>
<p>NVR has an Nginx 1.2.1 Light version preinstalled. A lot of Nginx config
options related to SSL and proxying were added much later on.
This article may inadvertently rely on
<a href="/2017/09/08/ubuquiti-nvr-unifi-mfi-install.html#add-ubiquitis-debian-repositories">newer Debian’s version from backports</a>.
Install with: <code class="language-plaintext highlighter-rouge">apt-get -t wheezy-backports install nginx-common nginx-light</code></p>
</blockquote>
<blockquote>
<p>If you’re using a CloudKey2 you need to copy the chain file into
both <code class="language-plaintext highlighter-rouge">/etc/ssl/private/cloudkey.crt</code> and <code class="language-plaintext highlighter-rouge">/etc/ssl/private/cloudkey.key</code>
which would be picked up by Nginx. To update UniFi controller’s JKS store
follow
<a href="https://blog.arrogantrabbit.com/ssl/Ubiquiti-SSL/">Provisioning custom SSL keys to Ubiquiti CloudKey and UniFi Controller</a>, where all the steps before
<code class="language-plaintext highlighter-rouge">openssl pkcs12 -export ...</code> were already completed. Be sure to create
<code class="language-plaintext highlighter-rouge">cert.tar</code> as recommended.</p>
</blockquote>
<h3 id="configuring-nginx-reverse-proxy">Configuring Nginx reverse proxy</h3>
<ul>
<li>Ubiquiti insists on using HTTPS ports, so we proxy pass to HTTPS ports</li>
<li>Nginx by default does not validate proxy SSL certificates</li>
<li>Ubiquiti software relies on WebSockets a lot
(see <a href="https://www.nginx.com/blog/websocket-nginx/">Nginx & WebSockets</a>)</li>
<li>Ubiquiti software uses X443 ports for HTTPS and X080 ports for HTTP</li>
</ul>
<p>Overall it’s a standard Nginx SSL reverse-proxy configuration:</p>
<pre><code class="language-txt">cat > /etc/nginx/sites-enabled/ssl-proxy <<EOF
server {
listen 443 ssl;
server_name mfi.home.timothybasanov.com;
ssl_certificate /etc/nginx/NVR_SSL_Chain.pem;
ssl_certificate_key /etc/nginx/NVR_SSL_Chain.pem;
ssl on;
ssl_protocols TLSv1.2;
location / {
proxy_pass https://localhost:6443;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
}
}
server {
listen 443 ssl;
server_name nvr.home.timothybasanov.com;
ssl_certificate /etc/nginx/NVR_SSL_Chain.pem;
ssl_certificate_key /etc/nginx/NVR_SSL_Chain.pem;
ssl on;
ssl_protocols TLSv1.2;
location / {
proxy_pass https://localhost:7443;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
}
}
server {
listen 443 ssl;
server_name unifi.home.timothybasanov.com;
ssl_certificate /etc/nginx/NVR_SSL_Chain.pem;
ssl_certificate_key /etc/nginx/NVR_SSL_Chain.pem;
ssl on;
ssl_protocols TLSv1.2;
location / {
proxy_pass https://localhost:8443;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
}
}
EOF
</code></pre>
<h3 id="fix-for-a-unifi-video-controllers-secure-websockets">Fix for a UniFi Video controller’s secure WebSockets</h3>
<ul>
<li>UniFi Video uses <code class="language-plaintext highlighter-rouge">wss://hostname:7446</code> for secure web sockets</li>
<li>Direct WSS URL is passed from a server via XHR API call</li>
<li>We can not find and replace it</li>
<li>Nginx can not intercept it as port is already bound</li>
<li><em>Solution:</em> We need to add our SSL certificates to UniFi-Video server</li>
</ul>
<blockquote>
<p>To proxy this call via Nginx one needs to either have a separate IP
for Nginx server or to fix the url passed from API in-flight. I did not want
to deal with the additional IP address. As for in-flight replacement
it requires a module from <code class="language-plaintext highlighter-rouge">nginx-full</code> deb package,
which conflicts with <code class="language-plaintext highlighter-rouge">nginx-light</code>,
which is required for <code class="language-plaintext highlighter-rouge">nvr-webui</code> which I still rely on.</p>
</blockquote>
<p>Ubiquiti published all the instructions in their
<a href="https://community.ubnt.com/t5/UniFi-Video-Blog/UniFi-Video-3-7-0-Release/ba-p/1934006">UniFi Video 3.7.0 release notes</a>:</p>
<ol>
<li>Stop the service: <code class="language-plaintext highlighter-rouge">/etc/init.d/unifi-video stop</code></li>
<li>Clean up the outdated key store:
<code class="language-plaintext highlighter-rouge">rm /usr/lib/unifi-video/data/ufv-truststore</code></li>
<li>Enable support for custom certificates, an experimental feature:
<code class="language-plaintext highlighter-rouge">echo ufv.custom.certs.enable=true >> /usr/lib/unifi-video/data/system.properties</code></li>
<li>Create a directory for new certificates:
<code class="language-plaintext highlighter-rouge">mkdir /usr/lib/unifi-video/data/certificates</code></li>
<li>Generate X.509 DER certificates file:
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Generate it from the original files</span>
<span class="o">(</span>
openssl pkcs12 <span class="nt">-nodes</span> <span class="nt">-nokeys</span> <span class="nt">-in</span> NVR_SSL.p12
openssl x509 <span class="nt">-inform</span> der <span class="nt">-in</span> SSL_CA.cer
<span class="o">)</span> | openssl x509 <span class="nt">-outform</span> DER <span class="se">\</span>
<span class="o">></span> /usr/lib/unifi-video/data/certificates/ufv-server.cert.der
<span class="c"># Alternatively read back Nginx configs</span>
openssl x509 <span class="nt">-outform</span> DER <span class="nt">-in</span> /etc/nginx/NVR_SSL_Chain.pem <span class="se">\</span>
<span class="o">></span> /usr/lib/unifi-video/data/certificates/ufv-server.cert.der
</code></pre></div> </div>
</li>
<li>Generate PKCS#8 DER private key file:
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Generate it from the original files</span>
openssl pkcs12 <span class="nt">-nodes</span> <span class="nt">-nocerts</span> <span class="nt">-in</span> NVR_SSL.p12 <span class="se">\</span>
| openssl pkcs8 <span class="nt">-topk8</span> <span class="nt">-nocrypt</span> <span class="nt">-outform</span> DER <span class="se">\</span>
<span class="o">></span> /usr/lib/unifi-video/data/certificates/ufv-server.key.der
<span class="c"># Alternatively read back Nginx configs</span>
openssl pkcs8 <span class="nt">-topk8</span> <span class="nt">-nocrypt</span> <span class="nt">-outform</span> DER <span class="nt">-in</span> /etc/nginx/NVR_SSL_Chain.pem <span class="se">\</span>
<span class="o">></span> /usr/lib/unifi-video/data/certificates/ufv-server.key.der
</code></pre></div> </div>
</li>
<li>Fix up the permissions:
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chmod </span>600 /usr/lib/unifi-video/data/certificates
<span class="nb">chown</span> <span class="nt">-R</span> unifi-video:unifi-video /usr/lib/unifi-video/data/certificates
</code></pre></div> </div>
</li>
<li>Start the service: <code class="language-plaintext highlighter-rouge">/etc/init.d/unifi-video start</code></li>
<li>Verify <code class="language-plaintext highlighter-rouge">less /var/log/unifi-video/server.log</code> log to not to have
exceptions about a key import format. If there are issues, start
from the first step all over</li>
<li>Clean up: <code class="language-plaintext highlighter-rouge">rm -rf /usr/lib/unifi-video/data/certificates</code></li>
</ol>
<blockquote>
<p>There are several things that Ubiquiti may fix to make our life easier:</p>
<ul>
<li>Allow certificates import directly via Web UI</li>
<li>Allow binding to <code class="language-plaintext highlighter-rouge">localhost</code> and using HTTP instead of HTTPS</li>
<li>Add support for <code class="language-plaintext highlighter-rouge">X-Remote-IP</code> headers, now
all logins are logged coming from Nginx’s IP address <code class="language-plaintext highlighter-rouge">127.0.0.1</code></li>
<li>Make their deb packages depend on <code class="language-plaintext highlighter-rouge">nginx-light</code> <strong>or</strong> <code class="language-plaintext highlighter-rouge">nginx-full</code></li>
</ul>
</blockquote>
<p>Done. Now everything should work just fine. I verified it to work
even in Safari!</p>TimothyAll Ubiquiti controllers (mFI, UniFi Controller and UniFi Video Controller) insist on using HTTPS for all connections, which may break the experience in Safari when one uses default out-of-the-box self-signed certificates. Solution is simple: use Nginx to deal with SSL for most of the cases, the rest is to use SSL via Java to make secure sockets work. File extensions are very confusing when one is dealing with certificates. In short if you use OpenSSL: PEM means ASCII plaintext, DER means binary format, these only apply to -inform and -outform parameters (quoting this article) PKCS#8 is a format for private keys, Java can read its DER form. Use -topk8 (to PK8 i.e. to PKCS#8) to output it. Use -nocrypt to disable key encryption enabled by default. PKCS#12 is a format for a key bag, where several keys and certificates may be stored. Output is controlled with -nodes (no DES i.e. no encryption), -nocerts (skips public keys), and -nokeys (skips private keys) X.509 is the default format for keys and certificates, can be read by Nginx. Can be directly concatenated in its PEM formInstalling UniFi and mFI Controller Software onto Ubiquiti’s UNC-NVR2017-09-08T00:00:00-07:002017-09-08T00:00:00-07:00https://timothybasanov.com/2017/09/08/ubuquiti-nvr-unifi-mfi-install<p>It makes some sense to keep all Ubiquiti’s software on the same PC,
and UNV-NVR is the best one with software already preinstalled.
NVR has Debian 7 installed by default, so it’s easy to install both
mFI and UniFi,
with a caveat of using MongoDB 2.4 instead of a default 2.0 or 3.0 one.</p>
<!--more-->
<h2 id="enable-ssh-on-nvr">Enable SSH on NVR</h2>
<p>We will need SSH access to NVR box. You will have an option to disable it
after we finished.</p>
<blockquote>
<p>See Ubituiti’s support articles
<a href="https://help.ubnt.com/hc/en-us/articles/222970307-UniFi-Video-How-to-Enable-SSH-on-the-UVC-NVR-Hardware-NVR-">UniFi Video - How to Enable SSH on the UVC-NVR (Hardware NVR)</a>.</p>
</blockquote>
<p>Open http://NVR_IP in Google Chrome, click on “Settings” button up right
and enable SSH server on Configuration page. Don’t forget to change the password
from the default ont.</p>
<h2 id="install-ubiquitis-software">Install Ubiquiti’s software</h2>
<p>Connect to the box with a <code class="language-plaintext highlighter-rouge">root</code> user and the password you setup above.</p>
<h3 id="add-ubiquitis-debian-repositories">Add Ubiquiti’s Debian repositories</h3>
<p>We need to add references to Ubiquiti’s repos to be able to install its
software.</p>
<blockquote>
<p>See Ubiquiti’s support articles
<a href="https://help.ubnt.com/hc/en-us/articles/211656797-mFi-How-To-Install-the-mFi-Controller-in-Ubuntu-or-Debian">mFi - How To Install the mFi Controller in Ubuntu or Debian</a>
and
<a href="https://help.ubnt.com/hc/en-us/articles/220066768-UniFi-How-to-Install-Update-via-APT-on-Debian-or-Ubuntu">UniFi - How to Install & Update via APT on Debian or Ubuntu</a>.
You never need to update “keys”, they come pre-installed.</p>
</blockquote>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'deb http://dl.ubnt.com/mfi/distros/deb/debian debian ubiquiti'</span> <span class="se">\</span>
<span class="o">></span> /etc/apt/sources.list.d/ubnt-mfi.list
<span class="nb">echo</span> <span class="s1">'deb http://www.ubnt.com/downloads/unifi/debian stable ubiquiti'</span> <span class="se">\</span>
<span class="o">></span> /etc/apt/sources.list.d/ubnt-unifi.list
apt-get update
</code></pre></div></div>
<h3 id="install-mongodb-24">Install MongoDB 2.4</h3>
<p>Well, turns out MongoDB 2.0 is too old and 2.4 is required. We need to
install fresher version from
<a href="https://backports.debian.org/Instructions/">Wheezy Backports Debian</a>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Remove MongoDB's old version 2.0 and all Ubiquiti's software</span>
apt-get purge <span class="s1">'^mongodb.*'</span> mfi unifi unifi-video
reboot
</code></pre></div></div>
<blockquote>
<p>You need purge to make sure that old MongoDB databases would be disabled.</p>
</blockquote>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Install a version 2.4</span>
<span class="nb">echo</span> <span class="s1">'deb http://ftp.debian.org/debian wheezy-backports main contrib non-free'</span> <span class="se">\</span>
<span class="o">></span> /etc/apt/sources.list.d/debian-backports.list
apt-get update
apt-get <span class="nt">-t</span> wheezy-backports <span class="nb">install </span>mongodb
</code></pre></div></div>
<blockquote>
<p>Using old version of MongoDB almost works, until server reboot.</p>
</blockquote>
<h3 id="install-unifi-unifi-video-and-mfi">Install UniFi, UniFi Video and mFI</h3>
<p>Time to reinstall all the software back!</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt-get <span class="nb">install </span>mfi unifi unifi-video
reboot
</code></pre></div></div>
<blockquote>
<p>Be sure to agree for a unifi’s backup, if apt asks for permission.
Declining may cause installation failure and half-installed packages.</p>
</blockquote>
<p>That’s it! Now everything should be just working. =)</p>
<blockquote>
<p>Note: There is a bug with default permissions for UniFi package. mFI package
is fine. Wrong permissions prevent UniFi being detected by NVR software
and no “button” appears on the main Web UI. This is a temporary fix until a next
restart:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chmod</span> +x /usr/lib/unifi/data
<span class="nb">chmod</span> +r /usr/lib/unifi/data/system.properties
</code></pre></div> </div>
</blockquote>TimothyIt makes some sense to keep all Ubiquiti’s software on the same PC, and UNV-NVR is the best one with software already preinstalled. NVR has Debian 7 installed by default, so it’s easy to install both mFI and UniFi, with a caveat of using MongoDB 2.4 instead of a default 2.0 or 3.0 one.How to Recover iPhone Backup’s Password?2017-08-30T00:00:00-07:002017-08-30T00:00:00-07:00https://timothybasanov.com/2017/08/30/recover-iphone-backup-password<p>If you backup your iPhone or iPad onto your iMac via iTunes you probably use
encrypted backups. Sometimes it happens that you forget your encryption
password. As it turns out
there is no way to disable backups or change their password without knowing
the original password. See Apple support article
<a href="https://support.apple.com/en-us/HT205220">About encrypted backups in iTunes</a>.</p>
<p>There are several paid solutions that can decrypt your password that could
be run inside a Windows virtual machine. But there is little guarantee that
they would not share all your data through the Internet. So, the obvious
solution is to use a command-line utility that does it for free.</p>
<blockquote>
<p>Time to try one password is roughly <strong>a minute per core</strong> on my 2014 iMac, so
this solution does not work if <strong>really</strong> don’t remember your password.</p>
</blockquote>
<!--more-->
<h3 id="why-does-it-take-a-minute-to-try-only-one-password">Why does it take a minute to try only one password?</h3>
<p>See StackOverflow for
<a href="https://stackoverflow.com/questions/1498342/how-to-decrypt-an-encrypted-apple-itunes-iphone-backup">How to decrypt an encrypted Apple iTunes iPhone backup?</a>
In short it’s a usual <a href="https://en.wikipedia.org/wiki/PBKDF2">PBKDF2</a>
scheme with lots of iterations. So there is
not shortcut, unless SHA is completely broken by the time you’re
reading this article.</p>
<h3 id="is-it-important-which-ios-version-do-you-have">Is it important which iOS version do you have?</h3>
<p>Yes. New versions of iOS/macOS/iTunes often bring new schemes for password
encryption, usually it only gets stronger over time. It also means
that unless somebody spent their time reverse engineering new encryption
scheme, you’re out of luck.</p>
<p>I’ve tried it with iOS 10.3.3 backups in iTunes 12.6.2.20 on macOS 10.12.15
and it worked fine.</p>
<h2 id="what-have-i-used-to-decrypt-it">What have I used to decrypt it?</h2>
<p>Collection of python scripts that can decrypt iPhone backups as one of the
many features that they support:
<a href="https://github.com/dinosec/iphone-dataprotection">iphone-dataprotection</a>.
One particularly easy to use python file is <code class="language-plaintext highlighter-rouge">python_scripts/backup_tool.py</code></p>
<p>I my case I knew prefix for my password, and I was pretty sure about specific
letters for the rest, so my search was very short.
I used this simple shell script to try to figure out the last 3 letters:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">##!/bin/bash</span>
try_pass<span class="o">()</span> <span class="o">{</span>
<span class="nv">END</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1$2$3</span><span class="s2">"</span>
<span class="c"># Print password in the logs to make it easier to read them</span>
<span class="nb">echo</span> <span class="s2">"Trying password: '*****</span><span class="nv">$END</span><span class="s2">'"</span>
<span class="c"># Agree to typing in a password and type in the password when tool prompts</span>
<span class="o">(</span> <span class="nb">echo </span>y <span class="p">;</span> <span class="nb">echo</span> <span class="s1">'known_pa$$w0rd_prefix'</span><span class="s2">"</span><span class="nv">$END</span><span class="s2">"</span> <span class="o">)</span> <span class="se">\</span>
| ./backup_tool.py <span class="se">\</span>
~/Library/Application<span class="se">\ </span>Support/MobileSync/Backup/00_LONG_BACKUP_HASH_00 <span class="se">\</span>
./extract
<span class="c"># If extract directory was created, exit with non-0 status</span>
<span class="o">[</span> <span class="o">!</span> <span class="nt">-d</span> ./extract <span class="o">]</span>
<span class="o">}</span>
<span class="c">## Make it available to parallel, only works in bash</span>
<span class="nb">export</span> <span class="nt">-f</span> try_pass
<span class="c">## show progress, tag output, exit when non-0 status, pipe output</span>
parallel <span class="nt">--progress</span> <span class="nt">--tag</span> <span class="nt">--halt</span> soon,fail<span class="o">=</span>1 <span class="se">\</span>
try_pass ::: <span class="o">{</span>a..z<span class="o">}</span> ::: <span class="s1">''</span> <span class="o">{</span>a..z<span class="o">}</span> ::: <span class="s1">''</span> <span class="o">{</span>a..z<span class="o">}</span> <span class="se">\</span>
<span class="o">></span> getpass.out
</code></pre></div></div>
<blockquote>
<p><strong>xargs</strong> on macOS has less options than its GNU variant.
<strong>parallel</strong> is more convenient, but requires
<a href="https://brew.sh">Homebrew</a> installation:
<code class="language-plaintext highlighter-rouge">brew install parallel</code>, help available at <code class="language-plaintext highlighter-rouge">man parallel_tutorial</code>.</p>
</blockquote>
<p>The only thing left is run it and wait until finished and check
<code class="language-plaintext highlighter-rouge">getpass.out</code> file for the extraction password.</p>TimothyIf you backup your iPhone or iPad onto your iMac via iTunes you probably use encrypted backups. Sometimes it happens that you forget your encryption password. As it turns out there is no way to disable backups or change their password without knowing the original password. See Apple support article About encrypted backups in iTunes. There are several paid solutions that can decrypt your password that could be run inside a Windows virtual machine. But there is little guarantee that they would not share all your data through the Internet. So, the obvious solution is to use a command-line utility that does it for free. Time to try one password is roughly a minute per core on my 2014 iMac, so this solution does not work if really don’t remember your password.Taking Your LAN Network under Control in the Presence of IoT Devices2017-04-18T00:00:00-07:002017-04-18T00:00:00-07:00https://timothybasanov.com/2017/04/18/taking-iot-under-control-with-firewall<p>Using Internet of Things devices like Wi-Fi-connected light bulbs, switches,
motion detectors and such poses a challenge to secure your LAN. Devices’
firmware usually is of a pretty bad quality with administrator password
hard-coded and exploits never patched. At the same time these devices heavily
rely on zero-configuration protocols for ease of discovery, and these protocols
were never developed with security in mind.</p>
<p>I’m trying to reconcile these inherently insecure devices working with
inherently insecure protocols by using a specially configured Linux-based
firewall. I’ll guide you through my thought process to show how to make
everything work seamlessly, but (more) secure at the same time.</p>
<pre><code class="language-txt"> Internet
|
(ETH00) ====== Router (192.168.0.1) ====== (192.168.0.X)
| | Secured Wi-Fi/Eth Network
Firewall |
| | | (172.16.0.0)
| | | Guest Wi-Fi Network
| | (ETH01)
| | (192.168.0.X)
| | DeviceType0
| |
| (WLAN01)
| (192.168.0.X)
| DeviceType1
|
(WLAN00)
(192.168.0.X)
DeviceType2
</code></pre>
<!--more-->
<h2 id="overview">Overview</h2>
<ul>
<li>All trusted or <em>Secured</em> devices are directly connected to the main <em>Router</em>
connected to the <em>Internet</em> and are not affected by the setup.
(Example: owner’s laptops and phones that have complete implicit trust are
on the <em>Secured</em> network)</li>
<li>All completely untrusted or <em>Guest</em> devices devices live on a separate
subnetwork and don’t pass any traffic to the <em>Secured</em> network.
This is how Apple AirPort Guest Network works out of the box.
(Example: A friend’s laptop is on the <em>Guest</em> network does not see any other
devices)</li>
<li>All partially trusted or <em>DeviceTypeX</em> devices are connected through the
<em>Firewall</em> and can only respond to requests from devices from Secured
network.
<em>Secured</em> network devices see <em>DeviceTypeX</em> devices, but not in reverse.
Devices of the same Type can interact with each other.
(Example: iPhone on the <em>Secured</em> network can see Belkin WeMo switch on
<em>DeviceType1</em>
newtork, but not in reverse; or Belkin WeMo switch can talk to detector
on the same <em>DeviceType1</em> network, but can not talk to the HP Printer on
<em>DeviceType2</em> network).</li>
</ul>
<h3 id="firewall-network-interfaces">Firewall Network Interfaces</h3>
<p>Firewall has these network interfaces:</p>
<ul>
<li>ETH00: wired, connects to Router to the Secured network</li>
<li>ETH01, ETH02, ETH03: wired, each connect to a specific DeviceType network</li>
<li>WLAN00, WLAN01, WLAN02, WLAN03: 4 different Wi-Fi networks sharing the same
channel and the same network card</li>
<li>BRIDGE00: Bridge across <em>all</em> network interfaces. This is the interface
all <code class="language-plaintext highlighter-rouge">FORWARD</code> rules would be applied against</li>
</ul>
<h2 id="hardware-used">Hardware Used</h2>
<h3 id="pc-soekris-net6501">PC: <a href="http://soekris.com/products/net6501-1.html">Soekris net6501</a></h3>
<ul>
<li>Intel Atom CPU 600MHz-1.6GHz</li>
<li>512MB-2048MB RAM</li>
<li>4x Intel 82574L Gigabit Ethernet ports</li>
<li>RS-232 Serial port for BIOS and OS boot control</li>
<li>PCIe slot for extensions</li>
<li>Size: 11.4”W x 6.11”D x 1.41”H</li>
<li>Passive cooling</li>
</ul>
<p>US-made, small box, passive cooling, good networking and relatively cheap.</p>
<p>If you’re using macOS, I’d recommend to buy any FTDI RS-232 USB cable
and <a href="https://itunes.apple.com/us/app/serial/id877615577?mt=12">Serial App</a>
to simplify configuration over a serial port.</p>
<h3 id="wi-fi-tp-link-n900">Wi-Fi: <a href="http://www.tp-link.com/us/products/details/cat-5519_TL-WDN4800.html">TP-Link N900</a></h3>
<ul>
<li>Supports Wi-Fi 802.11n, 2.4GHz and 5GHz</li>
<li>3x3 antenna array</li>
<li>Access point: 4 different SSID on the same channel</li>
</ul>
<p>Really good <a href="https://wikidevi.com/wiki/TP-LINK_TL-WDN4800">Linux support</a>,
relatively fast, cheap.</p>
<h2 id="software-used">Software Used</h2>
<h3 id="zeroshell"><a href="http://www.zeroshell.org">ZeroShell</a></h3>
<ul>
<li>Small, simple Linux-based firewall/router</li>
<li>Web- and SSH-based interfaces, no X11</li>
<li>Firewall: full iptables support</li>
<li>Wi-Fi: access point w/ many different SSID on the same channel</li>
</ul>
<p>Simple, ideal for network appliance like Soekris.</p>
<p>Configuration instructions:</p>
<ul>
<li>Create all Wi-Fi networks from <em>RS-232 | Wi-Fi manager</em>, you can have
up to 4 on the same channel</li>
<li>Create <em>Setup | Network | New BRIDGE</em> with all interfaces: <code class="language-plaintext highlighter-rouge">BRIDGE00</code></li>
</ul>
<blockquote>
<p>Another distribution I evaluated was pfSense. pfSense 2.3 is built on top of
FreeBSD 10 and does not support my Wi-Fi card of choice: TL-WDN4800.
pfSense 2.4 built on top of FreeBSD 11 is in beta test as of April 2017
and only supports amd64, which requires kernel recompilation to work on
<a href="http://wiki.soekris.info/Installing_FreeBSD#net6501">Soekris net6501</a>.</p>
<p>pfSense 2.3 installation instructions:</p>
<ul>
<li>Text on the console would be garbled, press <code class="language-plaintext highlighter-rouge">3</code> to get into a command line
interface of FreeBSD loader</li>
<li>Type
<a href="https://www.freebsd.org/doc/en/books/handbook/serialconsole-setup.html"><code class="language-plaintext highlighter-rouge">set console=comconsole</code></a>
to fix console. You should see <code class="language-plaintext highlighter-rouge">OK</code></li>
<li>Type
<a href="https://puck.nether.net/~majdi/ntp/"><code class="language-plaintext highlighter-rouge">set hint.acpi.0.disabled=1</code></a>
to disable ACPI. Otherwise boot will hang without any log messages</li>
<li>Type <code class="language-plaintext highlighter-rouge">boot -v</code> to boot the loader and install as usual</li>
</ul>
</blockquote>
<h4 id="ipv6">IPv6</h4>
<p>ZeroShell allows all IPv6 traffic by default and there is no configuration.
Simplest way to deal with it is to drop it all.</p>
<p>Add a new script: <em>Setup | Scripts/Cron | NAT And Virtual Servers</em></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Disable IPv6 support: routing and all, ZeroShell does not do it right by default</span>
<span class="nb">echo</span> <span class="nt">-n</span> <span class="s2">"[Disable IPv6]: "</span>
ip6tables <span class="nt">-P</span> FORWARD DROP <span class="se">\</span>
<span class="o">&&</span> sysctl <span class="nt">-w</span> net.ipv6.conf.all.disable_ipv6<span class="o">=</span>1 <span class="se">\</span>
<span class="o">&&</span> sysctl <span class="nt">-w</span> net.ipv6.conf.default.disable_ipv6<span class="o">=</span>1 <span class="se">\</span>
<span class="o">&&</span> sysctl <span class="nt">-w</span> net.ipv6.conf.lo.disable_ipv6<span class="o">=</span>1 <span class="se">\</span>
<span class="o">&&</span> <span class="nb">echo </span>SUCCESS <span class="o">||</span> <span class="nb">echo </span>FAILED
</code></pre></div></div>
<h2 id="zeroshell-firewall-rules">ZeroShell Firewall Rules</h2>
<p>All these rules are configured using ZeroShell Firewall.
<em>Firewall</em> strings represent how the rule <em>looks</em> like in ZeroShell Web UI.
I also provide
relevant <em>iptables</em> rules to make sure you can re-implement them in any
other Linux distribution.</p>
<p>All rules apply to <em>FORWARD</em> chain and bridged packets only.</p>
<h4 id="1-drop-invalid-packets">1. Drop invalid packets</h4>
<blockquote>
<p>Technically speaking this rule is not required, but why not?</p>
</blockquote>
<ul>
<li>Connection State: <em>INVALID</em></li>
<li>ACTION: <em>DROP</em></li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">DROP all opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-is-bridged state INVALID /* Drop invalid packets */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -m physdev --physdev-is-bridged -m state --state INVALID -j DROP</code></p>
<h3 id="broadcast-protocols">Broadcast Protocols</h3>
<h4 id="2-allow-dhcp-requests-to-secure-network">2. Allow DHCP requests to <em>Secure</em> network</h4>
<blockquote>
<p>There is no good way to filter out DHCP requests as they are broadcasts</p>
</blockquote>
<ul>
<li>Output: <em>ETH00</em></li>
<li>Protocol Matching: <em>UDP</em></li>
<li>Source Port: <code class="language-plaintext highlighter-rouge">68</code></li>
<li>Dest. Port: <code class="language-plaintext highlighter-rouge">67</code></li>
<li>IPTABLES: <code class="language-plaintext highlighter-rouge">-m pkttype --pkt-type broadcast</code></li>
<li>ACTION: <em>ACCEPT</em></li>
<li>LOG</li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT udp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-out ETH00 --physdev-is-bridged udp spt:68 dpt:67 PKTTYPE = broadcast /* Allow DHCP requests to Secure network */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -p udp -m physdev --physdev-out ETH00 --physdev-is-bridged -m udp --sport 68 --dport 67 -m pkttype --pkt-type broadcast -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/002"</code></p>
<h4 id="3-drop-the-rest-of-the-broadcast">3. Drop the rest of the Broadcast</h4>
<blockquote>
<p>I’m not sure how ARP is handled, but it looks like it just works</p>
</blockquote>
<ul>
<li>IPTABLES: <code class="language-plaintext highlighter-rouge">-m pkttype --pkt-type broadcast</code></li>
<li>ACTION: <em>DROP</em></li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">DROP 4 opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-is-bridged PKTTYPE = broadcast /* Drop the rest of broadcast (IP w/o ARP) */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -p ip -m physdev --physdev-is-bridged -m pkttype --pkt-type broadcast -j DROP</code></p>
<h3 id="multicast-zeroconf-protocols">Multicast ZeroConf Protocols</h3>
<p>ZeroConf protocols rely on Multicast IP messages to find devices
available nearby. Two specific protocols I support are
<a href="https://en.wikipedia.org/wiki/Multicast_DNS">mDNS</a> and
<a href="https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol">SSDP</a>.</p>
<p>These protocols are very different from a normal TCP/UDP Unicast as <em>iptables</em>
does not know if that’s <em>ESTABLISHED</em> or <em>NEW</em> connection. And we only allow
<em>requests</em> from <em>Secured</em> network to <em>DeviceTypeX</em> networks, and only
<em>responses</em> from <em>DeviceTypeX</em> to <em>Secured</em> network.</p>
<h4 id="4-allow-mdns-requests-from-secured-network">4. Allow mDNS requests from <em>Secured</em> network</h4>
<blockquote>
<p>mDNS search request may contain several requests in the same packet. Any device
may potentially respond if it knows something:</p>
<ul>
<li>UDP Multicast to <code class="language-plaintext highlighter-rouge">224.0.0.251:5353</code></li>
<li>Binary UDP data</li>
<li>UDP data 17th bit is 0 (3rd byte highest bit)</li>
</ul>
</blockquote>
<ul>
<li>Input: <em>ETH00</em></li>
<li>Fragments: Not match second and further</li>
<li>IPTABLES: <code class="language-plaintext highlighter-rouge">-m u32 --u32 0>>22&0x3C@8>>15&1=0</code></li>
<li>nDPI: mdns</li>
<li>ACTION: <em>ACCEPT</em></li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT all opt !f in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-in ETH00 --physdev-is-bridged ndpi protocol mdns u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x0" /* Allow mDNS requests from Secured network (UDP payload, 3rd byte, first bit is 0) */ </code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD ! -f -m physdev --physdev-in ETH00 --physdev-is-bridged -m ndpi --mdns -m u32 --u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x0" -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/004"</code></p>
<h4 id="5-allow-mdns-responses-to-secured-network">5. Allow mDNS responses to <em>Secured</em> network</h4>
<blockquote>
<p>mDNS search response may contain several responses in the same packet.
Any device may potentially respond if it knows something:</p>
<ul>
<li>UDP Multicast to <code class="language-plaintext highlighter-rouge">224.0.0.251:5353</code></li>
<li>Binary UDP data</li>
<li>UDP data 17th bit is 0 (3rd byte highest bit)</li>
</ul>
</blockquote>
<ul>
<li>Output: <em>ETH00</em></li>
<li>Fragments: Not match second and further</li>
<li>IPTABLES: <code class="language-plaintext highlighter-rouge">-m u32 --u32 0>>22&0x3C@8>>15&1=1</code></li>
<li>nDPI: mdns</li>
<li>ACTION: <em>ACCEPT</em></li>
<li>LOG</li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT all opt !f in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-out ETH00 --physdev-is-bridged ndpi protocol mdns u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x1" /* Allow mDNS responses to Secured network (UDP payload, 3rd byte, first bit is 1) */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD ! -f -m physdev --physdev-in ETH00 --physdev-is-bridged -m ndpi --mdns -m u32 --u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x0" -j ACCEPT</code></p>
<h4 id="6-allow-ssdp-requests-from-secured-network">6. Allow SSDP requests from <em>Secured</em> network</h4>
<blockquote>
<p>SSDP search request uses
<a href="https://en.wikipedia.org/wiki/Universal_Plug_and_Play#Protocol">HTTPU</a>
as a transport layer:</p>
<ul>
<li>UDP Multicast to <code class="language-plaintext highlighter-rouge">239.255.255.250:1900</code></li>
<li>From <code class="language-plaintext highlighter-rouge">A.B.C.D:N</code></li>
<li>Text UDP data</li>
<li>Starts with <code class="language-plaintext highlighter-rouge">M-SEARCH *</code></li>
</ul>
</blockquote>
<ul>
<li>Output: <em>ETH00</em></li>
<li>Fragments: Not match second and further</li>
<li>IPTABLES: <code class="language-plaintext highlighter-rouge">-m string --algo bm --to 0x40 --string M-SEARCH</code></li>
<li>nDPI: ssdp</li>
<li>ACTION: <em>ACCEPT</em></li>
<li>LOG</li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT all opt !f in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-out ETH00 --physdev-is-bridgedprotocol SSDP STRING match "M-SEARCH" ALGO name bm TO 64 /* Allow SSDP requests from Secured network (has M-SEARCH string) */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD ! -f -m physdev --physdev-in ETH00 --physdev-is-bridged -m ndpi --ssdp -m string --string "M-SEARCH" --algo bm --to 64 -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/006"</code></p>
<h4 id="7-allow-ssdp-responses-to-secured-network">7. Allow SSDP responses to <em>Secured</em> network</h4>
<blockquote>
<p>SSDP search response uses
<a href="https://en.wikipedia.org/wiki/Universal_Plug_and_Play#Protocol">HTTPU</a>
as a transport layer:</p>
<ul>
<li>UDP Unicast to <code class="language-plaintext highlighter-rouge">A.B.C.D:N</code></li>
<li>Text UDP data</li>
<li>Starts with <code class="language-plaintext highlighter-rouge">HTTP/1.1 200 OK</code></li>
</ul>
</blockquote>
<blockquote>
<p>SSDP responses uses <em>Unicast</em> and not <em>Multicast</em>. And destination
port depends on the original SSDP request port number</p>
</blockquote>
<blockquote>
<p>We ignore SSDP notifications (essentially broadcast messages):</p>
<ul>
<li>UDP Multicast to <code class="language-plaintext highlighter-rouge">239.255.255.250:1900</code></li>
<li>Text UDP data</li>
<li>Starts with <code class="language-plaintext highlighter-rouge">NOTIFY *</code></li>
</ul>
</blockquote>
<ul>
<li>Output: <em>ETH00</em></li>
<li>Fragments: Not match second and further</li>
<li>Protocol Matching: <em>UDP</em></li>
<li>IPTABLES: <code class="language-plaintext highlighter-rouge">-m string --algo bm --to 0x40 --string HTTP/1.1</code></li>
<li>nDPI: ssdp</li>
<li>ACTION: <em>ACCEPT</em></li>
<li>LOG</li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT udp opt !f in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-out ETH00 --physdev-is-bridged STRING match "HTTP/1.1" ALGO name bm TO 64 /* Allow SSDP responses to Secured network (UDP unicast) */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -p udp ! -f -m physdev --physdev-out ETH00 --physdev-is-bridged -m string --string "HTTP/1.1" --algo bm --to 64 -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/007"</code></p>
<h4 id="8-drop-the-rest-of-the-multicast">8. Drop the rest of the Multicast</h4>
<ul>
<li>IPTABLES: <code class="language-plaintext highlighter-rouge">-m pkttype --pkt-type multicast</code></li>
<li>ACTION: <em>DROP</em></li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">DROP all opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-is-bridged PKTTYPE = multicast /* Drop the rest of multicast */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -m physdev --physdev-is-bridged -m pkttype --pkt-type multicast -j DROP</code></p>
<h3 id="unicast-network-protocols">Unicast Network Protocols</h3>
<p>There are many Unicast protocols that we should take into account. Rules of
thumb:</p>
<ul>
<li>Connections originating from <em>Secured</em> network are allowed</li>
<li>Responses to a connection from <em>Secured</em> network are allowed</li>
<li>Connections to <em>Router</em> are usually allowed</li>
<li>Connections to <em>Internet</em> are always allowed</li>
</ul>
<h4 id="9-allow-all-traffic-from-secured-network">9. Allow all traffic from <em>Secured</em> network</h4>
<ul>
<li>Input: ETH00</li>
<li>ACTION: <em>ACCEPT</em></li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT all opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-in ETH00 --physdev-is-bridged /* Allow all traffic from Secured network */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -m physdev --physdev-in ETH00 --physdev-is-bridged -j ACCEPT</code></p>
<h4 id="10-allow-all-established-and-related-packets">10. Allow all established and related packets</h4>
<ul>
<li>Output: <em>ETH00</em></li>
<li>Connection state: ESTABLISHED, RELATED</li>
<li>ACITON: ACCEPT</li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT all opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 PHYSDEV match --physdev-out ETH00 --physdev-is-bridged state RELATED,ESTABLISHED /* Allow all established and related packets */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -m physdev --physdev-out ETH00 --physdev-is-bridged -m state --state RELATED,ESTABLISHED -j ACCEPT</code></p>
<h4 id="11-allow-an-icmp-ping-traffic-to-router">11. Allow an ICMP ping traffic to <em>Router</em></h4>
<blockquote>
<p>Some devices rely on ping to the gateway to verify a connection status</p>
</blockquote>
<blockquote>
<p>Router address is hardcoded here to 192.168.0.1</p>
</blockquote>
<ul>
<li>Output: <em>ETH00</em></li>
<li>Destination IP: 192.168.0.1</li>
<li>Protocol Matching: ICMP</li>
<li>ICMP Type: echo-request (ping)</li>
<li>ACTION: <em>ACCEPT</em></li>
<li>LOG</li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT icmp opt -- in * out * 0.0.0.0/0 -> 192.168.0.1 PHYSDEV match --physdev-out ETH00 --physdev-is-bridged icmptype 8 /* Allow an ICMP ping traffic to AirPort */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -d 192.168.0.1/32 -p icmp -m physdev --physdev-out ETH00 --physdev-is-bridged -m icmp --icmp-type 8 -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/011"</code></p>
<h4 id="12-allow-any-udp-traffic-to-router-dhcp-dns-etc">12. Allow any UDP traffic to <em>Router</em> (DHCP, DNS etc.)</h4>
<blockquote>
<p>Router address is hardcoded here to 192.168.0.1</p>
</blockquote>
<ul>
<li>Output: <em>ETH00</em></li>
<li>Destination IP: 192.168.0.1</li>
<li>Protocol Matching: <em>UDP</em></li>
<li>ACTION: <em>ACCEPT</em></li>
<li>LOG</li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT udp opt -- in * out * 0.0.0.0/0 -> 192.168.0.1 PHYSDEV match --physdev-out ETH00 --physdev-is-bridged /* Allow any UDP traffic to AirPort (DHCP, DNS etc.) */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD -d 192.168.0.1/32 -p udp -m physdev --physdev-out ETH00 --physdev-is-bridged -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/012"</code></p>
<h4 id="13-allow-any-traffic-outside-of-lan-network">13. Allow any traffic outside of LAN network</h4>
<blockquote>
<p>We rely on our LAN to be limited to 192.168.0.0/16 network</p>
</blockquote>
<ul>
<li>Output: <em>ETH00</em></li>
<li>Destination IP: NOT 192.168.0.0/16</li>
<li>ACTION: <em>ACCEPT</em></li>
</ul>
<p>Firewall: <code class="language-plaintext highlighter-rouge">ACCEPT all opt -- in * out * 0.0.0.0/0 !-> 192.168.0.0/16 PHYSDEV match --physdev-out ETH00 --physdev-is-bridged /* Allow any traffic outside of LAN network */</code></p>
<p>iptables: <code class="language-plaintext highlighter-rouge">-A FORWARD ! -d 192.168.0.0/16 -m physdev --physdev-out ETH00 --physdev-is-bridged -j ACCEPT</code></p>
<h4 id="14-drop-by-default">14. DROP by default</h4>
<ul>
<li>Policy: DROP</li>
</ul>
<p>iptables: <code class="language-plaintext highlighter-rouge">-P FORWARD DROP</code></p>
<h2 id="end-result-iptables-rules">End result: <em>iptables</em> rules</h2>
<p>Here is an end result in form of <em>iptables</em> rules which you can use directly
if desired.</p>
<p><code class="language-plaintext highlighter-rouge">iptables -S</code></p>
<pre><code class="language-txt">-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
-N NetBalancer
-N SYS_DNS
-N SYS_GUI
-N SYS_HTTPS
-N SYS_INPUT
-N SYS_OUTPUT
-N SYS_SSH
-A INPUT -j SYS_GUI
-A INPUT -j SYS_INPUT
-A INPUT -p tcp -m tcp --dport 80 -j SYS_HTTPS
-A INPUT -p tcp -m tcp --dport 443 -j SYS_HTTPS
-A INPUT -p tcp -m tcp --dport 22 -j SYS_SSH
-A FORWARD -m physdev --physdev-is-bridged -m state --state INVALID -j DROP
-A FORWARD -p udp -m physdev --physdev-out ETH00 --physdev-is-bridged -m udp --sport 68 --dport 67 -m pkttype --pkt-type broadcast -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/002"
-A FORWARD -p udp -m physdev --physdev-out ETH00 --physdev-is-bridged -m udp --sport 68 --dport 67 -m pkttype --pkt-type broadcast -j ACCEPT
-A FORWARD -p ip -m physdev --physdev-is-bridged -m pkttype --pkt-type broadcast -j DROP
-A FORWARD ! -f -m physdev --physdev-in ETH00 --physdev-is-bridged -m ndpi --mdns -m u32 --u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x0" -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/004"
-A FORWARD ! -f -m physdev --physdev-in ETH00 --physdev-is-bridged -m ndpi --mdns -m u32 --u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x0" -j ACCEPT
-A FORWARD ! -f -m physdev --physdev-out ETH00 --physdev-is-bridged -m ndpi --mdns -m u32 --u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x1" -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/005"
-A FORWARD ! -f -m physdev --physdev-out ETH00 --physdev-is-bridged -m ndpi --mdns -m u32 --u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x1" -j ACCEPT
-A FORWARD ! -f -m physdev --physdev-in ETH00 --physdev-is-bridged -m ndpi --ssdp -m string --string "M-SEARCH" --algo bm --to 64 -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/006"
-A FORWARD ! -f -m physdev --physdev-in ETH00 --physdev-is-bridged -m ndpi --ssdp -m string --string "M-SEARCH" --algo bm --to 64 -j ACCEPT
-A FORWARD -p udp ! -f -m physdev --physdev-out ETH00 --physdev-is-bridged -m string --string "HTTP/1.1" --algo bm --to 64 -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/007"
-A FORWARD -p udp ! -f -m physdev --physdev-out ETH00 --physdev-is-bridged -m string --string "HTTP/1.1" --algo bm --to 64 -j ACCEPT
-A FORWARD -m physdev --physdev-is-bridged -m pkttype --pkt-type multicast -j DROP
-A FORWARD -m physdev --physdev-in ETH00 --physdev-is-bridged -j ACCEPT
-A FORWARD -m physdev --physdev-out ETH00 --physdev-is-bridged -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -d 192.168.0.1/32 -p icmp -m physdev --physdev-out ETH00 --physdev-is-bridged -m icmp --icmp-type 8 -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/011"
-A FORWARD -d 192.168.0.1/32 -p icmp -m physdev --physdev-out ETH00 --physdev-is-bridged -m icmp --icmp-type 8 -j ACCEPT
-A FORWARD -d 192.168.0.1/32 -p udp -m physdev --physdev-out ETH00 --physdev-is-bridged -m limit --limit 10/min --limit-burst 15 -j LOG --log-prefix "FORWARD/012"
-A FORWARD -d 192.168.0.1/32 -p udp -m physdev --physdev-out ETH00 --physdev-is-bridged -j ACCEPT
-A FORWARD ! -d 192.168.0.0/16 -m physdev --physdev-out ETH00 --physdev-is-bridged -j ACCEPT
-A OUTPUT -j SYS_OUTPUT
-A SYS_DNS -s 10.0.0.0/8 -j ACCEPT
-A SYS_DNS -s 172.16.0.0/12 -j ACCEPT
-A SYS_DNS -s 192.168.0.0/16 -j ACCEPT
-A SYS_DNS -s 192.168.0.0/24 -j ACCEPT
-A SYS_DNS -s 192.168.0.0/24 -j ACCEPT
-A SYS_DNS -s 192.168.250.0/24 -j ACCEPT
-A SYS_DNS -j DROP
-A SYS_GUI -s 192.168.0.2/32 -p tcp -m tcp --dport 12081 -j ACCEPT
-A SYS_HTTPS -i lo -j ACCEPT
-A SYS_HTTPS -m physdev --physdev-in ETH00 -j ACCEPT
-A SYS_HTTPS -j DROP
-A SYS_INPUT -i lo -j ACCEPT
-A SYS_INPUT -p udp -m udp --sport 53 -m state --state ESTABLISHED -j ACCEPT
-A SYS_INPUT -p tcp -m tcp --sport 53 -m state --state ESTABLISHED -j ACCEPT
-A SYS_INPUT -p udp -m udp --dport 53 -j SYS_DNS
-A SYS_INPUT -p tcp -m tcp --dport 53 -j SYS_DNS
-A SYS_INPUT -p tcp -m tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
-A SYS_INPUT -p tcp -m tcp --sport 8245 -m state --state ESTABLISHED -j ACCEPT
-A SYS_INPUT -p udp -m udp --sport 123 -m state --state ESTABLISHED -j ACCEPT
-A SYS_INPUT -j RETURN
-A SYS_OUTPUT -o lo -j ACCEPT
-A SYS_OUTPUT -p udp -m udp --dport 53 -j ACCEPT
-A SYS_OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A SYS_OUTPUT -p tcp -m tcp --dport 8245 -j ACCEPT
-A SYS_OUTPUT -p udp -m udp --dport 123 -j ACCEPT
-A SYS_OUTPUT -j RETURN
-A SYS_SSH -i lo -j ACCEPT
-A SYS_SSH -m physdev --physdev-in ETH00 -j ACCEPT
-A SYS_SSH -j DROP
</code></pre>
<p><code class="language-plaintext highlighter-rouge">ip6tables -S</code></p>
<pre><code class="language-txt">-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
</code></pre>TimothyUsing Internet of Things devices like Wi-Fi-connected light bulbs, switches, motion detectors and such poses a challenge to secure your LAN. Devices’ firmware usually is of a pretty bad quality with administrator password hard-coded and exploits never patched. At the same time these devices heavily rely on zero-configuration protocols for ease of discovery, and these protocols were never developed with security in mind. I’m trying to reconcile these inherently insecure devices working with inherently insecure protocols by using a specially configured Linux-based firewall. I’ll guide you through my thought process to show how to make everything work seamlessly, but (more) secure at the same time. Internet | (ETH00) ====== Router (192.168.0.1) ====== (192.168.0.X) | | Secured Wi-Fi/Eth Network Firewall | | | | (172.16.0.0) | | | Guest Wi-Fi Network | | (ETH01) | | (192.168.0.X) | | DeviceType0 | | | (WLAN01) | (192.168.0.X) | DeviceType1 | (WLAN00) (192.168.0.X) DeviceType2Decoding any Java-originated SSL Connection with Pre-Shared Master Secret2016-05-26T00:00:00-07:002016-05-26T00:00:00-07:00https://timothybasanov.com/2016/05/26/java-pre-master-secret<p>When you try to decode SSL connection from your Java application for a purpose of testing you usually can go by using a known specified server certificate.</p>
<p>But, when Diffie-Hellman key exchange is used or when server-side certificate is outside of your control you still may decrypt the connection as long as you have access to startup command-line parameters of your Java application.</p>
<p>Basic idea is to specify <code class="language-plaintext highlighter-rouge">-Djavax.net.debug=ssl,keygen</code> during Java start up, and export all pre-shared master secret keys to a log file. Log file is read by Wireshark or similar application to aid decoding of SSL traffic.</p>
<!--more-->
<blockquote>
<p>More info on <code class="language-plaintext highlighter-rouge">javax.net.debug</code> parameter: <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#Debug">http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#Debug</a></p>
</blockquote>
<h2 id="enabling-the-property-for-debug-output">Enabling the property for debug output</h2>
<p>There will be <em>a lot</em> of output. You will have a separate session key and per-connection keys as well.</p>
<h3 id="first-connection-sample">First connection sample</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*** ServerHelloDone
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1.2
main, WRITE: TLSv1.2 Handshake, length = 262
SESSION KEYGEN:
PreMaster Secret:
0000: 03 03 B1 49 3C F1 16 FF 3D 33 D0 D5 F2 46 CB E1 ...I<...=3...F..
0010: 68 BF DF 26 B7 74 EA B5 7D E7 8D E3 FF BB 8B 90 h..&.t..........
0020: E8 03 A0 CC 5F F3 5D BA BD BB 42 E6 05 C6 36 3B ...._.]...B...6;
CONNECTION KEYGEN:
Client Nonce:
0000: 57 3D 06 80 5D 59 A4 12 D2 24 05 56 9E 12 73 61 W=..]Y...$.V..sa
0010: 59 29 52 A5 07 1A 8E 43 1F E1 93 BF D4 50 C6 86 Y)R....C.....P..
Server Nonce:
0000: 57 3D 06 80 B0 E3 8F 7A 1E 12 DE D3 B7 65 AC 29 W=.....z.....e.)
0010: 44 66 C9 65 FD A8 53 B0 CA 7A CE D0 16 BD 5E F0 Df.e..S..z....^.
Master Secret:
0000: BA 05 35 3B EF B6 54 7B 6E F4 93 5A DE E7 CD 12 ..5;..T.n..Z....
0010: 43 E6 39 C9 FA A5 03 E0 1C 01 6D 67 2A 62 2E B0 C.9.......mg*b..
0020: 86 3C E0 5F 1C 4C B4 DE 22 38 7B BF F3 62 64 70 .<._.L.."8...bdp
Client MAC write Secret:
0000: 65 D5 C0 FF 59 E0 29 B4 54 98 3B 59 93 25 5D F8 e...Y.).T.;Y.%].
0010: D9 63 AB 91 .c..
Server MAC write Secret:
0000: 1A 87 29 D9 57 92 80 8E 73 B6 88 7E C8 5A C3 11 ..).W...s....Z..
0010: 0F E6 E6 40 ...@
Client write key:
0000: 72 2C 10 1D 75 F8 D8 35 86 99 CE 66 36 3D 63 A5 r,..u..5...f6=c.
0010: C1 01 52 4D EC 13 1E 91 ..RM....
Server write key:
0000: CC 91 06 BB F0 E2 D4 64 48 68 26 DE D8 B7 0B EA .......dHh&.....
0010: B7 B6 B0 1A 61 0A A6 C8 ....a...
... no IV derived for this protocol
main, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
</code></pre></div></div>
<h3 id="second-connection-sample">Second connection sample</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>***
CONNECTION KEYGEN:
Client Nonce:
0000: 57 3D 1A E1 B4 A1 F2 6C D1 5F D4 BB DF 90 7B 88 W=.....l._......
0010: DD 52 57 6F 76 E9 5E D0 75 91 03 FB 19 31 8A 1B .RWov.^.u....1..
Server Nonce:
0000: 57 3D 1A E1 15 25 D8 7C B1 1F DD E5 C1 D1 F2 75 W=...%.........u
0010: 21 7E 92 B9 3D F6 8A 87 5F 46 CE 61 F8 17 25 BD !...=..._F.a..%.
Master Secret:
0000: D9 17 9B 11 2F B8 9D 0D 0A 42 C7 34 0E 4A 0B 4B ..../....B.4.J.K
0010: 2F 94 F1 CC C0 63 93 19 17 03 D4 9A E8 03 6F D8 /....c........o.
0020: D2 66 CF D1 4E E2 8E 3F E6 9D 41 3D E9 26 CD EB .f..N..?..A=.&..
Client MAC write Secret:
0000: 03 D3 D2 48 A8 5F FD 40 B5 86 30 5A 9B 0A 13 89 ...H._.@..0Z....
0010: EC 78 B0 B6 A1 0E B7 0E 82 7B 50 B7 15 10 EA 60 .x........P....`
Server MAC write Secret:
0000: E7 E4 BC 3E 87 12 D7 2B C6 47 2F 62 4F 49 FF 55 ...>...+.G/bOI.U
0010: 41 23 69 B2 13 B8 8B 24 0E 75 B0 06 FB 13 91 B4 A#i....$.u......
Client write key:
0000: 30 72 FA 01 95 80 88 D1 D8 99 A3 5C 6E A5 2B 0D 0r.........\n.+.
Server write key:
0000: 0D 7C D7 2E 68 2A 31 E2 C6 8E A6 B9 85 00 FB E5 ....h*1.........
... no IV derived for this protocol
</code></pre></div></div>
<h2 id="extracting-keys-to-a-log-file-output-example">Extracting keys to a log file, output example</h2>
<p>Here is an example python script I’ve written to decode relevant bits: <code class="language-plaintext highlighter-rouge">extract-pre-master-secret</code></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">##!/usr/bin/env python
</span><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="k">def</span> <span class="nf">extract_data_from_line</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="s">'</span><span class="err">\</span><span class="s">d+:([ 0-9A-F]{51}) .*'</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
<span class="k">if</span> <span class="n">m</span><span class="p">:</span>
<span class="k">return</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s">' '</span><span class="p">,</span> <span class="s">''</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">line</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">parsing_mastersecret_line</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">parsing_clientnonce_line</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdin</span><span class="p">:</span>
<span class="k">if</span> <span class="n">parsing_mastersecret_line</span><span class="p">:</span>
<span class="n">parsing_mastersecret_line</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">parsing_clientnonce_line</span><span class="p">:</span>
<span class="n">parsing_clientnonce_line</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">line</span> <span class="o">==</span> <span class="s">'Client Nonce:</span><span class="se">\n</span><span class="s">'</span><span class="p">:</span>
<span class="n">parsing_clientnonce_line</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">cn</span> <span class="o">=</span> <span class="s">""</span>
<span class="k">if</span> <span class="mi">2</span> <span class="o"><=</span> <span class="n">parsing_clientnonce_line</span> <span class="o"><=</span> <span class="mi">3</span><span class="p">:</span>
<span class="n">cn</span> <span class="o">=</span> <span class="n">cn</span> <span class="o">+</span> <span class="n">extract_data_from_line</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="k">if</span> <span class="n">line</span> <span class="o">==</span> <span class="s">'Master Secret:</span><span class="se">\n</span><span class="s">'</span><span class="p">:</span>
<span class="n">parsing_mastersecret_line</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">ms</span> <span class="o">=</span> <span class="s">""</span>
<span class="k">if</span> <span class="mi">2</span> <span class="o"><=</span> <span class="n">parsing_mastersecret_line</span> <span class="o"><=</span> <span class="mi">4</span><span class="p">:</span>
<span class="n">ms</span> <span class="o">=</span> <span class="n">ms</span> <span class="o">+</span> <span class="n">extract_data_from_line</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="k">if</span> <span class="mi">5</span> <span class="o">==</span> <span class="n">parsing_mastersecret_line</span><span class="p">:</span>
<span class="k">print</span> <span class="s">'CLIENT_RANDOM'</span><span class="p">,</span> <span class="n">cn</span><span class="p">,</span> <span class="n">ms</span>
<span class="k">if</span> <span class="n">__name__</span><span class="o">==</span><span class="s">'__main__'</span><span class="p">:</span>
<span class="n">sys</span><span class="o">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></pre></div></div>
<blockquote>
<p>For more information on the output file format: <a href="http://security.stackexchange.com/questions/35639/decrypting-tls-in-wireshark-when-using-dhe-rsa-ciphersuites">http://security.stackexchange.com/questions/35639/decrypting-tls-in-wireshark-when-using-dhe-rsa-ciphersuites</a></p>
</blockquote>
<h3 id="example-script-output">Example script output</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CLIENT_RANDOM 573D06805D59A412D22405569E127361592952A5071A8E431FE193BFD450C686 BA05353BEFB6547B6EF4935ADEE7CD1243E639C9FAA503E01C016D672A622EB0863CE05F1C4CB4DE22387BBFF3626470
CLIENT_RANDOM 573D1AE1B4A1F26CD15FD4BBDF907B88DD52576F76E95ED0759103FB19318A1B D9179B112FB89D0D0A42C7340E4A0B4B2F94F1CCC06393191703D49AE8036FD8D266CFD14EE28E3FE69D413DE926CDEB
</code></pre></div></div>
<h3 id="example-command-line-ive-used">Example command line I’ve used:</h3>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mvn verify <span class="nt">-Djavax</span>.net.debug<span class="o">=</span>ssl,keygen <span class="se">\</span>
| <span class="nb">tee</span> <span class="o">>(</span>extract-pre-master-secret <span class="o">>></span> target/ssl/pre-shared-master-key.log<span class="o">)</span>
</code></pre></div></div>
<h2 id="decoding-ssl-via-wireshark">Decoding SSL via Wireshark</h2>
<p>Go to <em>Settings / Protocols / SSL</em> and set path to the log file generated by a script. Now it should automatically decode any SSL/TLS traffic captured from that Java application.</p>TimothyWhen you try to decode SSL connection from your Java application for a purpose of testing you usually can go by using a known specified server certificate. But, when Diffie-Hellman key exchange is used or when server-side certificate is outside of your control you still may decrypt the connection as long as you have access to startup command-line parameters of your Java application. Basic idea is to specify -Djavax.net.debug=ssl,keygen during Java start up, and export all pre-shared master secret keys to a log file. Log file is read by Wireshark or similar application to aid decoding of SSL traffic.Maven Integration Tests and Jetty with SSL Enabled2016-05-21T00:00:00-07:002016-05-21T00:00:00-07:00https://timothybasanov.com/2016/05/21/maven-tests-jetty-ssl<p>Running integration tests with <em>Jetty</em> using <em>maven</em> usually is plain and awesome. At least not until you try to enable <em>SSL</em>. And then suddenly everything goes to hell. As I have not found a definitive source of a simple working <em>Jetty</em> config and I’m sharing my findings.</p>
<p>To run integration tests with Jetty under maven with SSL you’ll need to</p>
<ul>
<li>Start and stop Jetty before and after integration tests</li>
<li>Generate <em>Jetty</em> SSL keys in a <em>PKCS12</em> format using <code class="language-plaintext highlighter-rouge">keytool</code></li>
<li>Config Jetty to enable SSL and use generated keys</li>
</ul>
<!--more-->
<h2 id="starting-and-stopping-jetty-before-and-after-integration-tests">Starting and stopping Jetty before and after integration tests</h2>
<p>This part is easy and is well known. Just add this to your <code class="language-plaintext highlighter-rouge">pom.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.eclipse.jetty<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>jetty-maven-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>9.3.8.v20160314<span class="nt"></version></span>
<span class="nt"><executions></span>
<span class="nt"><execution></span>
<span class="nt"><id></span>jetty-start<span class="nt"></id></span>
<span class="nt"><phase></span>pre-integration-test<span class="nt"></phase></span>
<span class="nt"><goals></span>
<span class="nt"><goal></span>start<span class="nt"></goal></span>
<span class="nt"></goals></span>
<span class="nt"></execution></span>
<span class="nt"><execution></span>
<span class="nt"><id></span>jetty-stop<span class="nt"></id></span>
<span class="nt"><phase></span>post-integration-test<span class="nt"></phase></span>
<span class="nt"><goals></span>
<span class="nt"><goal></span>stop<span class="nt"></goal></span>
<span class="nt"></goals></span>
<span class="nt"></execution></span>
<span class="nt"></executions></span>
<span class="nt"></plugin></span>
</code></pre></div></div>
<p>It executes <em>Jetty</em> plugin before and after integration tests to start and stop local <em>Jetty</em> instance.</p>
<h2 id="generating-jetty-ssl-keys-in-a-pkcs12-format-using-keytool">Generating <em>Jetty</em> SSL keys in a <em>PKCS12</em> format using <code class="language-plaintext highlighter-rouge">keytool</code></h2>
<blockquote>
<p>Using PKCS12 format for keystore allows you to load same keys in other programs like Wireshark.</p>
</blockquote>
<p>Use script to generate an RSA keypair to use for SSL <code class="language-plaintext highlighter-rouge">src/main/scripts/generate-integration-test-ssl-key</code>:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">##!/usr/bin/env bash</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> target
<span class="nb">cd </span>target
<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-f</span> ssl/jetty.key <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">mkdir</span> <span class="nt">-p</span> ssl
<span class="nb">cd </span>ssl
<span class="nb">yes</span> | keytool <span class="nt">-genkey</span> <span class="nt">-keyalg</span> RSA <span class="nt">-alias</span> jetty <span class="nt">-noprompt</span> <span class="se">\</span>
<span class="nt">-keystore</span> jetty.p12 <span class="nt">-storetype</span> pkcs12 <span class="se">\</span>
<span class="nt">-storepass</span> changeit <span class="nt">-keypass</span> changeit
<span class="k">fi</span>
</code></pre></div></div>
<p>You may run it once manually or automatically. To automate this script has to be run before <em>Jetty</em> is started, add this to your <code class="language-plaintext highlighter-rouge">pom.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><plugin></span>
<span class="c"><!-- Execute external shell scripts for integration tests --></span>
<span class="nt"><groupId></span>org.codehaus.mojo<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>exec-maven-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.5.0<span class="nt"></version></span>
<span class="nt"><executions></span>
<span class="c"><!-- Generate SSL keys --></span>
<span class="nt"><execution></span>
<span class="nt"><id></span>ssl-keys<span class="nt"></id></span>
<span class="nt"><phase></span>pre-integration-test<span class="nt"></phase></span>
<span class="nt"><goals></span>
<span class="nt"><goal></span>exec<span class="nt"></goal></span>
<span class="nt"></goals></span>
<span class="nt"><configuration></span>
<span class="nt"><executable></span>${build.scriptSourceDirectory}/generate-integration-test-ssl-key<span class="nt"></executable></span>
<span class="nt"></configuration></span>
<span class="nt"></execution></span>
<span class="nt"></executions></span>
<span class="nt"></plugin></span>
</code></pre></div></div>
<h2 id="configuring-jetty-to-enable-ssl-and-use-generated-keys">Configuring Jetty to enable SSL and use generated keys</h2>
<p>Recent versions of <em>Jetty</em> do not support <em>SSL/HTTPS</em> configuration through the <code class="language-plaintext highlighter-rouge">pom.xml</code>, it has to be a separate property file. Add this config to your <em>Jetty</em>’s section in <code class="language-plaintext highlighter-rouge">pom.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><configuration></span>
<span class="nt"><jettyXml></span>${project.basedir}/src/main/config/jetty-https.xml<span class="nt"></jettyXml></span>
<span class="nt"><systemProperties></span>
<span class="nt"><systemProperty></span>
<span class="nt"><name></span>jetty.ssl.keyStorePath<span class="nt"></name></span>
<span class="nt"><value></span>${project.build.directory}/ssl/jetty.p12<span class="nt"></value></span>
<span class="nt"></systemProperty></span>
<span class="nt"><systemProperty></span>
<span class="nt"><name></span>org.eclipse.jetty.ssl.password<span class="nt"></name></span>
<span class="nt"><value></span>changeit<span class="nt"></value></span>
<span class="nt"></systemProperty></span>
<span class="nt"></systemProperties></span>
<span class="nt"></configuration></span>
</code></pre></div></div>
<p>And here is the simplest <code class="language-plaintext highlighter-rouge">jetty.xml</code> possible that enables <em>SSL/HTTPS</em> <code class="language-plaintext highlighter-rouge">src/main/config/jetty-https.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0"?></span>
<span class="cp"><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"></span>
<span class="c"><!-- Simplest working Jetty SSL configuration serving on https://localhost:8443--></span>
<span class="nt"><Configure</span> <span class="na">id=</span><span class="s">"Server"</span> <span class="na">class=</span><span class="s">"org.eclipse.jetty.server.Server"</span><span class="nt">></span>
<span class="nt"><Call</span> <span class="na">name=</span><span class="s">"addConnector"</span><span class="nt">></span>
<span class="nt"><Arg></span>
<span class="c"><!-- Setting up a new connector that serves HTTPS --></span>
<span class="nt"><New</span> <span class="na">class=</span><span class="s">"org.eclipse.jetty.server.ServerConnector"</span><span class="nt">></span>
<span class="nt"><Arg</span> <span class="na">name=</span><span class="s">"server"</span><span class="nt">></span>
<span class="nt"><Ref</span> <span class="na">refid=</span><span class="s">"Server"</span><span class="nt">/></span>
<span class="nt"></Arg></span>
<span class="nt"><Arg</span> <span class="na">name=</span><span class="s">"factories"</span><span class="nt">></span>
<span class="nt"><Array</span> <span class="na">type=</span><span class="s">"org.eclipse.jetty.server.ConnectionFactory"</span><span class="nt">/></span>
<span class="nt"></Arg></span>
<span class="c"><!-- Port is either default 8443 or supplied via -Djetty.ssl.port=8443 --></span>
<span class="nt"><Set</span> <span class="na">name=</span><span class="s">"port"</span><span class="nt">></span>
<span class="nt"><Property</span> <span class="na">name=</span><span class="s">"jetty.ssl.port"</span> <span class="na">deprecated=</span><span class="s">"ssl.port"</span> <span class="na">default=</span><span class="s">"8443"</span><span class="nt">/></span>
<span class="nt"></Set></span>
<span class="nt"><Call</span> <span class="na">name=</span><span class="s">"addConnectionFactory"</span><span class="nt">></span>
<span class="nt"><Arg></span>
<span class="c"><!-- You need to setup SSL-specific connection factory --></span>
<span class="nt"><New</span> <span class="na">class=</span><span class="s">"org.eclipse.jetty.server.SslConnectionFactory"</span><span class="nt">></span>
<span class="nt"><Arg</span> <span class="na">name=</span><span class="s">"next"</span><span class="nt">></span>http/1.1<span class="nt"></Arg></span>
<span class="nt"><Arg</span> <span class="na">name=</span><span class="s">"sslContextFactory"</span><span class="nt">></span>
<span class="nt"><New</span> <span class="na">class=</span><span class="s">"org.eclipse.jetty.util.ssl.SslContextFactory"</span><span class="nt">></span>
<span class="c"><!-- Setting up path to a keystore --></span>
<span class="nt"><Set</span> <span class="na">name=</span><span class="s">"keyStorePath"</span><span class="nt">></span>
<span class="nt"><Property</span> <span class="na">name=</span><span class="s">"jetty.ssl.keyStorePath"</span><span class="nt">/></span>
<span class="nt"></Set></span>
<span class="c"><!-- Setting up passworrd for a keystore --></span>
<span class="nt"><Set</span> <span class="na">name=</span><span class="s">"keyStorePassword"</span><span class="nt">></span>
<span class="nt"><Property</span> <span class="na">name=</span><span class="s">"org.eclipse.jetty.ssl.password"</span><span class="nt">/></span>
<span class="nt"></Set></span>
<span class="c"><!-- Disabling Diffie-Hellman key exchange
to simplify traffic decryption --></span>
<span class="nt"><Call</span> <span class="na">name=</span><span class="s">"addExcludeCipherSuites"</span><span class="nt">></span>
<span class="nt"><Arg></span>
<span class="nt"><Array</span> <span class="na">type=</span><span class="s">"String"</span><span class="nt">></span>
<span class="nt"><Item></span>.*DHE.*<span class="nt"></Item></span>
<span class="nt"></Array></span>
<span class="nt"></Arg></span>
<span class="nt"></Call></span>
<span class="nt"></New></span>
<span class="nt"></Arg></span>
<span class="nt"></New></span>
<span class="nt"></Arg></span>
<span class="nt"></Call></span>
<span class="c"><!-- And serve HTTP after SSL decryption --></span>
<span class="nt"><Call</span> <span class="na">name=</span><span class="s">"addConnectionFactory"</span><span class="nt">></span>
<span class="nt"><Arg></span>
<span class="nt"><New</span> <span class="na">class=</span><span class="s">"org.eclipse.jetty.server.HttpConnectionFactory"</span><span class="nt">></span>
<span class="nt"><Arg</span> <span class="na">name=</span><span class="s">"config"</span><span class="nt">></span>
<span class="nt"><New</span> <span class="na">class=</span><span class="s">"org.eclipse.jetty.server.HttpConfiguration"</span><span class="nt">/></span>
<span class="nt"></Arg></span>
<span class="nt"></New></span>
<span class="nt"></Arg></span>
<span class="nt"></Call></span>
<span class="nt"></New></span>
<span class="nt"></Arg></span>
<span class="nt"></Call></span>
<span class="nt"></Configure></span>
</code></pre></div></div>
<p>And that’s it. Now you can run your local <em>Jetty</em> serving from <code class="language-plaintext highlighter-rouge">https://localhost:8443</code>.</p>TimothyRunning integration tests with Jetty using maven usually is plain and awesome. At least not until you try to enable SSL. And then suddenly everything goes to hell. As I have not found a definitive source of a simple working Jetty config and I’m sharing my findings. To run integration tests with Jetty under maven with SSL you’ll need to Start and stop Jetty before and after integration tests Generate Jetty SSL keys in a PKCS12 format using keytool Config Jetty to enable SSL and use generated keys