Update README and slim vcc, check in man page

parent c4d1e08f
......@@ -30,7 +30,7 @@ stamp-h1
# vmodtool
vcc_*_if.[ch]
vmod_*.rst
vmod_hoailona.rst
# man
......
......@@ -11,9 +11,6 @@ resources. This sequence will install the VMOD::
> make check # to run unit tests in src/tests/*.vtc
> sudo make install
``make check`` requires that the VMOD ``blob`` is installed
(https://code.uplex.de/uplex-varnish/libvmod-blob).
If you have installed Varnish in a non-standard directory, call
``autogen.sh`` and ``configure`` with the ``PKG_CONFIG_PATH``
environment variable pointing to the appropriate path. For example,
......
..
.. NB: This file is machine generated, DO NOT EDIT!
..
.. Edit ../src/vmod_hoailona.vcc and run make instead
..
========================================
Akamai SecureHD Token Authorization VMOD
========================================
.. role:: ref(emphasis)
**THIS BRANCH IS FOR VARNISH-CACHE MASTER trunk >7.3 ONLY**
=============
vmod_hoailona
=============
See branches for support of older versions.
----------------------------------------
Akamai SecureHD Token Authorization VMOD
----------------------------------------
.. role:: ref(emphasis)
:Manual section: 3
.. _Varnish-Cache: https://varnish-cache.org/
SYNOPSIS
========
This Varnish Module (VMOD) supports use of the SecureHD Policy service
provided by Akamai Media Services.
.. parsed-literal::
import hoailona [as name] [from "path"]
new xpolicy = hoailona.policy(ENUM type, DURATION ttl, STRING description, BLOB secret, INT start_offset)
new xhosts = hoailona.hosts()
VOID xhosts.add(STRING host, STRING policy, STRING path, STRING description)
INT xhosts.policy(STRING host, STRING path)
STRING xhosts.token(STRING acl, DURATION ttl, STRING data)
BLOB xhosts.secret()
STRING xhosts.explain()
STRING version()
::
PROJECT RESOURCES
=================
new OBJECT = hoailona.policy(ENUM type [, DURATION ttl]
[, STRING description] [, BLOB secret]
[, INT start_offset])
* The primary repository is at https://code.uplex.de/uplex-varnish/libvmod-hoailona
new OBJECT = hoailona.hosts()
<obj>.add(STRING host, STRING policy [, STRING path]
[, STRING description])
INT <obj>.policy(STRING host, STRING path)
STRING <obj>.token([STRING acl] [, DURATION ttl] [, STRING data])
BLOB <obj>.secret()
STRING <obj>.explain()
This server does not accept user registrations, so please use ...
STRING hoailona.version()
* the mirror at https://gitlab.com/uplex/varnish/libvmod-hoailona for issues,
merge requests and all other interactions.
DESCRIPTION
===========
The full documentation of the VMOD is in :ref:`vmod_hoailona(3)`. If
you are reading this document online, it should be available as
`vmod_hoailona.man.rst <src/vmod_hoailona.man.rst>`_.
This Varnish Module (VMOD) supports use of the SecureHD Policy service
provided by Akamai Media Services. Applications of the VMOD include:
......@@ -241,470 +213,47 @@ subroutines, subsequent calls to ``.token()`` and ``.secret()`` in the
same backend transaction are based on the policy that was determined
by that call.
.. _hoailona.policy():
new xpolicy = hoailona.policy(ENUM type, DURATION ttl, STRING description, BLOB secret, INT start_offset)
---------------------------------------------------------------------------------------------------------
::
new xpolicy = hoailona.policy(
ENUM {OPEN, DENY, TOKEN} type,
DURATION ttl=0,
STRING description=0,
BLOB secret=0,
INT start_offset=0
)
Create a policy. The ``type`` enum is required, to classify the policy
as ``OPEN``, ``DENY`` or ``TOKEN``.
When ``TOKEN`` is specified, then a ``ttl`` greater than 0 MUST be
specified; the TTL has no effect for the ``OPEN`` and ``DENY`` types
and may be left out. The TTL determines the length of time for which
token authorization is valid by default. Unless the TTL is overriden,
strings generated by the ``hosts.token()`` method contain parameters
(epoch times) that define the duration of the authorization to
correspond with ``ttl``.
The optional ``secret`` parameter may contain a shared secret for
authorization, which serves as the key for an HMAC. The data type for
``secret`` is BLOB, which cannot be expressed in native VCL, but can
be generated by a VMOD (such as VMOD ``blob``). By default, no
shared secret is stored for the policy.
The optional ``description`` parameter may contain any string; if
present, this string is used in the output of the ``hosts.explain()``
method to describe the policy chosed by ``hosts.policy()``. By default,
no description is stored for a policy.
The optional ``start_offset`` parameter can be used to alter the
"start" time (parameter ``st``) in tokens that are generated based
on this policy. This can be useful, for example, to address issues
of time synchronization between the Akamai server and the host on
which Varnish is running.
By default, ``start_offset`` is 0; in this case ``st`` is unmodified
and is set to the epoch time for "now". When ``start_offset`` is set
to -10, for example, then ``st`` is set to 10 seconds before "now"
(and hence authorization may be less likely to fail due to
unsynchronized clocks).
Examples::
# Open policy, no authorization required
new open = hoailona.policy(OPEN);
# Token authorization required, where authorization lasts 2 hours,
# using the given shared secret, and setting the start offset to
# 10 seconds before "now".
# (Note that in Varnish 5.0.0, the negative integer for start_offset
# must be written as 0-10, because negative literals are not parsed
# correctly.)
import blob;
new token = hoailona.policy(type=TOKEN, ttl=2h, start_offset=0-10,
secret=blob.decode(decoding=HEX, encoded=
"717569636B2062726F776E20666F7879"));
# A policy for "access denied"
new forbid = hoailona.policy(DENY, description="access denied");
.. _hoailona.hosts():
new xhosts = hoailona.hosts()
-----------------------------
Create a ``hosts`` object, which provides a store for a configuration
that associates with policies with hostnames, and optionally with
path patterns. The constructor has no parameters; the object only
becomes useful by calling the ``.add()`` method.
.. _xhosts.add():
VOID xhosts.add(STRING host, STRING policy, STRING path, STRING description)
----------------------------------------------------------------------------
::
VOID xhosts.add(
STRING host,
STRING policy,
STRING path=0,
STRING description=0
)
Associate ``policy`` with the ``host``, optionally restricted to the
path pattern described by ``path``. The ``host`` and ``policy``
parameters are required, and must be non-empty.
Restricted to: ``vcl_init``
The value of ``host`` MUST be a valid host name, optionally beginning
with an asterisk (``*``):
* A host name may only contain alphanumeric characters, ``-`` or
``.``, and optionally the leading ``*``.
* It may not begin with ``.`` or ``-``.
* ``*`` may only appear as the first character.
If ``host`` begins with ``*``, then host names given in the
``.policy()`` method will match any non-empty string at the beginning,
if followed by the same suffix. Thus for example ``*.example.com`` can
be used to specify any subdomain of ``example.com``.
Host names are case-insensitive; that is, ``.policy()`` will match
a host name successfully regardless of case.
Policies must be added in the order for which the host name lookup is
to be carried out later. This is particularly important when the
asterisk is used and more specific entries exist.
The string in ``policy`` MUST be identical to the object name (VCL
symbol) for a ``policy`` object previously defined in ``vcl_init``.
If no such policy object exists, then the VCL load will fail with an
error message.
If no ``path`` parameter is provided, then ``policy`` is assigned
globally to the host; that is, the ``.policy()`` method will determine
that the given policy holds for the host regardless of the path. In
that case, subsequent invocations of ``.add()`` MAY NOT assign any
other policy to the same host name, either globally or restricted to
any path pattern. If the same value of ``host`` appears again in any
other call to ``.add()``, then the VCL load will fail with an error
message.
If a ``path`` parameter is provided, then the policy holds for
``host`` for patterns that match ``path``. The VMOD uses the same
pattern language for path patterns that is used by the Akamai SecureHD
Policy Editor:
* An asterisk matches a single path component, i.e. any non-empty
string of characters that are not ``/``.
* Thus ``/foo/*/bar`` matches ``/foo/baz/bar`` but not
``/foo/baz/quux/bar`` or ``/foo//bar``.
* When three dots (``...``) follow or precede a slash, they match
a non-empty sequence of path components of any length.
* Thus ``/foo/.../bar`` matches both ``/foo/baz/bar`` and
``/foo/baz/quux/bar``, but not ``/foo//bar``.
* ``/foo/bar/...`` matches any path prefixed by ``/foo/bar/``,
but not ``/foo/bar/`` (with no suffix) or ``/foo/bar``.
* ``.../foo/bar`` matches any path ending in ``/foo/bar``,
but not ``/foo/bar`` with no prefix.
* Any other dot in the pattern matches a literal dot in a path.
* Any other characters in the pattern match exactly with a path. Thus
if none of ``*`` or ``...`` appear in ``path``, then ``path``
specifies an exact string match.
The VMOD also enforces the same syntactic restrictions on path
patterns as Akamai:
* Valid characters are alphanumerics, space, or any of these
characters: ``_-~.%:/[]@!$&()*+,;=``
* ``...`` may only appear before or after ``/``.
* Two or more consecutive asterisks are not allowed.
If ``path`` violates any of these restrictions, then the VCL load will
fail with an error message.
Note that, while hosts are matched in order, paths are not. See
:ref:`func_hosts.policy` for details.
If a policy is assigned to a ``host`` and a ``path`` pattern, then
subsequent invocations of ``.add()`` may assign policies to the same
host and different patterns. A later invocation of ``.add()`` MAY NOT
assign a policy to the same host globally, or to the same host and the
same pattern. In either of these cases, the VCL load will fail with an
error message.
The optional ``description`` parameter may be any string, and if
present it is used in the output of ``.explain()``. By default, no
description is set.
Examples::
sub vcl_init {
new p1 = hoailona.policy(OPEN);
new p2 = hoailona.policy(TOKEN, 1h);
new p3 = hoailona.policy(TOKEN, 2h);
new p4 = hoailona.policy(TOKEN, 3h);
new deny = hoailona.policy(DENY);
new h = hoailona.hosts();
# Assign a policy globally to example.com
h.add("example.com", "p1");
# Assign a policy to a fixed path on subdomains of example.com
h.add("*.example.com", "p2", "/foo/bar");
# Assign a policy to any path beginning with /baz/quux/
# on example.org
h.add("example.org", "p3", "/baz/quux/...");
# Assign a policy to any path with three components, where
# the first component is /foo/ and the last is /bar, on example.org
h.add("example.org", "p4", "/foo/*/bar");
# Deny access to any path on evil.org, with a description to be used
# by h.explain()
h.add("evil.org", "deny", description="no access to evil.org");
}
.. _xhosts.policy():
INT xhosts.policy(STRING host=0, STRING path=0)
-----------------------------------------------
Determine the policy type that holds for ``host`` and ``path``. The
return values are:
* 0 for ``DENY``
* 1 for ``OPEN``
* 2 for ``TOKEN``
* -1 if no matching policy can be found
* -2 if there was an internal error
Restricted to: ``client, backend``
The method searches for host names added by the ``.add()`` method that
match ``host`` in order of addition, possibly matching the suffix if
the host name in ``.add()`` began with an asterisk. If it finds a host
for which a policy was assigned globally (without a path pattern),
then it returns the type for that policy.
If it finds a host for which path patterns were defined, it attempts
to match ``path``, respecting the use of ``*`` or ``...`` in the
patterns. It returns the policy type assigned for a matching pattern.
If more than one path pattern was assigned for the host, then it
attempts to match the "most specific" patterns first. The general idea
is: if, for example, the patterns ``/foo/.../bar`` and
``/foo/.../baz/bar`` were assigned for a matching host, and the
``path`` to be matched is ``/foo/quux/baz/bar``, then the more
specific pattern ``/foo/.../baz/bar`` will be matched and the policy
type assigned for that pattern will be returned, even though
``/foo/.../bar`` would have also matched.
Formally, the "more specific" relation is defined as:
* Pattern A is more specific than pattern B if:
* A has more slashes than B
* otherwise (if A and B have the same number of slashes) if
B contains ``...`` and A does not
* else if A has fewer asterisks than B
* else if A is longer than B
* else if A precedes B lexigraphically
Note that, in contrast to hosts, for paths the order in which they
were added with the ``.add()`` method is irrelevant. The rules for
path matching should be identical to those of the Akamai SecureHD
Policy Editor.
Subsequent calls to the ``.token()``, ``.secret()`` or ``.explain()``
methods refer to the most recent invocation of ``.policy()`` in the
same task scope, that is in the same client or backend transaction.
Likewise, if both the ``host`` and ``path`` parameters are empty,
``.policy()`` returns again the result of the most recent invocation
with parameters.
Calling ``.policy()`` with only one of the ``host`` and ``path``
parameters empty is an error.
.. _xhosts.token():
STRING xhosts.token(STRING acl, DURATION ttl, STRING data)
----------------------------------------------------------
::
STRING xhosts.token(
STRING acl=0,
DURATION ttl=0,
STRING data=0
)
If the previous invocation of ``.policy()`` determined policy type
``TOKEN`` (return value 2 from ``.policy()``), then return the
non-cryptographic portion of an authorization token; return NULL if no
matching policy could be determined. There are no required parameters.
Restricted to: ``client, backend``
If none of the optional parameters are specified, then the method
returns a string with the parameters ``st`` and ``exp`` for the start
and end times (as epoch times) for the duration of the authorization.
``st`` is derived from "now", and may be modified by a
``start_offset`` defined for the chosen policy, as described above.
``exp`` is set by adding the duration of the TTL for the chosen policy
to ``st``. So at minimum, ``.token()`` may generate a string like::
st=1484251854~exp=1484255454
The optional ``ttl`` parameter overrides the TTL determined from the
policy; if set, then the ``exp`` parameter is computed accordingly.
The optional parameters ``acl`` and ``data`` may be used to set values
for parameters ``acl`` and/or ``data`` in the token string, if these
are required for your SecureHD authorization. By default, neither of
the ``acl`` or ``data`` parameters are included in the token string.
Examples::
sub vcl_recv {
if (config.policy(req.http.Host, req.url) == 2) {
# This generates the simplest token with default values
set req.http.Tmp = config.token();
# Override the TTL determined from the policy
set req.http.Tmp = config.token(ttl=3h);
# Include values for acl and data in the token string
set req.http.Tmp = config.token(acl="/foo", data="user=foo");
# This last example may generate a token string like:
# st=1484251854~exp=1484255454~acl=/foo~data=user=foo
# The contents of the Tmp header may now be used as
# needed for SecureHD authorization.
}
.. _xhosts.secret():
BLOB xhosts.secret()
--------------------
Return the shared secret stored for the policy determined by the
previous invocation of ``.policy()``. Returns NULL if no such shared
secret was specified, or if no matching policy could be determined.
Restricted to: ``client, backend``
Examples::
import blobdigest;
import blob;
sub vcl_recv {
if (config.policy(req.http.Host, req.url) == 2) {
# Generates the non-crypto part of the token
set req.http.Tmp = config.token();
# Use VMOD blobdigest to generate the HMAC, where
# the shared secret serves as the HMAC key.
set req.http.Tmp-HMAC
= blob.encode(HEX, LOWER,
blobdigest.hmacf(SHA256, config.secret(),
blob.decode(encoded=
req.http.Tmp-Token)));
# Concatenate elements of the authorization token
set req.http.Token = "hdnea=" + req.http.Tmp + "~hmac="
+ req.http.Tmp-HMAC;
# The contents of the Tmp header may now be used as
# a query string or cookie contents, as required for
# authorization at the Akamai server (for example by
# constructing a redirect response in VCL).
}
}
.. _xhosts.explain():
STRING xhosts.explain()
-----------------------
Returns a string describing the policy that was determined for a host
and path by the most recent invocation of ``.policy()`` in the current
task scope (client or backend context), suitable for diagnosis or
logging. The returned string contains:
* The name of the policy object that was determined
* The hostname that matched
* If applicable, the path pattern that matched
If description strings were provided in the declaration of the policy
and/or in the ``.add()`` method call that assigned the policy, then
these are included in the string.
Restricted to: ``client, backend``
Example::
INSTALLATION
============
import std;
See `INSTALL.rst <INSTALL.rst>`_ in the source repository.
sub vcl_recv {
if (config.policy(req.http.Host, req.url) == 2) {
# [...]
}
std.log("Policy determination: " + config.explain());
}
ACKNOWLEDGEMENTS
================
.. _hoailona.version():
Development of this module was sponsored by BILD GmbH & Co. KG
STRING version()
----------------
SUPPORT
=======
Returns the version string for this VMOD.
.. _gitlab.com issues: https://gitlab.com/uplex/varnish/libvmod-hoailona/-/issues
Example::
To report bugs, use `gitlab.com issues`_.
std.log("Using VMOD hoailona version " + hoailona.version());
For enquiries about professional service and support, please contact
info@uplex.de\ .
REQUIREMENTS
CONTRIBUTING
============
This VMOD requires Varnish since version 5.2
.. _merge requests on gitlab.com: https://gitlab.com/uplex/varnish/libvmod-hoailona/-/merge_requests
LIMITATIONS
===========
To contribute to the project, please use `merge requests on gitlab.com`_.
The VMOD uses Varnish workspace for the strings returned by the
``hosts.token()`` method and for task-scoped data saved when the
``hosts.policy()`` method is called. It also uses workspace during
``vcl_init`` for temporary internal data structures needed while
policy and hosts configurations are constructed. If the VMOD's methods
fail with the message ``out of space`` in the Varnish log (with the
log tag ``VCL_Error``), or if VCL initialization fails with such a
message, then you need to increase one or both of the varnishd runtime
parameters ``workspace_client`` and ``workspace_backend``. The size
of workspace during ``vcl_init`` is governed by ``workspace_client``.
It appears to us that the Akamai documentation is not explicit about
whether the ``...`` and ``*`` constructs in the path pattern syntax
are required to match non-empty strings. Such a requirement would
mean, for example, that ``/foo/*/bar`` and ``/foo/.../bar`` do not
match ``/foo//bar``, that ``/foo/bar/...`` does not match
``/foo/bar/`` (with no suffix), and that ``.../foo/bar`` does not
match ``/foo/bar`` (with no prefix). All of their examples show these
constructs matching non-empty strings, and it seems to us to be the
intuitive interpretation. So the VMOD explicitly enforces this
requirement.
To support the project's development and maintenance, there are
several options:
INSTALLATION
============
.. _paypal: https://www.paypal.com/donate/?hosted_button_id=BTA6YE2H5VSXA
See `INSTALL.rst <INSTALL.rst>`_ in the source repository.
.. _github sponsor: https://github.com/sponsors/nigoroll
ACKNOWLEDGEMENTS
================
* Donate money through `paypal`_. If you wish to receive a commercial
invoice, please add your details (address, email, any requirements
on the invoice text) to the message sent with your donation.
Development of this module was sponsored by BILD GmbH & Co. KG
* Become a `github sponsor`_.
* Contact info@uplex.de to receive a commercial invoice for SWIFT payment.
SEE ALSO
========
......
..
.. NB: This file is machine generated, DO NOT EDIT!
..
.. Edit ./vmod_hoailona.vcc and run make instead
..
.. role:: ref(emphasis)
=============
vmod_hoailona
=============
----------------------------------------
Akamai SecureHD Token Authorization VMOD
----------------------------------------
:Manual section: 3
SYNOPSIS
========
.. parsed-literal::
import hoailona [as name] [from "path"]
new xpolicy = hoailona.policy(ENUM type, DURATION ttl, STRING description, BLOB secret, INT start_offset)
new xhosts = hoailona.hosts()
VOID xhosts.add(STRING host, STRING policy, STRING path, STRING description)
INT xhosts.policy(STRING host, STRING path)
STRING xhosts.token(STRING acl, DURATION ttl, STRING data)
BLOB xhosts.secret()
STRING xhosts.explain()
STRING version()
::
new OBJECT = hoailona.policy(ENUM type [, DURATION ttl]
[, STRING description] [, BLOB secret]
[, INT start_offset])
new OBJECT = hoailona.hosts()
<obj>.add(STRING host, STRING policy [, STRING path]
[, STRING description])
INT <obj>.policy(STRING host, STRING path)
STRING <obj>.token([STRING acl] [, DURATION ttl] [, STRING data])
BLOB <obj>.secret()
STRING <obj>.explain()
STRING hoailona.version()
DESCRIPTION
===========
This Varnish Module (VMOD) supports use of the SecureHD Policy service
provided by Akamai Media Services. Applications of the VMOD include:
* Defining policies for access to media content:
* Policy type TOKEN: token authorization required, with a TTL
(time-to-live) limiting the duration of authorized access, and
possibly with a shared secret used for keyed-hash message
authentication codes (HMACs) that are required for authorization
* Policy type OPEN: access permitted without authorization
* Policy type DENY: access denied
* Assigning policies to hosts, either globally for a host, or for
sets of paths defined for the host
* Determining which policy holds for a given host and path
* Generating authorization tokens
This manual presupposes familiarity with the Akamai SecureHD
service. For more information, see the documentation provided by
Akamai (see `Akamai documentation`_).
The VMOD does not provide cryptographic code to generate HMACs, but it
does provide the means to associate shared secrets with a policy,
which can be used together with a VMOD that does compute HMACs (such
as the ``blobdigest`` VMOD, see `SEE ALSO`_).
The name of the VMOD is inspired by the Hawaiian word *ho`ailona*, for
"sign" or "symbol" (pronounced "ho-eye-lona"), which we believe to be
a suitable translation for "token". We welcome feedback from speakers
of Hawaiian about the choice of the name.
Defining policies
-----------------
Policies are defined by means of ``policy`` objects that are
constructed in ``vcl_init``. A policy is defined by its type (TOKEN,
OPEN or DENY), a TTL for the TOKEN type, and possibly a shared secret
used for authorization. For example::
import hoailona;
import blob;
sub vcl_init {
# Define a policy for token authorization lasting one hour,
# and associate it with a shared secret.
new token_policy
= hoailona.policy(TOKEN, 1h,
blob.decode(encoded="secret"));
# Define a policy for open access (authorization not required)
new open_policy = hoailona.policy(OPEN);
# Define an "access denied" policy
new deny_policy = hoailona.policy(DENY);
}
Policy objects have no methods; they become useful when they are
assigned to hosts and paths, as shown in the following.
Assigning policies to hosts and paths
-------------------------------------
Most of the work of the VMOD is done through the ``hosts`` object,
which is used in ``vcl_init`` to assign policies to hostnames, either
globally for a host, or for sets of paths on a host. Patterns for
paths are defined with the same syntax used by Akamai's SecureHD
Policy Editor::
sub vcl_init {
# After policies have been defined as shown above ...
new config = hoailona.hosts();
# Assign the token_policy globally to host example.com
config.add("example.com", "token_policy");
# Assign the open_policy to the path /foo/bar on host example.org
config.add("example.org", "open_policy", "/foo/bar");
# Assign the deny_policy to any path beginning with /baz/quux
# on subdomains of example.org
config.add("*.example.org", "deny_policy", "/baz/quux/...");
}
Policies are assigned by using strings that must exactly match the
object names (the symbols in the VCL source) for policy objects that
were previously defined in ``vcl_init``. Details about permissible
host names and the pattern syntax used for paths are given below.
Determining the policy for a host and path
------------------------------------------
After policies and their assignments to hosts and paths have been
configured in ``vcl_init``, the policy that holds for a given host and
path can be determined from the ``policy`` method of the ``hosts``
object::
sub vcl_recv {
# The policy method returns 0 for policy type DENY
if (config.policy(req.http.Host, req.url) == 0) {
# Handle "access denied" by returning 403 Forbidden
return(synth(403));
}
# .policy() returns 1 for policy type OPEN
if (config.policy() == 1) {
return(pass);
}
# .policy() returns 2 for policy type TOKEN
if (config.policy() == 2) {
# Handle token authorization ...
# [...]
}
}
Generating authorization tokens
-------------------------------
When the policy type TOKEN has been determined for a host and path
(return value 2 from the ``.policy()`` method), the ``.token()``
method can be used to generate the non-cryptographic portion of
the authorization token, and ``.secret()`` can be used to retrieve
the shared secret associated with the policy, to generate the
HMAC for the token::
import blobdigest;
import blob;
sub vcl_recv {
# .policy() returns 2 for policy type TOKEN
if (config.policy(req.http.Host, req.url) == 2) {
# Handle token authorization:
# Assign the non-cryptographic part of the token to a temp
# header
set req.http.Tmp-Token = config.token();
# Use VMOD blobdigest to generate the HMAC, and VMOD blob
# to encode the result in lower case hex.
# The shared secret serves as the HMAC key, and the token just
# assigned to the temp header is the message to be hashed.
set req.http.Tmp-HMAC
= blob.encode(HEX, LOWER,
blobdigest.hmacf(SHA256, config.secret(),
blob.decode(encoded=
req.http.Tmp-Token)));
# These two temp headers can now be combined to form the full
# token string required for authorization at the Akamai
# server, such as:
#
# "hdnea=" + req.http.Tmp-Token + "~hmac=" + req.http.Tmp-HMAC
}
}
At minimum, the string returned by ``.token()`` contains the
parameters ``st`` and ``exp``, whose values are the start and end
times (epoch times) for the duration of the authorization. By default,
the authorization begins "now" and lasts for the duration of the TTL
defined in the policy constructor, but these can be overriden by
optional parameters. Other optional parameters can provide values for
additional token parameters such as ``acl`` and ``data``, as described
below.
The ``.secret()`` method returns the BLOB that was provided in the
constructor of the policy object, for the policy that was determined
for the given host and path. Together with VMODs for cryptography,
this can be used to generate the HMAC for authorization. The HMAC and
the string returned from ``.token()`` can then be combined to form the
URL query string or Cookie as required according to Akamai's APIs
(for example, by generating a redirect response from VCL).
Invocations of the ``.token()`` and ``.secret()`` methods have task
scope, meaning that they refer back to the most recent invocation of
``.policy()`` in the same client or backend transaction. For example,
when ``.policy()`` is called in any of the ``vcl_backend_*``
subroutines, subsequent calls to ``.token()`` and ``.secret()`` in the
same backend transaction are based on the policy that was determined
by that call.
.. _hoailona.policy():
new xpolicy = hoailona.policy(ENUM type, DURATION ttl, STRING description, BLOB secret, INT start_offset)
---------------------------------------------------------------------------------------------------------
::
new xpolicy = hoailona.policy(
ENUM {OPEN, DENY, TOKEN} type,
DURATION ttl=0,
STRING description=0,
BLOB secret=0,
INT start_offset=0
)
Create a policy. The ``type`` enum is required, to classify the policy
as ``OPEN``, ``DENY`` or ``TOKEN``.
When ``TOKEN`` is specified, then a ``ttl`` greater than 0 MUST be
specified; the TTL has no effect for the ``OPEN`` and ``DENY`` types
and may be left out. The TTL determines the length of time for which
token authorization is valid by default. Unless the TTL is overriden,
strings generated by the ``hosts.token()`` method contain parameters
(epoch times) that define the duration of the authorization to
correspond with ``ttl``.
The optional ``secret`` parameter may contain a shared secret for
authorization, which serves as the key for an HMAC. The data type for
``secret`` is BLOB, which cannot be expressed in native VCL, but can
be generated by a VMOD (such as VMOD ``blob``). By default, no
shared secret is stored for the policy.
The optional ``description`` parameter may contain any string; if
present, this string is used in the output of the ``hosts.explain()``
method to describe the policy chosed by ``hosts.policy()``. By default,
no description is stored for a policy.
The optional ``start_offset`` parameter can be used to alter the
"start" time (parameter ``st``) in tokens that are generated based
on this policy. This can be useful, for example, to address issues
of time synchronization between the Akamai server and the host on
which Varnish is running.
By default, ``start_offset`` is 0; in this case ``st`` is unmodified
and is set to the epoch time for "now". When ``start_offset`` is set
to -10, for example, then ``st`` is set to 10 seconds before "now"
(and hence authorization may be less likely to fail due to
unsynchronized clocks).
Examples::
# Open policy, no authorization required
new open = hoailona.policy(OPEN);
# Token authorization required, where authorization lasts 2 hours,
# using the given shared secret, and setting the start offset to
# 10 seconds before "now".
# (Note that in Varnish 5.0.0, the negative integer for start_offset
# must be written as 0-10, because negative literals are not parsed
# correctly.)
import blob;
new token = hoailona.policy(type=TOKEN, ttl=2h, start_offset=0-10,
secret=blob.decode(decoding=HEX, encoded=
"717569636B2062726F776E20666F7879"));
# A policy for "access denied"
new forbid = hoailona.policy(DENY, description="access denied");
.. _hoailona.hosts():
new xhosts = hoailona.hosts()
-----------------------------
Create a ``hosts`` object, which provides a store for a configuration
that associates with policies with hostnames, and optionally with
path patterns. The constructor has no parameters; the object only
becomes useful by calling the ``.add()`` method.
.. _xhosts.add():
VOID xhosts.add(STRING host, STRING policy, STRING path, STRING description)
----------------------------------------------------------------------------
::
VOID xhosts.add(
STRING host,
STRING policy,
STRING path=0,
STRING description=0
)
Associate ``policy`` with the ``host``, optionally restricted to the
path pattern described by ``path``. The ``host`` and ``policy``
parameters are required, and must be non-empty.
Restricted to: ``vcl_init``.
The value of ``host`` MUST be a valid host name, optionally beginning
with an asterisk (``*``):
* A host name may only contain alphanumeric characters, ``-`` or
``.``, and optionally the leading ``*``.
* It may not begin with ``.`` or ``-``.
* ``*`` may only appear as the first character.
If ``host`` begins with ``*``, then host names given in the
``.policy()`` method will match any non-empty string at the beginning,
if followed by the same suffix. Thus for example ``*.example.com`` can
be used to specify any subdomain of ``example.com``.
Host names are case-insensitive; that is, ``.policy()`` will match
a host name successfully regardless of case.
Policies must be added in the order for which the host name lookup is
to be carried out later. This is particularly important when the
asterisk is used and more specific entries exist.
The string in ``policy`` MUST be identical to the object name (VCL
symbol) for a ``policy`` object previously defined in ``vcl_init``.
If no such policy object exists, then the VCL load will fail with an
error message.
If no ``path`` parameter is provided, then ``policy`` is assigned
globally to the host; that is, the ``.policy()`` method will determine
that the given policy holds for the host regardless of the path. In
that case, subsequent invocations of ``.add()`` MAY NOT assign any
other policy to the same host name, either globally or restricted to
any path pattern. If the same value of ``host`` appears again in any
other call to ``.add()``, then the VCL load will fail with an error
message.
If a ``path`` parameter is provided, then the policy holds for
``host`` for patterns that match ``path``. The VMOD uses the same
pattern language for path patterns that is used by the Akamai SecureHD
Policy Editor:
* An asterisk matches a single path component, i.e. any non-empty
string of characters that are not ``/``.
* Thus ``/foo/*/bar`` matches ``/foo/baz/bar`` but not
``/foo/baz/quux/bar`` or ``/foo//bar``.
* When three dots (``...``) follow or precede a slash, they match
a non-empty sequence of path components of any length.
* Thus ``/foo/.../bar`` matches both ``/foo/baz/bar`` and
``/foo/baz/quux/bar``, but not ``/foo//bar``.
* ``/foo/bar/...`` matches any path prefixed by ``/foo/bar/``,
but not ``/foo/bar/`` (with no suffix) or ``/foo/bar``.
* ``.../foo/bar`` matches any path ending in ``/foo/bar``,
but not ``/foo/bar`` with no prefix.
* Any other dot in the pattern matches a literal dot in a path.
* Any other characters in the pattern match exactly with a path. Thus
if none of ``*`` or ``...`` appear in ``path``, then ``path``
specifies an exact string match.
The VMOD also enforces the same syntactic restrictions on path
patterns as Akamai:
* Valid characters are alphanumerics, space, or any of these
characters: ``_-~.%:/[]@!$&()*+,;=``
* ``...`` may only appear before or after ``/``.
* Two or more consecutive asterisks are not allowed.
If ``path`` violates any of these restrictions, then the VCL load will
fail with an error message.
Note that, while hosts are matched in order, paths are not. See
:ref:`func_hosts.policy` for details.
If a policy is assigned to a ``host`` and a ``path`` pattern, then
subsequent invocations of ``.add()`` may assign policies to the same
host and different patterns. A later invocation of ``.add()`` MAY NOT
assign a policy to the same host globally, or to the same host and the
same pattern. In either of these cases, the VCL load will fail with an
error message.
The optional ``description`` parameter may be any string, and if
present it is used in the output of ``.explain()``. By default, no
description is set.
Examples::
sub vcl_init {
new p1 = hoailona.policy(OPEN);
new p2 = hoailona.policy(TOKEN, 1h);
new p3 = hoailona.policy(TOKEN, 2h);
new p4 = hoailona.policy(TOKEN, 3h);
new deny = hoailona.policy(DENY);
new h = hoailona.hosts();
# Assign a policy globally to example.com
h.add("example.com", "p1");
# Assign a policy to a fixed path on subdomains of example.com
h.add("*.example.com", "p2", "/foo/bar");
# Assign a policy to any path beginning with /baz/quux/
# on example.org
h.add("example.org", "p3", "/baz/quux/...");
# Assign a policy to any path with three components, where
# the first component is /foo/ and the last is /bar, on example.org
h.add("example.org", "p4", "/foo/*/bar");
# Deny access to any path on evil.org, with a description to be used
# by h.explain()
h.add("evil.org", "deny", description="no access to evil.org");
}
.. _xhosts.policy():
INT xhosts.policy(STRING host=0, STRING path=0)
-----------------------------------------------
Determine the policy type that holds for ``host`` and ``path``. The
return values are:
* 0 for ``DENY``
* 1 for ``OPEN``
* 2 for ``TOKEN``
* -1 if no matching policy can be found
* -2 if there was an internal error
Restricted to: ``client``, ``backend``.
The method searches for host names added by the ``.add()`` method that
match ``host`` in order of addition, possibly matching the suffix if
the host name in ``.add()`` began with an asterisk. If it finds a host
for which a policy was assigned globally (without a path pattern),
then it returns the type for that policy.
If it finds a host for which path patterns were defined, it attempts
to match ``path``, respecting the use of ``*`` or ``...`` in the
patterns. It returns the policy type assigned for a matching pattern.
If more than one path pattern was assigned for the host, then it
attempts to match the "most specific" patterns first. The general idea
is: if, for example, the patterns ``/foo/.../bar`` and
``/foo/.../baz/bar`` were assigned for a matching host, and the
``path`` to be matched is ``/foo/quux/baz/bar``, then the more
specific pattern ``/foo/.../baz/bar`` will be matched and the policy
type assigned for that pattern will be returned, even though
``/foo/.../bar`` would have also matched.
Formally, the "more specific" relation is defined as:
* Pattern A is more specific than pattern B if:
* A has more slashes than B
* otherwise (if A and B have the same number of slashes) if
B contains ``...`` and A does not
* else if A has fewer asterisks than B
* else if A is longer than B
* else if A precedes B lexigraphically
Note that, in contrast to hosts, for paths the order in which they
were added with the ``.add()`` method is irrelevant. The rules for
path matching should be identical to those of the Akamai SecureHD
Policy Editor.
Subsequent calls to the ``.token()``, ``.secret()`` or ``.explain()``
methods refer to the most recent invocation of ``.policy()`` in the
same task scope, that is in the same client or backend transaction.
Likewise, if both the ``host`` and ``path`` parameters are empty,
``.policy()`` returns again the result of the most recent invocation
with parameters.
Calling ``.policy()`` with only one of the ``host`` and ``path``
parameters empty is an error.
.. _xhosts.token():
STRING xhosts.token(STRING acl, DURATION ttl, STRING data)
----------------------------------------------------------
::
STRING xhosts.token(
STRING acl=0,
DURATION ttl=0,
STRING data=0
)
If the previous invocation of ``.policy()`` determined policy type
``TOKEN`` (return value 2 from ``.policy()``), then return the
non-cryptographic portion of an authorization token; return NULL if no
matching policy could be determined. There are no required parameters.
Restricted to: ``client``, ``backend``.
If none of the optional parameters are specified, then the method
returns a string with the parameters ``st`` and ``exp`` for the start
and end times (as epoch times) for the duration of the authorization.
``st`` is derived from "now", and may be modified by a
``start_offset`` defined for the chosen policy, as described above.
``exp`` is set by adding the duration of the TTL for the chosen policy
to ``st``. So at minimum, ``.token()`` may generate a string like::
st=1484251854~exp=1484255454
The optional ``ttl`` parameter overrides the TTL determined from the
policy; if set, then the ``exp`` parameter is computed accordingly.
The optional parameters ``acl`` and ``data`` may be used to set values
for parameters ``acl`` and/or ``data`` in the token string, if these
are required for your SecureHD authorization. By default, neither of
the ``acl`` or ``data`` parameters are included in the token string.
Examples::
sub vcl_recv {
if (config.policy(req.http.Host, req.url) == 2) {
# This generates the simplest token with default values
set req.http.Tmp = config.token();
# Override the TTL determined from the policy
set req.http.Tmp = config.token(ttl=3h);
# Include values for acl and data in the token string
set req.http.Tmp = config.token(acl="/foo", data="user=foo");
# This last example may generate a token string like:
# st=1484251854~exp=1484255454~acl=/foo~data=user=foo
# The contents of the Tmp header may now be used as
# needed for SecureHD authorization.
}
.. _xhosts.secret():
BLOB xhosts.secret()
--------------------
Return the shared secret stored for the policy determined by the
previous invocation of ``.policy()``. Returns NULL if no such shared
secret was specified, or if no matching policy could be determined.
Restricted to: ``client``, ``backend``.
Examples::
import blobdigest;
import blob;
sub vcl_recv {
if (config.policy(req.http.Host, req.url) == 2) {
# Generates the non-crypto part of the token
set req.http.Tmp = config.token();
# Use VMOD blobdigest to generate the HMAC, where
# the shared secret serves as the HMAC key.
set req.http.Tmp-HMAC
= blob.encode(HEX, LOWER,
blobdigest.hmacf(SHA256, config.secret(),
blob.decode(encoded=
req.http.Tmp-Token)));
# Concatenate elements of the authorization token
set req.http.Token = "hdnea=" + req.http.Tmp + "~hmac="
+ req.http.Tmp-HMAC;
# The contents of the Tmp header may now be used as
# a query string or cookie contents, as required for
# authorization at the Akamai server (for example by
# constructing a redirect response in VCL).
}
}
.. _xhosts.explain():
STRING xhosts.explain()
-----------------------
Returns a string describing the policy that was determined for a host
and path by the most recent invocation of ``.policy()`` in the current
task scope (client or backend context), suitable for diagnosis or
logging. The returned string contains:
* The name of the policy object that was determined
* The hostname that matched
* If applicable, the path pattern that matched
If description strings were provided in the declaration of the policy
and/or in the ``.add()`` method call that assigned the policy, then
these are included in the string.
Restricted to: ``client``, ``backend``.
Example::
import std;
sub vcl_recv {
if (config.policy(req.http.Host, req.url) == 2) {
# [...]
}
std.log("Policy determination: " + config.explain());
}
.. _hoailona.version():
STRING version()
----------------
Returns the version string for this VMOD.
Example::
std.log("Using VMOD hoailona version " + hoailona.version());
LIMITATIONS
===========
The VMOD uses Varnish workspace for the strings returned by the
``hosts.token()`` method and for task-scoped data saved when the
``hosts.policy()`` method is called. It also uses workspace during
``vcl_init`` for temporary internal data structures needed while
policy and hosts configurations are constructed. If the VMOD's methods
fail with the message ``out of space`` in the Varnish log (with the
log tag ``VCL_Error``), or if VCL initialization fails with such a
message, then you need to increase one or both of the varnishd runtime
parameters ``workspace_client`` and ``workspace_backend``. The size
of workspace during ``vcl_init`` is governed by ``workspace_client``.
It appears to us that the Akamai documentation is not explicit about
whether the ``...`` and ``*`` constructs in the path pattern syntax
are required to match non-empty strings. Such a requirement would
mean, for example, that ``/foo/*/bar`` and ``/foo/.../bar`` do not
match ``/foo//bar``, that ``/foo/bar/...`` does not match
``/foo/bar/`` (with no suffix), and that ``.../foo/bar`` does not
match ``/foo/bar`` (with no prefix). All of their examples show these
constructs matching non-empty strings, and it seems to us to be the
intuitive interpretation. So the VMOD explicitly enforces this
requirement.
INSTALLATION
============
See `INSTALL.rst <INSTALL.rst>`_ in the source repository.
ACKNOWLEDGEMENTS
================
Development of this module was sponsored by BILD GmbH & Co. KG
SEE ALSO
========
* varnishd(1)
* vcl(7)
* source repository: https://code.uplex.de/uplex-varnish/libvmod-hoailona
* VMOD blobdigest: https://code.uplex.de/uplex-varnish/libvmod-blobdigest
Akamai documentation
--------------------
Technical documentation about SecureHD token authorization appears to
be available only to Akamai customers who have access to the Luna
Control Center. This public document gives a non-technical overview:
* https://www.akamai.com/jp/ja/multimedia/documents/product-brief/securehd-media-content-security-product-brief.pdf
Users of the Luna Control Center can consult:
* SecureHD Policy Editor User's Guide
* https://control.akamai.com/dl/customers/SPE/spe_ug.pdf
* SecureHD Policy Editor online help
* https://control.akamai.com/dl/SPE/index.htm
* Sanctioned token generator code (since code is the best documentation)
* https://control.akamai.com/dl/customers/SPE/EdgeAuth-latest.zip
COPYRIGHT
=========
::
This document is licensed under the same conditions
as the libvmod-hoailona project. See LICENSE for details.
Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
......@@ -584,11 +584,6 @@ Example::
std.log("Using VMOD hoailona version " + hoailona.version());
REQUIREMENTS
============
This VMOD requires Varnish since version 5.2
LIMITATIONS
===========
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment