Running website from shared folder on server in DMZ

So we have two load balanced servers in the DMZ (Demilitarized Zone), these servers are not connected to our company domain, so there are no domain users or domain drives. Normally we would locate the files for a website on each server, there was a requirement for this particular website to locate the files on a shared location. What we did was to have the website files on one server in the DMZ and to configure the sites in IIS (Internet Information Services) on both servers to point to the single shared location.

This is how we configured the sites, on the main server.

  1. Create a local user
    1. Navigate to Control Panel > Administrative Tools > Computer Management > Local Users and Groups.
    2. Right-click Users and select New User from the context menu.
    3. Enter values for User name, Full name, Description, Password and Confirm password.
    4. Ensure User must change password at next logon is unticked.
    5. Ensure User cannot change password is ticked.
    6. Ensure Password never expires is ticked.
    7. Ensure Account is disabled is unticked.
  2. Create a local folder e.g. C:\Websites\MySite.
  3. Copy the website files to this new folder.
  4. Share the website folder
    1. On the properties of the newly created folder (right-click > properties)
    2. Select the Sharing tab.
    3. In the File Sharing dialog add the local user previously created and set appropriate permissions e.g. Read/Write.
    4. Click the Share button close the dialog.
    5. Click the Close button to close the Properties dialog.
  5. Set up IIS. After opening IIS
    1. Create a new application pool. Set the appropriate .Net version and application pool identity. On my setup this was .Net4.0 and AppPoolIdentity respectively.
    2. Create a new site. Set the Physical Location to be the shared folder e.g. \\Server1\MySite. Also set the application pool the one just created.
    3. Set bindings domain name bindings as required.
    4. In IIS click the new site to display the Features View.
    5. In the Features View, double click the Authentication icon. Right-click Anonymous Authentication and select Edit from the context menu.
    6. In the Edit Anonymous Authentication Credentials dialog select the Application Pool identity radio button and click the OK button to close the dialog.

The first server is now configured and the website should run at this point. Setting up the second server requires similar actions. Carry out the following on the second server in the DMZ.

  1. Create a local user by completing point 1 above. Ensure the same credentials and settings are used.
  2. Set up IIS. After opening IIS
    1. Create a new application pool. Set the appropriate .Net version, e.g. .Net4.0. Set the application identity as the local user created above.
    2. Create a new site. Set the Physical Location to be the shared folder from first server e.g. \\Server1\MySite. Also set the application pool the one just created.
    3. Set bindings domain name bindings as required.

That should be about it for the second server. Both the servers should serve up the sites.

Disclaimer: By posting this information I am not suggesting this as the ‘right way’ or ‘best practise’ it was something we did temporarily to quickly get a site up and running without opening up file wall ports in to the company domain servers.

Uploading Multiple files from a single form post in MVC

Some time back I wrote a post explaining how to allow File Upload In An ASP.NET MVC Application. Here I am going to expand on the post a little an show how to allow multiple file upload in a single post to the server.

I used bootstrap for styling.

install-package bootstrap -projectname YourProjectName

The layout page is fairly bare, only style sheet links and a Renderbody html helper.

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>File Upload Demo</title>
    <link href="~/Content/bootstrap.min.css" rel="stylesheet" />
    <link href="~/Content/bootstrap-theme.min.css" rel="stylesheet" />
    <link href="~/Content/root.css" rel="stylesheet" />
</head>
<body>
    <div>
       @RenderBody() 
    </div>
</body>
</html>

The view model contains two list properties. A list of HttpPostedFileBase objects and a list of string.

namespace FileUpload.Models
{
    public class MultipleFileUploadViewModel
    {
        public List<HttpPostedFileBase> UploadFilePaths { get; set; }
        public List<string> Urls { get; set; }
    }
}

When the view is posted to the server the list of posted files (HttpPostedFileBase) will be iterated in a for loop and the files saved to disk. The save locations will added to the model in the list of string.

namespace FileUpload.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Multiple()
        {
            return View(new MultipleFileUploadViewModel());
        }

        [HttpPost]
        public ActionResult Multiple(MultipleFileUploadViewModel model)
        {
            var selectedPaths = model.UploadFilePaths.Where(f => f != null);
            if (selectedPaths.Count() == 0)
            {
                ModelState.AddModelError(string.Empty, "No files were selected for upload");
                return View(model);
            }

            model.Urls = new List<string>();
            foreach (var path in selectedPaths)
            {
                var fileName = Path.GetFileName(path.FileName);
                var uploadPath = Path.Combine(Server.MapPath("~/uploads/"), fileName);
                path.SaveAs(uploadPath);
                model.Urls.Add(Url.Content(Path.Combine("/uploads/", fileName)));
            }
            return View(model);
        }
    }
}

The view has 4 file input boxes, they all have the same name which matches the name of the posted file list property. Also make sure the form has the multipart/form-data encryption, missing or wrong encryption will prevent the file data being posted to the controller.

@model FileUpload.Models.MultipleFileUploadViewModel
@{
    ViewBag.Title = "Multiple File Upload";
}

<div class="container">
    <h2>Index</h2>
    <div class="clearfix">
        @using (Html.BeginForm("multiple", "home", FormMethod.Post, new { enctype = "multipart/form-data" }))
        {
            @Html.AntiForgeryToken()
            @Html.ValidationSummary()
            <div class="panel panel-default">
                <div class="panel-heading">
                    File Upload
                </div>
                <div class="panel-body">
                    <div class="form-group">
                        <span><strong>Files to upload</strong></span>
                        <div class="input-group">
                            <input type="file" name="UploadFilePaths" class="form-control" />
                            <input type="file" name="UploadFilePaths" class="form-control" />
                            <input type="file" name="UploadFilePaths" class="form-control" />
                            <input type="file" name="UploadFilePaths" class="form-control" />
                        </div>
                        <span>
                            <input type="submit" class="btn btn-default" value="Upload" />
                        </span>
                    </div>

                </div>
            </div>
        }
        @if (Model.Urls != null)
        {
            <ul>
            @foreach (var url in Model.Urls)
            {
                <li><a href="@url" alt="" target="_blank">@url</a></li>
            }
            </ul>
        }
    </div>
</div>

One last thing to mention is file sizes. By default the largest upload size is 4MB. In this case with multiple files the total size of all files must not exceed the 4MB limit. Exceeding the limit will cause a Maximum Request Length Exceeded error. This maximum can be set in the web.config file as below.

<system.web>
    <httpRuntime maxRequestLength="51200" executionTimeout="240" />
</system.web>

The maxRequestLength unit is kilobytes, so in this example 51200 = 50MB. The executionTimeout is set to 4 minutes (240 seconds).

Configuration file mail settings

Sending emails is a very common activity for an application to perform. Using the System.Net section in the configuration file (app.config, web.config) to hold the settings makes things easier. There are a few different ways that the settings can be configured and I use a different method for when in development, user acceptance testing or live environments via XML transformation. I will not go into the details of XML transformations here, that is for another day.

Method 1
The first way is to send emails to localhost address 127.0.0.1.

<system.net>
    <mailSettings>
        <smtp deliveryMethod="Network">
            <network defaultCredentials="true" host="127.0.0.1"/>
        </smtp>
    </mailSettings></system.net>

To intercept and read the email grab a dummy SMTP server such as smtp4dev which, when installed sits in the system tray. The received messages can be quickly viewed, saved and the source/structure inspected, but what I find it does not do is format emails in a reader friendly manner which can make checking HTML emails difficult.

Method 2
You can configure emails to be sent to a local folder as an .eml file.

<system.net>
    <mailSettings>
        <smtp deliveryMethod="specifiedPickupDirectory">
            <specifiedPickupDirectory pickupDirectoryLocation="c:\maildrop"/>
        </smtp>
    </mailSettings>
</system.net> 

To view the messages use an application such as MailView. This is my current preferred option as emails can be easily read in both plain text and HTML format. What MailView does not do is refresh the email list as they drop into the designated folder.

Method 3
You can configure the settings to work through a local network SMTP server (e.g. exchange server).

<system.net>
    <mailSettings>
        <smtp deliveryMethod="network">
            <network host="mailserver.domain" defaultCredentials="true"/>
        </smtp>
    </mailSettings></system.net> 

Method 4
To configure an application for hosting on a third party host

<system.net>
    <mailSettings>
        <smtp>
            <network host="smtpout.secureserver.net"
                userName="XXX@XXXcom" password="Password*" port="25"/>
        </smtp>
    </mailSettings>
</system.net> 

For more information relating to mailsettings and descriptions on other attributes not used here see the MSDN article.