Showing posts with label inets. Show all posts
Showing posts with label inets. Show all posts

Monday, 22 November 2010

EEE: erlang inets http authentication with mnesia

Again, I am using R13B04 (erts-5.7.5) on Fedora 13.

Unfortunately erlang's inets http server only performs Basic authentication.  So I would only authenticate over https, like I've shown here.  Setting up erlang inets for SSL can be tricky depending on what you want to do.  One important gotcha is detailed in this post.

Create Tables

First, your application must create some mnesia tables for authentication. For this you need the record definitions.  The documentation for mod_auth says to add the following line to your module.

-include("mod_auth.hrl").

erlc failed to find the file, so I found it in "/usr/lib64/erlang/lib/inets-5.3/src/" and copied it to my project manually.

Creating the tables is no different to the documentation

mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(httpd_user,
                        [{type, bag},
                         {disc_copies, [node()]},
                         {attributes, record_info(fields, 
                                                  httpd_user)}]),
    mnesia:create_table(httpd_group,
                        [{type, bag},
                         {disc_copies, [node()]},          
                         {attributes, record_info(fields, 
                                                  httpd_group)}]),
    mnesia:wait_for_tables([httpd_user, httpd_group], 60000).

The documentation says this is a naive implementation, a trick I have used is to catch the output of mnesia:create_schema.  Ok means that the schema didn't exist before and the tables have to be created.  If there is a better way, I'd like to hear it.

Add user and group

Next, adding a user and group to the database, I use the mod_auth functions

true = mod_auth:add_user("superuser", "password", "Super User", 443, "/test"),
true = mod_auth:add_group_member("users", "superuser", 443, "/test").

Configuring for Authentication

Below is the next version of my "443.conf" file which includes the necessary configuration for authentication.

[
 {modules, [
  mod_alias, 
  mod_auth, 
  mod_esi, 
  mod_actions, 
  mod_cgi, 
  mod_dir, 
  mod_get, 
  mod_head, 
  mod_log, 
  mod_disk_log
 ]},
 {port,443},
 {server_name,"localhost.localdomain"},
 {server_root,"log"},
 {document_root,"secure"},
 {erl_script_alias, {"/test", [test]}},
 {directory, 
  {"/test", [
   {auth_name, "Data Server"}, 
   {allow_from, all}, 
   {auth_type, mnesia},
   {require_group, ["users"]}
  ]}
 },
 {socket_type, ssl},
 {ssl_certificate_file, "localhost.pem"},
 {error_log, "error.log"},
 {security_log, "security.log"},
 {transfer_log, "transfer.log"},
 {mime_types,[
  {"html","text/html"},
  {"css","text/css"},
  {"js","application/x-javascript"}
 ]}
]. 

To access my test esi module, I now need a to use a password.

EEE: Binding erlang ssl to ports < 1024

After a bit of hair pulling, I finally worked out how to get erlang's inets to bind to port 443 and service https requests. Again, I am using R13B04 (erts-5.7.5) on Fedora 13.

I found this page on erlanganswers.com which said to use setcap on Linux > 2.6.24. Unfortunately this didn't work. After clearing the hair off my keyboard, Running "netstat -ptl" while temporarily serving https on port 8080 showed me that beam was not doing the listening, but ssl_esock.

setcap "cap_net_bind_service=+ep" /usr/lib64/erlang/lib/ssl-3.10.8/priv/bin/ssl_esock

No need to setcap any other file except this one.

Below is a "443.conf" file that configures inets httpd to serve https only. Look at my previous post on starting inets http server for the commands to start it.

[
 {modules, [
  mod_alias, 
  mod_auth, 
  mod_esi, 
  mod_actions, 
  mod_cgi, 
  mod_dir, 
  mod_get, 
  mod_head, 
  mod_log, 
  mod_disk_log
 ]},
 {port,443},
 {server_name,"localhost.localdomain"},
 {server_root,"log"},
 {document_root,"secure"},
 {erl_script_alias, {"/test", [test]}},
 {socket_type, ssl},
 {ssl_certificate_file, "localhost.pem"},
 {error_log, "error.log"},
 {security_log, "security.log"},
 {transfer_log, "transfer.log"},
 {mime_types,[
  {"html","text/html"},
  {"css","text/css"},
  {"js","application/x-javascript"}
 ]}
].

EEE: Configuring Erlang inets

Erlang's built in web server (part of inets) has a number of quirks, first one I came across is listing the server modules in a specific order in the configuration file.  I am using R13B04 erts-5.7.5 on Fedora 13 for this post.

Much of this came from this stackoverflow question.   I've almost copied it completely except for a few details below.

Here is a complete configuration file example. (8080.conf)

[
 {modules, [
  mod_alias, 
  mod_auth, 
  mod_esi, 
  mod_actions, 
  mod_cgi, 
  mod_dir, 
  mod_get, 
  mod_head, 
  mod_log, 
  mod_disk_log
 ]},
 {port,8080},
 {server_name,"localhost.localdomain"},
 {server_root,"log"},
 {document_root,"www"},
 {erl_script_alias, {"/test", [test]}},
 {error_log, "error.log"},
 {security_log, "security.log"},
 {transfer_log, "transfer.log"},
 {mime_types,[
  {"html","text/html"},
  {"css","text/css"},
  {"js","application/x-javascript"}
 ]}
].

Here is how to start inets using the configuration file.

 inets:start(),
 inets:start(httpd, [{proplist_file, "8080.conf"}]). 

Make sure that both "www" and "log" directories are there otherwise it won't work either.  If you put a test file in the "www" directory, opening http://localhost:8080/test.html will download it.

A few things didn't work, so I wouldn't bother trying this
  1.  Trying to add extra configuration items before or after proplist_file when calling inets:start.  I tried to specify the port dynamically in this fashion, but it didn't work. Looking at the source might help me work out why.
  2. Switching port 8080 to a port < 1024.  Linux doesn't let you do this without some extra trickery.  I'm investigating how to get around this.