Jason Haley

Ramblings from an Independent Consultant

Virus Scan File Uploads Using Multi-Container Web App

This month at the Microsoft Build conference, the Azure App Service team announced multi-container support for Linux Web Apps. This capability enables you to deploy multiple containers in a single Web App.

In the session PaaS and Container Innovation – What’s new with App Service members of the App Service team show a demo of a Web App that has three containers: Nginx, WordPress and Redis.

The multi-container capability isn’t designed to compete with Kubernetes or other orchestrators but to just allow the ability to easily add that one or two more containers that will help support your containerized web application (like a cache for example).

This past weekend I was working on an Azure Functions extension that I’m planning on using to provide virus scanning for a website – when the thought crossed my mind that multi containers would enable me to provide virus scanning to a web app even easier. So, I took a detour from working on my extension and worked on this sample instead.

Backstory

Often web sites need the ability to upload files. However, if you have been through a secure code review or penetration testing, you’ll know that to safely provide that functionality to your users you need to scan any uploaded files for viruses. This is one of those parts of an implementation people put off until later – especially if they don’t already have a solution for the virus scanning – then find out implementing it isn’t as easy as it should be.

In Azure you can upload files easily to blob storage, ensure the transport is secure and even ensure the files are encrypted at rest. But scanning those saved blobs for viruses is one of those features you have to implement yourself.

A couple of options for virus scanning via an API:

  • VirusTotal – a third party API that would require passing the file out of Azure to the service
  • ClamAV – an open source anti-virus scanning server (GNU GPL v2 license)

For my scenario, I have the following constraints:

  • I need to be able to integrate the virus scanning into my codebase using C#
  • I cannot transfer the files out of the data center just to scan for viruses
  • I don’t want to have a VM running 24/7 that is only used to scan less than 100 files a month

The Solution: Linux Web App with Two Containers

After doing some research, I’ve found a way to stay within my constraints and easily add virus scanning to a Web App.

  1. Use ASP.NET Core so I can run the site in a Linux container
  2. Use a second container to run the mkodockx/docker-clamav image (utilize the Nuget nClam package as a client to the ClamAv server)
  3. No need for a VM since we can now run multiple containers in a single Web App

blogimage1

Creating the demo web app

To verify things work the way I want, I created a simple web app that uploads files and then displays the results of the virus scan. In order to save time, I started with the Asp.Net Core Web application template, ripped the majority of the views and actions out and then used some code from the ASP.NET Core documentation for the file upload logic: File uploads in ASP.NET Core

I put a copy of the code in GitHub if you want to see the full web site code: https://github.com/JasonHaley/file-upload-demo

Here’s the code that takes the uploaded file(s), passes them off to the ClamAV server container and returns the results:

[HttpPost("UploadFiles")]
public async Task UploadFiles(List files)
{
    var log = new List();

    foreach (var formFile in files)
    {
        if (formFile.Length > 0)
        {
            var clam = new ClamClient("clamav-server", 3310);
            var result = await clam.SendAndScanFileAsync(formFile.OpenReadStream());

            log.Add(new ScanResult()
            {
                FileName = formFile.FileName,
                Result = result.Result.ToString(),
                Message = result.InfectedFiles?.FirstOrDefault()?.VirusName,
                RawResult = result.RawResult
            });
        }
    }
            
    var model = new UploadFilesViewModel();
    model.Results = log;
                        
    return View(model);
}

The important thing to note with using the ClamClient– is the communication between the web site and the clamav-server container uses the container’s name, not an IP address.

Walkthrough

You can follow these rest of this entry if you want to get it going yourself. In order to do this, you will need Docker for Windows running on your machine and a recent version of Visual Studio 2017.

Once you have the file upload logic in your ASP.NET Core Web application, you need to add the Docker support to the project.

1. Right click on the WebApp in the Solution Explorer

2. Choose Add -> Docker Support

blogimage2

This will add a Dockerfile to your Web project and a docker-compose project to the solution.

blogimage3

In your docker-compose project, open the docker-compose.yml file and add the clamav-server to the services, like shown below:

version: '3.4'

services:
  clamav-server:
    image: mkodockx/docker-clamav
  webapp:
    image: ${DOCKER_REGISTRY}webapp
    build:
      context: .
      dockerfile: WebApp/Dockerfile

Now run the debugger (hit F5) to start the web application. The first run will take a little while to start since it has to pull down the ClamAV image and update the virus definitions.

Once it starts you should see a file upload page like shown here:

blogimage4

Select a file to upload and see if it has a virus in it:

blogimage5

If you want to test for a virus you can find the Eicar virus text for a test file here: http://www.eicar.org/86-0-Intended-use.html

Push the Docker Image to a Docker Hub

Now that the code works locally, the next step is to put the web project’s container into a repository so you can configure a Web App in Azure to use it.

For the purposes of this demo, I put my web app in docker hub at: https://hub.docker.com/r/haleyjason/file-upload-demo/

If you want to create your own image to put in docker hub, change your build to a Release build then start the application again. This will create the release images locally.

You will also need to have a Docker Hub account and create a repo to push the image to.

Once you have the Docker Hub repo ready, complete the following steps at a command line:

  1. List your Docker containers to get the container id and name
    docker ps
  2. Login by using the following and entering your Docker Hub username instead of <username>
    docker login –username <username>
    Then enter your password when prompted
  3. Tag your image using your container id and repo name
    docker tag <container> <dockerhub account>/<docker hub repo>:<tag>
    I used something like:
    docker tag 0c98 haley/file-upload-demo:latest
  4. Push the image to the repo
    docker push <dockerhub account>/<docker hub repo>

You should now have the web app container in Docker Hub.

Create the Azure Web App

The last step is to create a Web App and a Docker Compose file to connect the images.

First create a docker-compose.yml file that just connects the containers. The file contents should be similar to the following:


version: '3.4'

services:
  webapp:
    image: haleyjason/file-upload-demo
  clamav-server:
    image: mkodockx/docker-clamav

Save this file somewhere so you can upload it to the Web App in the next part.

  1. In the Azure portal, Click on the plus in the upper left corner -> Web -> Web App
    blogimage6
  2. On the Web App blade:
    - Provide an App name
    - Select your subscription
    - Select or create a new Resource Group
    - For OS, select Docker
    - For demo purposes, stay with the Generated App Service plan
    blogimage7
  3. Click on the Configure container menu, then the Docker Compose (Preview) tab
  4. In the Configuration text box –> clik the folder icon and select the docker-compose.yml file you created earlier that connects the two containers.
     blogimage11
  5. Click OK
  6. Check the Pin to dashboard checkbox
  7. Click the Create button to get the process of creating the web app started
  8. Once the web app is ready, in the Overview blade, click on the URL in for the application

Now wait 5 – 10 minutes … the first load takes several minutes – but once it is up and running it responds normally. 

When I select a couple of files:

blogimage9

I now get the scanned results:

blogimage10

Conclusion

The new multi-container capability of Azure App Service Linux Web Apps seems like a promising way to provide that ability to host a virus scanning server along side your web application.

Useful Utility: Markdown Monster

A couple of months ago at Boston .Net Architecture Group a friend of mine (Bob Goodearl) introduced me to Markdown Monster.

I’m sure many of you who deal with markdown all the time are thinking – big deal just learn the syntax .. what’s the bid deal right? 

For me the killer feature is: Paste Images from Clipboard

I have several hands on labs in Word form which I am converting to markdown files to version in github.  With Markdown Monster, I can just copy and paste the text over into a new .md file, format the header and bulleted lists – then I can copy and paste the images into the document.

When you paste the image it will prompt you for where you want to save it and what you want to name it – so it isn’t effortless – but it is a lot easier for me than to grab all those images from the word document, save each to a file then have to locate the file when modifying the markdown.

Great simple utility!

image