=pod

=head1 NAME

nginx-confgen - A preprocessor and macro system for nginx(-like) configuration
files.

=head1 SYNOPSIS

nginx-confgen -i input.conf -o output.conf

=head1 DESCRIPTION

nginx-confgen can be used to do pre-processing for nginx configuration files
(and other configuration files with a similar syntax). It has support for
"compile-time" macro expansion and variable interpolation, which should make it
less tedious to maintain large and complex configurations.

nginx-confgen does not currently support any command-line arguments. It simply
reads the configuration from standard input, and writes the processed
configuration to standard output.

nginx-confgen works by parsing the input into a syntax tree, modifying this
tree, and then formatting the tree to generate the output. It is completely
oblivious to nginx contexts and directives, so it is possible to do nonsensical
transformations and generate incorrect configuration files. Comments in the
input file will not be present in the output. See also the L</BUGS & WARTS>
below.

B<WARNING:> Do NOT use nginx-confgen with untrusted input, the C<pre_exec>
directive allows, by design, arbitrary code execution.

=head1 OPTIONS

The following command-line options are supported:

=over

=item -h

Show help text.

=item -V, --version

Show program version.

=item -i I<FILE>

Use the given file name as input file. If this option is not given or set to
C<->, then the file will be read from standard input.

=item -o I<FILE>

Write the output to the given file. If this option is not given or set to C<->,
then the file will be written to standard output.

=item -I I<DIR>

Set the search path for I<pre_include> directives. This option can be given
multiple times to search several directories in order. If this option is not
given, then include files are resolved relative to the directory that
nginx-confgen is run from (i.e. C<-I .>).

=back


=head1 DIRECTIVES

nginx-confgen recognizes and interprets the following directives:

=head2 pre_include

Similar to the C<include> directive in nginx, except that the file is included
during preprocessing. The included file may contain any preprocessing
directives supported by nginx-confgen. Variables and macros defined in the
included file will be available in the parent file.

Relative paths are searched for in the directories given with the C<-I> flag.

=head2 pre_set

Similar to the C<set> directive in nginx, except that variables defined with
C<pre_set> are resolved during preprocessing. Note that variables defined with
C<pre_set> are only available in the same scope as they are defined, for
example:

  pre_set $var outer;
  location / {
    pre_set $var inner;
    # $var is now "inner" within this location block.
  }
  # $var is "outer" again after the location block.

(This may change in the future)

=head2 pre_exec

Run a shell command, and store the output in a variable. For example, nginx
will not use your system's DNS resolution methods to resolve domain names.
Instead you need to manually set a C<resolver> address. With the following hack
you can fetch the nameserver from C</etc/resolv.conf> and use that as the
C<resolver>:

  pre_exec $nameserver "grep nameserver /etc/resolv.conf \\
                        | head -n 1 | sed 's/^nameserver //'";
  resolver $nameserver;

(The C<\\> is necessary, otherwise your shell will consider the newline as a
new command).

=head2 pre_if

Similar to the C<if> directive in nginx, except that this is evaluated during
preprocessing. Also unlike C<if>, parenthesis around the arguments are not
supported. Some examples:

  pre_if -f $certdir/ocsp.der {
    ssl_stapling on;
    ssl_stapling_file $certdir/ocsp.der;
  }
  pre_if !-f $certdir/ocsp.der {
    ssl_stapling off;
  }

  # You can have different configuration depending on the name of
  # the system on which nginx-confgen runs. Like... yeah.
  pre_exec $hostname 'hostname';
  pre_if $hostname ~* ^proxy_for_(.+) {
    proxy_pass http://$1/;
  }

=head2 pre_warn

This directive, when interpreted, will generate a warning to the standard error
of nginx-confgen. Can be used to signal that a special configuration is being
used:

  pre_if -e /etc/offline-mode {
    pre_warn "Putting website in offline mode!";
  }

Or to warn about certain directives:

  pre_macro proxy_cache $var {
    pre_warn "Using proxy_cache with $var violates company policy!";

    # But we can output it anyway.
    proxy_cache $var;
  }

=head2 macro

Define a I<macro>, which is a configuration block that you can later refer to.
The general syntax is as follows:

  macro macro_name $var1 $var2 @remaining_vars &block_var {
    # contents
  }

The optional C<@remaining_vars> argument will capture any number of variables,
and can be passed to another directive inside the macro contents. The optional
C<&block_var> allows the macro to be invoked with a block argument, which will
expand to any number of directives. Some examples:

  macro le {
    location /.well-known/acme-challenge {
      alias /etc/letsencrypt/challenge;
    }
  }
  # Usage:
  le;

  macro redir $path $to {
    location $path {
      return 301 $to;
    }
  }
  # Usage:
  redir / http://blicky.net/;

  macro vhost $primary_name @aliases &block {
    server {
      listen [::]:443 ssl;
      server_name $primary_name @aliases;
      ssl_certificate $crtdir/$primary_name/fullchain.pem;
      ssl_certificate_key $crtdir/$primary_name/privkey.pem;
      &block;
    }
  }
  # Usage:
  vhost example.com {
    root /var/www/example.com;
  }
  vhost example.org alias.example.org {
    root /var/www/example.org;
  }

Note that these are I<hygienic> macros, so variable capture is predictable (but
not necessarily the most useful):

  pre_var $dest /a;
  macro redir {
    # This will be /a, regardless of the context in which this macro is called.
    return 301 $dest;
  }
  # $dest is still '/a' inside the macro after this new variable definition.
  pre_var $dest /b;
  redir;

Similarly, macro arguments will not be available inside C<&block> expansion or
nested macro expansion.


=head1 BUGS & WARTS

nginx-confgen is a quickly written hack to solve a particular use case, it is
quite likely to have some weird behavior and bugs.

Comments and whitespace in the input files are thrown away and ignored. The
generated output is completely reformatted.

The nginx configuration syntax is not as regular as I had hoped. It's possible
for nginx modules to extend the syntax somewhat. A good example is the I<types>
directive in I<ngx_http_core_module>. While nginx-confgen should be able to
handle the I<types> directive just fine, other extensions may cause syntax
errors or will not survive a round-trip through nginx-confgen.
This applies to all I<*_by_lua_block> directives in the I<ngx_http_lua_module>.
The I<_by_lua> directives that accept a string should work just fine.

The error messages given by C<nginx-confgen> aren't always helpful.


=head1 AUTHOR

nginx-confgen is written by Yoran Heling <projects@yorhel.nl>

Web: L<https://dev.yorhel.nl/nginx-confgen>
