wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Creating nginx configurations for Domino SSL


Websites need to be secure, so the SHA-1 cipher is coming to an end. Despite best efforts, Domino is stuck with this outdated Cipher. While you can, on Windows, hide Domino behind IHS, I find nginx easier to tame.
Jesse explains how to configure nginx as the Domino proxy. So all is good, expecially since he also covered High availability.
But when you have a lot of sites, that's a lot of typing (and copy & paste from the Internet site documents). Mustache to the rescue! I've written about Mustache before and it suits the task quite nicely:
  1. Create one well working sample configuration
  2. Replace the site specific values with {{mustache}} variables
  3. Run it against all Internet site documents
The code I used (see below) generates just 4 variables:
  1. {{name}} The name of the site according to the configuration document. I use it here to configure the file name
  2. {{siteName}}The first web name, it will become the listen parameter
  3. {{allNames}} All web names, they will be listed as server_name
  4. {{settings}} all field values of the Internet site documents as concatenated strings. Using a dot notation they can be used directly. e.g. {{settings.SSLKeyFile}}. Using this approach you can do whatever is needed to generate your desired output
This is the initial template, based on Jesse's article:
server {
        listen {{siteName}}:443;
        server_name {{#allNames}} {{.}}{{/allNames}};
        client_max_body_size 100m;
 
        ssl on;
        # Original keyfile: {{settings.SSLKeyFile}}
        ssl_certificate      /etc/nginx/ssl/{{name}}.pem;
        ssl_certificate_key /etc/nginx/ssl/{{name}}.key;
 
        location / {
                proxy_read_timeout 240;
                proxy_pass http://localhost:8088;
                proxy_redirect off;
                proxy_buffering off;
 
                proxy_set_header        Host               $host;
                proxy_set_header        X-Forwarded-For    $proxy_add_x_forwarded_for;
                proxy_set_header        $WSRA              $remote_addr;
                proxy_set_header        $WSRH              $remote_addr;
                proxy_set_header        $WSSN              $host;
                proxy_set_header        $WSIS              True;
        }
}

The Java code takes in the file name of that template as parameter, so when you feel you rather use Apache or need a different output (e.g. a report), you are free to supply a different file here.The Java is quite simple and should be self explanatory. Since it is a one-time, you-know-what-you-are-doing utility, there is no real error handling:
 
package com.notessensei.nginxdomino;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;

import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import lotus.domino.NotesFactory;
import lotus.domino.NotesThread;
import lotus.domino.Session;
import lotus.domino.View;

import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;

public class ConfigGenerator {

    private static final String VIEW_NAME = "($InternetSites)";
    private static final String WEB_FORM  = "WebSite";

    private final File          outDir;
    private final String        dirFileName;
    private final String        templateFileName;

    public ConfigGenerator(String outputDir, String fileName, String templateFile) {
        this.outDir = new File(outputDir);
        this.dirFileName = fileName;
        this.templateFileName = templateFile;
        if (!outDir.exists()) {
            outDir.mkdirs();
        }
    }

    public static void main(String[] args) {
        if (args.length < 3) {
            System.err.println("Usage java -jar ngixdomino [outputdir] [path-to-local-pubnames] [path-to-template]");
            System.exit(1);
        }
        ConfigGenerator cg = new ConfigGenerator(args[0], args[1], args[2]);
        cg.generateDefaultConfigs();
        System.out.println("Done ..");
    }

    public void generateDefaultConfigs() {
        // Here we read from domino
        final Collection<InternetSite> internetSites = new ArrayList<InternetSite>();
        this.populateInternetSites(internetSites);
        if (internetSites.size() == 0) {
            System.err.println("Something went wrong, got no internet sites!");
            System.exit(2);
        }
        Mustache mustache = this.getDefaultTemplate();
        this.renderConfigs(mustache, internetSites);
    }

    private void renderConfigs(Mustache mustache, Collection<InternetSite> internetSites) {

        for (InternetSite is : internetSites) {
            String outfileName = this.outDir.getPath() + File.separator + is.getName();
            System.out.println("Creating " + outfileName);
            try {
                FileOutputStream out = new FileOutputStream(new File(outfileName));
                Writer pw = new PrintWriter(out);
                mustache.execute(pw, is);
                pw.flush();
                pw.close();
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private Mustache getDefaultTemplate() {
        MustacheFactory mf = new DefaultMustacheFactory();
        return mf.compile(this.templateFileName);
    }

    private void populateInternetSites(Collection<InternetSite> internetSites) {
        Session s = null;
        Database nab = null;
        View v = null;
        Document doc = null;

        NotesThread.sinitThread();
        try {
            s = NotesFactory.createSession();
            nab = s.getDatabase("", this.dirFileName);
            v = nab.getView(VIEW_NAME);
            doc = v.getFirstDocument();
            while (doc != null) {
                Document nextDoc = v.getNextDocument(doc);
                this.addoneInternetSite(internetSites, doc);
                doc.recycle();
                doc = nextDoc;
            }
            Utils.shred(doc, v, nab);
        } catch (NotesException e) {
            e.printStackTrace();
        }
        NotesThread.stermThread();
    }

    private void addoneInternetSite(Collection<InternetSite> internetSites, Document doc) throws NotesException {
        // Is it an internet site?
        if (!doc.getItemValueString("Form").equals(WEB_FORM)) {
            return; // Not an internet site
        }
        InternetSite is = new InternetSite(doc);
        internetSites.add(is);
    }
}

package com.notessensei.nginxdomino;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import lotus.domino.Document;
import lotus.domino.Item;
import lotus.domino.NotesException;

public class InternetSite {
    
    private static final String SITE_NAME_ITEM = "ISiteName";
    private static final String ALL_NAMES_ITEM = "ISiteAdrs";
    
    private final String name;
    private final ArrayList<String> allNames = new ArrayList<String>();
    private final Map<String, String> settings = new HashMap<String, String>();

    public InternetSite(Document doc) throws NotesException {
        this.name = doc.getItemValueString(SITE_NAME_ITEM);
        @SuppressWarnings("rawtypes")
        Vector v = doc.getItemValue(ALL_NAMES_ITEM);
        for (Object o : v) {
            this.allNames.add(o.toString());
        }
        for (Object o : doc.getItems()) {
            if (o instanceof Item) {
                Item i = (Item) o;              
                this.settings.put(i.getName(), i.getValueString());
            }
        }
    }

    public String getName() {
        return this.name;
    }
    
    public String getSiteName() {
        return this.allNames.get(0);
    }
    
    public Collection<String> getAllNames() {
        return this.allNames;
    }
    
    public Map<String,String> getSettings() {
        return this.settings;
    }
}

As usual YMMV.

Posted by on 20 September 2014 | Comments (1) | categories: IBM Notes nginx

Comments

  1. posted by Ray Davies on Wednesday 24 September 2014 AD:
    This is an awesome article. Thanks Stephan!

    Emoticon biggrin.gif