Using SSL Self Signed Certs with Node.js

If you plan to proxy sites or services that are SSL enabled and are signed with self-signed certs, then you need to be aware that you have to configure a few extra parameters to make sure the SSL handshake happens properly. Otherwise, the request goes through without validating the self-signed certs (which is a strange default behavior IMO).

Namely, you have to do the following:

  1. Use the https module (API docs here)
  2. Set the agent to false (unless you plan to provide one)
  3. Set the ca to the location where the self-signed cert is located respective to your node file
  4. Set the rejectUnauthorized to true so that an error is emitted upon failure

Here is a snippet of code that you can use as an example:

var https = require('https'),
        fs = require('fs'),
        host = 'localhost',
        port = 443;

    exports.getTest = function (req, res, next) {
        var url = '/login.html';

        processRequest(req, res, next, url);
    };

    function processRequest (req, res, next, url) {
        var httpOptions = {
            hostname: host,
            path: url,
            port: port,
            method: 'GET',
            agent: false,
            ca: [fs.readFileSync('ssl/myroot_cert.crt')],
            rejectUnauthorized: true
        };

        var reqGet = https.request(httpOptions, function (response) {
            var content = '';
            response.on('data', function (chunk) {
                content += chunk;
            });
            response.on('end', function () {
                try {
                        res.send("Successful SSL Handshake");
                }
                catch (e) {
                    res.send(500);
                }
            });
        });

        reqGet.on('error', function (e) {
            res.send("Unable to SSL Handshake", 401);
        });

        reqGet.end();

        return next();
    }

Setting up GIT with Apache Smart HTTP/S and LDAP

I recently was put on a project to explore how we could use GIT over HTTP and integrate with our existing LDAP for authnz.  The reason for HTTP is that it is pretty easy to set-up and you can encrypt the content transfer with SSL.  Also, HTTP/S is firewall friendly.  The downside is that HTTP is a “dumb” protocol. The information here consolidates some information I found on the web to accomplish this.  I am using RHEL 6, Apache 2.2, OpenLDAP and msysgit for my GIT client on my Windows machine.

First off, HTTP wasn’t necessarily the fastest protocol to use with GIT until they added a mod called git-http-backend, or SMART-HTTP, as of GIT 1.6.6. This article from the Pro GIT author, @chacon, details this and from my experience I cut my download times by two-thirds using this approach.  Moreover, github is also supporting this.  Basically what you need to do is as follows:

  1. Confirm you have Apache 2.2 installed: rpm -q httpd (install it with yum otherwise)
  2. Clone your GIT repo to Apache by doing the following (as per Pro GIT book):
    $ cd /var/www/html/git (mkdir if necessary)
    $ git clone  --bare /path/to/git_project gitproject.git
    $ cd gitproject.git
    $ mv  hooks/post- update.sample  hooks/post- update
    $ chmod a+x  hooks/post- update 
  3. Update your httpd.conf to include this:
    SetEnv GIT_PROJECT_ROOT /var/www/html/git
    SetEnv GIT_HTTP_EXPORT_ALL
    ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
  4. Add LDAP Authentication (in this case, any valid LDAP user will have access to the git location) as follows:
    <LocationMatch "^/git/.*/git-receive-pack$">
            SSLRequireSSL
            Order deny,allow
            Deny from All
            AuthName "GIT Repo"
            AuthType Basic
            AuthBasicProvider ldap
            AuthzLDAPAuthoritative off
            AuthLDAPURL "ldap://ldap-server.company.com:389/ou=users,o=company?uid"
            Require valid-user
    </LocationMatch>
  5. Restart httpd: /etc/init.d/httpd restart

For LDAP authorization, of course you may have several different repos running off the same host, all which require certain users or groups access to the given location. This site explains this in detail, but here is an example I used so that I could bind a particular repo location to an LDAP group with SSL in place:

<LocationMatch "/git/gitproject*">
        SSLRequireSSL
        Order deny,allow
        Deny from All
        AuthName "GIT Repo"
        AuthType Basic
        AuthBasicProvider ldap
        AuthzLDAPAuthoritative on
        LDAPTrustedGlobalCert CA_BASE64 /etc/pki/tls/http/rootCA.crt
        AuthLDAPURL "ldaps://ldap-server.company.com:636/ou=users,o=company?uid"
        AuthLDAPGroupAttribute member
        AuthLDAPGroupAttributeIsDN on
        Require ldap-group cn=my.group,ou=groups,o=company
        Satisfy any
</LocationMatch>

In this example, I’m binding project.git (use wildcard for LocationMatch in-case users forget to add .git extension) to any member in the LDAP group “my.group”. Note that you may need to define a different LDAP group attribute to match the field that will contain the DN of your users .  If you are not storing DN, then you can set AuthLDAPGroupAttributeIsDN to off.

The last step is enable SSL on your Apache server.  We use self-signed CERTs internally so you’re going to have to add those certs to your GIT clients unless of course your using a well known root CA like Verisign.  To do that with msysgit, you open the $MYSYSGIT_INSTALL/bin/curl-ca-bundle.crt and add the base-64 encoded text of your keys to the end of this file.  Then run the following command from the GIT BASH:

$ git config --global http.sslcainfo c:\\apps\\Git\\bin\\curl-ca-bundle.crt

That’s pretty much it!  Now you can simply connect to your repo from msysgit with SSL (no SSH keys req’d) and LDAP authorization:

$ git clone https://mygitserver.company.com/git/project.git
Cloning Project...
Username: [put user name that's in the LDAP group]
Password: [password]
remote: Counting objects: 17, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 17 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (17/17), done.