Setup an SFTP server on Ubuntu

This guide will show you how to setup your Ubuntu computer as an SFTP server. This could be on real hardware or on a virtualized server like a Digital Ocean droplet or an OpenShift gear. We used 13.10 to test this tutorial but it should work fine for 14.04 as well.

Required Software

Install the following packages to get started:

sudo apt-get install openssh-server

Instructions

When making any big config changes it’s always a good idea to backup the file you are changing first. In this case we will modify /etc/ssh/sshd_config so we want to back it up and then open up the original in *insert favourite text editor here (we’ll use nano for this tutorial)*:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo nano /etc/ssh/sshd_config

When you’ve got sshd_config open you need to find and edit the following lines as shown:


#Subsystem sftp /usr/lib/openssh/sftp-server
Subsystem sftp internal-sftp

UsePAM yes

Match Group sftp-user
ChrootDirectory %h
AllowTCPForwarding no
X11Forwarding no
ForceCommand internal-sftp

You can further restrict access with directives like AllowUsers and/or AllowGroups but we won’t go into detail about that right now.

When we make changes to sshd_config we have to restart the ssh service:

sudo service ssh restart

Next we have to create the sftp-user group, create the /srv/sftp folder, and set the permissions and ownership of the folder:

sudo groupadd sftp-user
sudo mkdir /srv
sudo mkdir /srv/sftp
sudo chown root:sftp-user /srv/sftp
sudo chmod 755 /srv/sftp

Almost there now. Next we need to create a user who will be able to sftp in to our server, add them to the sftp-user group and give them a password. This user, who we will call cleared-for-sftp, will not have a shell and we will set their home directory to /srv/sftp, which they will be placed into when they log in because of the  ChrootDirectory %h directive from our sshd_config file above.

sudo useradd cleared-for-sftp -d /srv/sftp -s /bin/false -g sftp-user
sudo passwd cleared-for-sftp

Enter a password for the cleared-for-sftp user and you’re all set. Give your login a try with Filezilla or from the commmand line with:

sftp cleared-for-sftp@[your-server]

where [your-server] is the IP address or domain name of your server.

Last words

When you check your login you should also double check that the user is restricted to the directory he logs into and that the user is not able to log in via ssh and get a shell session. This user should not be able to access your system files. This is a pretty basic setup and you can further customize it by creating more users and restricting them to their own folders using the Match User directive in sshd_config. If you get the infamous ‘Broken Pipe’ error make sure that all directories leading up to the sftp-user’s ChrootDirectory are owned by root and only have write privileges for the owner (ie all parent folders have privileges no higher than 755).

Good luck!

Download Links: Sending files through a Rails 4 app

Creating download links to send files through a Rails app is fairly straightforward thanks to the built-in send_file method.

For this tutorial we’ll use Rail 4.0.4. Let’s say that you have generated a resource called Document with an attribute called document_path which is a string that contains the absolute path to this document, for example /var/www/myapp/downloadables/mydocument.pdf.

If you just need to add download functionality to one resource in your app there are just three steps to complete:

  1. Create a route for your new ‘download’ action
  2. Define the ‘download’ method in your controller
  3. Add a link to your download action in a view

Start by creating a route for the download action on that resource. For this example the download action is specific to a member of the Document resource rather than the collection of all Documents, which means we will refer to this member using its id. Add a route to routes.rb that looks like this:


resources :documents do
    get 'download', on: :member
end

This will define a route for use that maps a url like /documents/1/download to the download action we are about to define in our DocumentsController. That method is pretty simple, it’s just:


class DocumentsController < ApplicationController
before_action :set_document

  #All of your other actions (show, create, delete etc) go here  

  #Download
  def download
    send_file(@document.document_path)
  end

  private
      # Use callbacks to share common setup or constraints between actions.
      def set_document
        @document = Document.find(params[:id])
      end
end

With that change we can already point our browser at myapp/documents/1/download to get our pdf, but that’s not very intuitive or user friendly. Let’s add a link to one of our views to download this document. Let’s add it to the documents/show.html.erb view for this example. The code we want to add is:


<%= link_to "Download this Document!", download_document_path %>

Easy! But don’t get confused about download_document_path vs. @document.document_pathdownload_document_path is a convenience string that Rails generated for us when we declared the download route in Step 1.  @document.document_path is the string attribute stored in our database which describes the document’s location on disk. When we click on “Download this Document!” link Rails routes this URL to the download_document_path which calls the ‘download’ action in the DocumentsController. The ‘download’ method in turn calls send_file which then uses the @document.document_path string to locate the file you want to send on disk. The app then read this file and sends it to the user.

Performance Considerations

The way it is right now we are sending the file directly to the user with no streaming and blocking one of our application’s threads in the process.  This is a performance issue worthy of it’s own article but for now I’m just going to point you to some other resources where you can learn more about this topic:

send_file documentation

File Downloads Done Right

Adding more downloadable resources using Concerns

Now let’s say that in addition to allowing your users to download Documents, you also have Photos that your want your users to download. While you could easily just follow the above steps for the Photos resource, that wouldn’t be very DRY. Instead, consider moving your ‘download’ method to a ‘Downloadable’ module and then including that module into the controller for each resource that should be downloadable. In Rails parlance this module would be a called a ‘Concern’.

To get started let’s create a file called ‘downloadable.rb’ in our app/controllers/concerns folder. This will be a module that extends ActiveSupport::Concern and we want to copy our ‘download’ method code from the DocumentsController and paste it into this new module and rename it ‘send’ so that we do not confuse and override it with the ‘download’ method in the controller. We also want to pass the filepath as a parameter since we no longer can assume we know anything about the instance variable. We then include this module into the DocumentsController concern and the PhotosController and make the ‘download’ method call send. In the end our code will look like this:

#app/controllers/concerns/downloadable.rb
module Downloadable extend ActiveSupport::Concern

  def send_to_user(args={})
    send_file args[:filepath]
  end

end
#app/controllers/documents_controller.rb
class DocumentsController < ApplicationController
include Downloadable
before_action :set_document

  #All of your other actions (show, create, delete etc) go here  

  #Download
  def download
    send_to_user filepath: @document.document_full_path
  end

  private
      # Use callbacks to share common setup or constraints between actions.
      def set_document
        @document = Document.find(params[:id])
      end
end
#app/controllers/photos_controller.rb
class PhotosController < ApplicationController
include Downloadable
before_action :set_photo

  #All of your other actions (show, create, delete etc) go here  

  #Download
  def download
    send_to_user filepath: @photo.photo_full_path
  end

  private
      # Use callbacks to share common setup or constraints between actions.
      def set_photo
        @photo = Photo.find(params[:id])
      end
end

Now we’ve got to define our routes for Photo#download. Fortunately Rails also lets us use concerns in routes.rb to avoid duplicating code everytime we want to make something downloadable. We can route a concern as follows:

#routes.rb
MyApp::Application.routes.draw do

  concern :downloadable do
    get 'download', on: :member
  end

  resources :documents, concerns: :downloadable

  resources :photos, concerns: :downloadable

  root 'welcome#index'
end

Lastly, you want to create a link on your Photo’s show.html.erb page to grab this photo:


<%= link_to "Download this Photo!", download_photo_path %>

Final Thoughts

You probably noticed that our ‘send’ method is so short that it would actually be less code to just copy the ‘download’ method into PhotosController. Yes that’s true, but I wouldn’t do it. Duplicating code like that can be a maintenance nightmare. Our use of send_file is very basic and more than a little naive. We didn’t even check if the file exists and is readable before we tried to send it! Once we add validation and testing code the ‘send’ method will get a lot longer and we’ll start to see why we didn’t want to just copy and paste. If you’re curious how you might implement the file checking I’ve included a possibility for an expanded version of Downloadable in the addendum below.

If you have any comments or impovements to this code, please feel free to comment!

Addendum

Here’s an expanded version of the Downloadable module which adds some flash notifications and redirects the to page that made the last request in case the file is not readable:


module Downloadable extend ActiveSupport::Concern

  def send_to_user(args={})
  	file = args[:filepath]
  	if File.exists?(file) and File.readable?(file)
      send_file file
    else
      redirect_to :back, notice: 'Unfortunately the requested file is not readable or cannot be located.'
    end
  end

end

Installing Windows 7 on Ubuntu 13.10 using VirtualBox 4.3

Sometimes you just can’t get away from Windows. If you’ve got a beefy enough computer and you don’t want to dual-boot one option is to run Windows in a virtual machine. This requires virtualization software such as VirtualBox which is what we’ll use in this tutorial. This tutorial assumes you have some basic skills with the terminal.

VirtualBox Download and Installation

You can download VirtualBox directly for your distro here, or you can add the repo to your distro’s package manager. I’m going to use the package manager here.

I’d prefer not to modify my /etc/apt/sources.list file as the VirtualBox download page suggests. Instead, I will create a file called virtualbox.list in the /etc/apt/sources.list.d directory:

sudo nano /etc/apt/sources.list.d/virtualbox.list

Add the following line to file and then save and exit (press Ctrl-x, answer y, then hit Enter):

deb http://download.virtualbox.org/virtualbox/debian saucy contrib

Set the permissions on this new file:

sudo chmod 644 /etc/apt/sources.list.d/virtualbox.list

Download the Oracle public key here and then run this command in the directory where you downloaded it:

sudo apt-key add oracle_vbox.asc

Or be fancy and just do it one command:

wget -q http://download.virtualbox.org/virtualbox/debian/oracle_vbox.asc -O- | sudo apt-key add –

You’re almost there! Now just update your packages and install:

sudo apt-get update
sudo apt-get install virtualbox-4.3 dkms

Creating the Virtual Machine

Before you start VirtualBox you should decide if you’re looking to install a 32bit or a 64bit. If you want to install a 64bit OS you will need to make sure that AMD-V (on AMD CPU’s) or VT-x (on Intel CPU’s) is available and enabled. For me, I had to go into my BIOS and turn it on as shown in the photo below.

Enable VT-x in your bios

Enable VT-x in your bios for 64bit support

Now open VirtualBox and click ‘New’. Select the version of OS you want to create a virtual machine of and give it a name.

New Virtual Machine

New Virtual Machine

Click ‘Next’ and select how much RAM you want to give the VM. Luckily for me I’ve got 16GB in my machine so I gave the VM a healthy 4GB of RAM. Note that if the OS you selected is 32bit giving it anymore than 4GB of RAM wouldn’t make any sense.

Memory Size

Memory Size

Next you will create or select your virtual hard drive. I am creating one here since I did not have one before.

Creating Virtual Hard Drive

Creating Virtual Hard Drive

Click ‘Next’ and select the hard drive file type. I left the default because I don’t anticipate using this VM with other virtualization software.

Select hard drive file type

Select hard drive file type. I left the default VDI.

Next you’ll want to select whether the disk space allocated to this VM is Dynamic or Fixed. I prefer dynamic since my computer has lots of horse power to deal with any additional overhead that might entail.

Dynamic or Static?

Dynamic or Static? I went with dynamic.

Now you can select the location and maximum size of your virtual hard drive. I gave it 200GB and put it in a folder on my mechanical hard drive since I don’t want to take up space on my SSD.

Hard drive file size and location

Hard drive file size and location

Click ‘Create’ and you’ll be taken back to the VirtualBox window. Now you’ll need to adjust the VM settings. Select the VM on the left and then click on ‘Settings’. In the settings panel select ‘Storage’ and the click on ‘Empty’ under Controller: IDE. Then on the right ‘Attributes’ panel click the icon that looks like a CD and select your Host Drive (ie your CD/DVD/BluRay drive), for me it was sr0. When the ‘Attributes’ panel changes you can probably leave the defaults and just click ‘OK’ to go back to the main window. You probably don’t need the ‘passthrough’ mode at this point (it’s for burning disks, reading encrypted DVDs, etc).

Setup the optical drive you wish to use to install the guest OS

Setup the optical drive you wish to use to install the guest OS

Make sure your OS installation disk is in the drive and then back in the VirtualBox main window, select your VM and click the ‘Start’ button. You should see the disk to start loading the installation!

Windows setup loading on the VM

Windows setup loading on the VM

Windows 7 setup running in VM

Windows 7 setup running in VM

Good luck! Remember that the default host-key is the right ctrl key, so if your mouse or keyboard is captured by the guest OS (ie the VM) just press right ctrl to return control to your host OS.