<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Reynold Chery</title>
    <description>The latest articles on DEV Community by Reynold Chery (@joshbaptiste).</description>
    <link>https://dev.to/joshbaptiste</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F52886%2F53a53460-ab12-41bc-ac8b-d58ecaa6eb10.jpeg</url>
      <title>DEV Community: Reynold Chery</title>
      <link>https://dev.to/joshbaptiste</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joshbaptiste"/>
    <language>en</language>
    <item>
      <title>Replacing OpenSSH/SFTP with SFTPGo</title>
      <dc:creator>Reynold Chery</dc:creator>
      <pubDate>Wed, 07 Apr 2021 00:02:17 +0000</pubDate>
      <link>https://dev.to/joshbaptiste/replacing-openssh-sftp-with-sftpgo-gkc</link>
      <guid>https://dev.to/joshbaptiste/replacing-openssh-sftp-with-sftpgo-gkc</guid>
      <description>&lt;p&gt;I run a pair of GNU/Linux VPS's, one running Plex as a client and the other as a storage server for media. They both run on "the cloud" hosted by different providers. I've been noticing lately that transfer rates between them over SSHFS have been lagging, causing buffering and drop issues for some of my media streams. I tested directly between the hosts using Iperf3 and while not spectacular between two large hosting providers, it's fine for streaming SDTV content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me@plex:~&lt;span class="nv"&gt;$ &lt;/span&gt;iperf3 &lt;span class="nt"&gt;-c&lt;/span&gt; 10.10.10.100 &lt;span class="nt"&gt;-p&lt;/span&gt; 5201 &lt;span class="nt"&gt;--bytes&lt;/span&gt; 1000000000 
Connecting to host 10.10.10.100 , port 5201
&lt;span class="o"&gt;[&lt;/span&gt;  4] &lt;span class="nb"&gt;local &lt;/span&gt;10.10.10.99 port 56634 connected to 10.10.10.100 port 5201
&lt;span class="o"&gt;[&lt;/span&gt; ID] Interval           Transfer     Bandwidth       Retr  Cwnd
&lt;span class="o"&gt;[&lt;/span&gt;  4]   0.00-1.00   sec  57.0 MBytes   478 Mbits/sec  165   3.94 MBytes       
&lt;span class="o"&gt;[&lt;/span&gt;  4]   1.00-2.00   sec  45.0 MBytes   378 Mbits/sec   17   1.99 MBytes       
&lt;span class="o"&gt;[&lt;/span&gt;  4]   2.00-3.00   sec  43.8 MBytes   367 Mbits/sec    0   2.02 MBytes       
&lt;span class="o"&gt;[&lt;/span&gt;  4]   3.00-4.00   sec  42.5 MBytes   356 Mbits/sec    0   2.15 MBytes       
&lt;span class="o"&gt;[&lt;/span&gt;  4]   4.00-5.00   sec  48.8 MBytes   409 Mbits/sec    0   2.41 MBytes       
&lt;span class="o"&gt;[&lt;/span&gt;  4]   5.00-6.00   sec  52.5 MBytes   440 Mbits/sec    0   2.76 MBytes       
&lt;span class="o"&gt;[&lt;/span&gt;  4]   6.00-7.00   sec  60.0 MBytes   503 Mbits/sec    0   3.02 MByte
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then moved on and tested the mount point. I mount the media storage filesystem from plex client to storage server using SSHFS. So I performed a crude check on the SFTP mount point using cat and a standard utility called pipe viewer (&lt;a href="http://www.ivarch.com/programs/pv.shtml"&gt;pv&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;me@plex:/mnt/media/tv/show/S01$ 
cat show.mkv | pv -rb --progress &amp;gt;/dev/null
4.88MiB [ 300KiB/s]          
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;indeed, file read hovers around ~300KB/s (with multiple stalls) were just bad. Now there are a slew of tweaks that can be done, via the ssh protocol lighter MACs, Ciphers, via SSHFS buffer cache, VFS tweaks etc.. and the network,  buffer, window sizes etc.. I decided to sidestep all the tweaks and try a different SFTP implementation entirely. I remembered a &lt;a href="https://news.ycombinator.com/item?id=20531541"&gt;hacker news post&lt;/a&gt; not too long ago and decided download a binary release of SFTPGo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/drakkan/sftpgo"&gt;SFTPGo&lt;/a&gt; is a standalone SFTP server written in Go, that does not rely on the system authentication mechanisms and can utilize multiple SQL authentication back-ends such as SQLite, Mysql etc.. but I wanted a quick way to use public/private key authentication and good thing it supports this via its "portable" option.&lt;/p&gt;

&lt;p&gt;First we'll create an ssh public/private key pair  on the Plex client VPS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me@plex:~ ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ecdsa &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/id_ecdsa_sftpgo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then add a section in ~/.ssh/config on the client that describes the storage server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="k"&gt;Host&lt;/span&gt; storage_server
   &lt;span class="k"&gt;Hostname&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;.10.10.100
   &lt;span class="k"&gt;Port&lt;/span&gt; &lt;span class="m"&gt;4444&lt;/span&gt;
   &lt;span class="k"&gt;User&lt;/span&gt; sftpdude
   &lt;span class="k"&gt;IdentityFile&lt;/span&gt; ~/.ssh/sftpgo_ecdsa
   &lt;span class="k"&gt;Ciphers&lt;/span&gt; aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com
   &lt;span class="k"&gt;MACs&lt;/span&gt; umac-128-etm@openssh.com,umac-128@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha1,hmac-sha2-512-etm@openssh.com,hmac-sha2-512
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll move to the storage server and create a dedicated directory and download the latest release of SFTPGo and install it manually&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me@storage:/ &lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /opt/sftpgo
me@storage:/ &lt;span class="nb"&gt;sudo chown &lt;/span&gt;me:me &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
me@storage:/ &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
me@storage:/opt/sftpgo wget &lt;span class="nt"&gt;-O&lt;/span&gt; - https://github.com/drakkan/sftpgo/releases/download/0.9.6/sftpgo_0.9.6_linux_x86_64.tar.xz | &lt;span class="nb"&gt;tar &lt;/span&gt;Jxf -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next create a wrapper script on the storage server and copy in the client's public key that was created earlier in ~/.ssh/id_ecdsa_sftpgo.pub&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me@storage:/opt/sftpgo/ &lt;span class="nb"&gt;cat &lt;/span&gt;sftpgo_start.sh
&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nv"&gt;sftpgo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/sftpgo/sftpgo
&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4444
&lt;span class="nv"&gt;pubkey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'ecdsa-sha2-nistp256 AAAAE2V....3YPCHWceWD2QcQFG='&lt;/span&gt;
&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/media

&lt;span class="nv"&gt;$sftpgo&lt;/span&gt; portable &lt;span class="nt"&gt;--username&lt;/span&gt; sftpdude &lt;span class="nt"&gt;--public-key&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pubkey&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--sftpd-port&lt;/span&gt; &lt;span class="nv"&gt;$port&lt;/span&gt; &lt;span class="nt"&gt;--directory&lt;/span&gt; &lt;span class="nv"&gt;$dir&lt;/span&gt; &lt;span class="nt"&gt;--permissions&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We run the newly created script on the storage server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me@storage:/opt/sftpgo/ /opt/sftpgo/ssftpgo_start.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We connect to the storage_server via sshfs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me@plex:~ /usr/bin/sshfs &lt;span class="nt"&gt;-f&lt;/span&gt; storage_server:/tv /mnt/media/tv2 &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ServerAliveInterval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15 &lt;span class="nt"&gt;-o&lt;/span&gt; rw,reconnect,idmap&lt;span class="o"&gt;=&lt;/span&gt;user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and retry via the crude cat | pv&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;me@plex:/mnt/media/tv2/show/S01$ 
cat show.mkv | pv -rb --progress &amp;gt;/dev/null
23.8MiB [2.84MiB/s]          
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and lo and behold .. much faster transfer rates without tweaking and just switching implementations. Of course it's far slower than a raw byte transfers (iperf3) but we are utilizing &lt;a href="https://en.wikipedia.org/wiki/Filesystem_in_Userspace"&gt;FUSE&lt;/a&gt; which in itself is a bottleneck traversing user-space/kernel boundaries, but it's good enough for this use case. &lt;/p&gt;

&lt;h2&gt;
  
  
  Securing things
&lt;/h2&gt;

&lt;p&gt;OpenSSH has decades of security under its belt and comes from the iron clad OpenBSD folks and we want to protect the system as much as possible from any weird side effects, bugs, and/or exploits typical of long running programs. While it is written in a garbage collected language (Go) which eliminates many classes of memory induced bugs, it is better to be safe than sorry. So if you're running a popular Linux distro today you most likely have systemd installed by default. &lt;a href="https://en.wikipedia.org/wiki/Systemd"&gt;Systemd&lt;/a&gt; is an initd/framework for starting/managing the system and it's services. It is what we'll use to to monitor, manage and secure the SFTPGo service. First thing we do is think about that what the service needs to do to perform its job; it starts up, creates a pub/priv key file if /opt/sftpgo/id_ecdsa doesn't exist, listens on a specified port, and reads/writes to /mnt/media/. So let's create a systemd service file that incorporates this behavior.&lt;/p&gt;

&lt;p&gt;So here's the systemd service file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;me@storage:/etc/systemd/system&lt;/span&gt; &lt;span class="err"&gt;cat&lt;/span&gt; &lt;span class="err"&gt;sftpgo@me.service&lt;/span&gt;
&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;SFTP Go&lt;/span&gt;
&lt;span class="py"&gt;AssertDirectoryNotEmpty&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/mnt/media/&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;%i&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/sftpgo/sftpgo_start.sh&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/sftpgo&lt;/span&gt;
&lt;span class="py"&gt;ExecReload&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/kill -s HUP $MAINPID&lt;/span&gt;
&lt;span class="py"&gt;KillMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;mixed&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;span class="py"&gt;StartLimitInterval&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;20s&lt;/span&gt;
&lt;span class="py"&gt;StartLimitBurst&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;
&lt;span class="py"&gt;NoNewPrivileges&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;PrivateTmp&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;PrivateDevices&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;DevicePolicy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;closed&lt;/span&gt;
&lt;span class="py"&gt;ProtectSystem&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;strict&lt;/span&gt;
&lt;span class="py"&gt;ProtectHome&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;ReadWritePaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/sftpgo /mnt/media/tv &lt;/span&gt;
&lt;span class="py"&gt;RestrictAddressFamilies&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;AF_INET&lt;/span&gt;
&lt;span class="c"&gt;#requires systemd 235+ and kernel 4.11+
&lt;/span&gt;&lt;span class="py"&gt;IPAccounting&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;IPAddressDeny&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;any&lt;/span&gt;
&lt;span class="py"&gt;IPAddressAllow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10.10.10.0/24&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;span class="py"&gt;Alias&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sg.service&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically we prevented read/write access anywhere except /opt/sftpgo and /mnt/media/tv. We restricted access to /home or any other filesystems via the ProtectHome, ProtectSystem stanzas. We also restricted socket creation to the AF_INET protocols while only accepting connections  from Plex client ip address range. Systemd provides a host of facilities one can use to secure their services without that much effort in tandem with the kernel 4.x+ namespacing, eBPF, cgroup controls. Most of systemd's lock down parameters are explained &lt;a href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html"&gt;here&lt;/a&gt;, there's even more we can do such as system call, namespace filtering, but we'll need to profile the binary some more before implementing that. &lt;/p&gt;

&lt;p&gt;Now we enable and start our new service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me@storage:~ &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;sftpgo@me
me@storage:~ &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start sftpgo@me
me@storage:~ &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status sftpgo@me 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And check some transfer stats after transferring some files (since we enabled IPAccounting=yes)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me@storage:~ &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl show sftpgo@me &lt;span class="nt"&gt;-p&lt;/span&gt; IPIngressBytes &lt;span class="nt"&gt;-p&lt;/span&gt; IPEgressBytes 
&lt;span class="nv"&gt;IPIngressBytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20382896
&lt;span class="nv"&gt;IPEgressBytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;677305852 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;99% of the time I have no need to replace the standard SFTP server implementation on my Linux/*BSD hosts, but it was worth it this time for some flexibility and the bump in speed. Though the OpenSSH SFTP implementation is actually &lt;a href="https://groups.google.com/forum/#!topic/golang-nuts/wLRJHQ9ydrk"&gt;faster&lt;/a&gt; than SFTPGo, so clearly something is wrong between these hosts that I need to troubleshoot down the road. But, just the fact that I can decouple the standard GNU/Linux user authentication mechanism from SFTP and name the user whatever I want as long as the script has the correct read/write permissions is a plus for me.&lt;/p&gt;

</description>
      <category>go</category>
      <category>openssh</category>
      <category>systemd</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
