Note that the API of this VMOD is considered experimental at this time and may change in subsequent releases. Such changes will be highlighted in the release notes.
The cwt VMOD enables verification and validation of CBOR Web Tokens
(CWTs) from VCL. CWT is derived from JSON Web Token (JWT) but uses CBOR
(Concise Binary Object Representation) rather than JSON.
A CWT is a set of claims encapsulated inside a COSE object (CBOR Object Signature and Encryption) cryptographically verifiable. An application can use these claims to enforce access control over privileged resources.
Please note however that it is up to the application to know the resources that should be guarded. This VMOD can validate claims that can be evaluated automatically, and it can give access to claims that can be represented in VCL, but it cannot expose claims using more complex types.
The following claims are supported:
iss: Issuersub: Subjectaud: Audienceexp: Expiration Timenbf: Not Beforeiat: Issued Atcti: CWT IDA Common Access Token (CAT) is a CWT that may contain CAT-specific claims. When working with a CAT, tokens follow acceptance rules stricter than CWT validation. For example, unknown claims must be ignored for CWT validation but they must be rejected for CAT acceptance.
Moreover, there are several locations from where Common Access Tokens may be imported, in which case accepting one token is enough to accept the client request.
The following claims are supported:
catv: CAT Versioncatnip: Network IPcatu: URIcatm: Methodcath: HeaderFor catu and cath claims, regular expression, SHA-256 and SHA-512/256
matching is not implemented.
This implements basic handling with verification and validation of CWT claims from the client. The COSE keys registered here are lifted from the RFC 8392, and should not be used on any production server.
vcl 4.1;
import cwt;
sub vcl_init {
# RFC 8392 Appendix A.2.2
cwt.import_cose_key(hex={"
a4205820403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d
795693880104024c53796d6d65747269633235360304
"});
# RFC 8392 Appendix A.2.3
cwt.import_cose_key(hex={"
a72358206c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858
bc206c1922582060f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168db
9529971a36e7b9215820143329cce7868e416927599cf65a34f3ce2ffda55a7e
ca69ed8919a394d42f0f2001010202524173796d6d6574726963454344534132
35360326
"});
}
}
sub vcl_recv {
if (!cwt.import_cat()) {
return (synth(400, cwt.get_error()));
}
if (!cwt.accept()) {
return (synth(403, cwt.get_error()));
}
}
The cryptographic verification of a CWT requires its public key. Keys can
be loaded from vcl_init and they will be used for the entire life time
of the VCL. Loading new keys requires a VCL reload.
Keys have identifiers (kid). More than one key can be loaded with the
same identifier, but that is generally discouraged. This can be used for
example during a key rotation, where two different keys may share the same
kid. It can also be the same kid used for different key types, for
different cryptographic algorithms.
For this reason, keys loaded last take precedence over keys loaded first.
It should be noted that keys may describe incomplete algorithms, also known as polymorphic algorithms. For example the EdDSA algorithm could lack a curve parameter specifying the underlying Edwards curve. Another example is the ECDSA algorithm where the number of bits does not match between the curve and the digest, which is not recommended.
This VMOD only accepts fully-specified algorithms, but it will accept a polymorphic algorithm identifier when all parameters are specified, and when applicable, only if it follows recommendation.
Keys can be loaded in the COSE format, and other formats will be supported.
VOID import_cose_key([STRING base64url], [STRING hex], [BLOB cbor])
Import the given COSE_Key object and add it to the key ring.
Exactly one of base64url, hex, or cbor must be specified. Failing
to import a key fails the VCL load.
The cryptographic signature verification and decryption algorithms currently supported are:
Arguments:
base64url accepts type STRING
hex accepts type STRING
cbor accepts type BLOB
Type: Function
Returns: None
Restricted to: vcl_init
VOID import_cose_keyset([STRING base64url], [STRING hex], [BLOB cbor])
Import the given COSE_KeySet object and add all of its keys to the key ring.
Exactly one of base64url, hex, or cbor must be specified. Failing
to import a key fails the VCL load.
The order of keys is maintained once imported. For example if a key with a
kid “previous” was imported before a key set, and that key set contains
keys with kid “first” and “last”, the new lookup order in the key ring
becomes “first”, “last” and “previous”.
Tokens can be imported in the current client task from vcl_recv. Multiple
tokens can be imported in the same task, but a token provenance MUST be
specified in order to load more than one at a time. All functions operating
on tokens may take a from argument. The syntax for provenance is the same
as VCL variables:
-)Provenance must be unique within a client task.
Arguments:
base64url accepts type STRING
hex accepts type STRING
cbor accepts type BLOB
Type: Function
Returns: None
Restricted to: vcl_init
BOOL import_cwt([STRING base64url], [STRING hex], [BLOB cbor], [STRING from])
Import a CWT into the current client task.
Exactly one of base64url, hex, or cbor must be specified.
Returns true on success and false otherwise.
Arguments:
base64url accepts type STRING
hex accepts type STRING
cbor accepts type BLOB
from accepts type STRING
Type: Function
Returns: Bool
Restricted to: vcl_recv
BOOL import_cat()
Automatically import Common Access Tokens from standard locations with a provenance identifying that location.
For now, only one location is scanned:
cat-hdr: the req.http.CTA-Common-Access-Token variableIt is however possible to import tokens from other locations with the help of other VMODs.
Returns true if all tokens were successfully imported and false otherwise.
Arguments: None
Type: Function
Returns: Bool
Restricted to: vcl_recv
BOOL verify([STRING from])
Verify the cryptographic signature of all imported CWTs, or the token
specified by the from argument.
Returns true if all tokens were successfully verified and false otherwise.
Arguments:
from accepts type STRINGType: Function
Returns: Bool
Restricted to: vcl_recv
BOOL validate([DURATION clock_skew], [INT asn], [STRING from])
Validate known claims of all imported CWTs, or the token specified by the
from argument. If a claim cannot semantically be validated, for example
the indented audience (aud), claim validation is limited to ensuring a
well-formed value.
The following arguments can influence token validation:
clock_skew: the time window tolerated for time-based checksasn: the ASN of the client, see VMOD asnReturns true if all tokens were successfully validated and false otherwise.
Arguments:
clock_skew accepts type DURATION
asn accepts type INT
from accepts type STRING
Type: Function
Returns: Bool
Restricted to: vcl_recv
BOOL accept([DURATION clock_skew], [INT asn])
Checks whether all claims can be accepted for at least one imported CWT, or
the token specified by the from argument. If a claim cannot semantically
be validated, for example the token issuer (iss), the check is limited
to ensuring a well-formed claim value.
Unknown claims are rejected.
Before checking the claims, the token signature is also verified.
This matches the acceptance criteria for Common Access Tokens.
The following arguments can influence token validation:
clock_skew: the time window tolerated for time-based checksasn: the ASN of the client, see VMOD asnReturns true if at least one token can be accepted and false otherwise.
Arguments:
clock_skew accepts type DURATION
asn accepts type INT
Type: Function
Returns: Bool
Restricted to: vcl_recv
STRING get_tokens()
Returns a comma-separated list of token provenances, for example:
"cat-query,cat-cookie"
Arguments: None
Type: Function
Returns: String
Restricted to: client
STRING get_error([STRING from])
Get the last error message for a specific token after an operation, or the
first error found at the task or token level when from is not specified.
An operation is a call to any function in the token management section. An operation clears all errors before executing.
The following functions access information about a token, when possible.
Known claims can be accessed by name with the name argument, and any claim
can be accessed by its numeric identifier with the key argument. Only one
of name or key must be used at a time.
When provenance is not specified, the first imported token is searched.
When well-known claims are not found, they return the same value as the
get_claim_<type>() function matching their return type.
Arguments:
from accepts type STRINGType: Function
Returns: String
Restricted to: client
BLOB get_claim_blob([STRING name], [INT key], [STRING from])
Search a claim with a binary string value.
Returns a BLOB on success or null on failure.
Arguments:
name accepts type STRING
key accepts type INT
from accepts type STRING
Type: Function
Returns: Blob
Restricted to: client
INT get_claim_int([STRING name], [INT key], [STRING from])
Search a claim with an integer value.
Returns an INT on success or 0 on failure.
Arguments:
name accepts type STRING
key accepts type INT
from accepts type STRING
Type: Function
Returns: Int
Restricted to: client
STRING get_claim_string([STRING name], [INT key], [STRING from])
Search a claim with an string value. The result must be allocated in the workspace.
Returns a STRING on success or null on failure.
Arguments:
name accepts type STRING
key accepts type INT
from accepts type STRING
Type: Function
Returns: String
Restricted to: client
TIME get_claim_time([STRING name], [INT key], [STRING from])
Search a claim with a value that can be converted to TIME.
Returns the TIME value on success or 0.0 on failure.
Arguments:
name accepts type STRING
key accepts type INT
from accepts type STRING
Type: Function
Returns: Time
Restricted to: client
STRING get_iss([STRING from])
Get the well-known issuer claim from a CWT.
Arguments:
from accepts type STRINGType: Function
Returns: String
Restricted to: client
STRING get_sub([STRING from])
Get the well-known subject claim from a CWT.
Arguments:
from accepts type STRINGType: Function
Returns: String
Restricted to: client
TIME get_exp([STRING from])
Get the well-known expiry claim from a CWT.
Arguments:
from accepts type STRINGType: Function
Returns: Time
Restricted to: client
TIME get_nbf([STRING from])
Get the well-known not-before claim from a CWT.
Arguments:
from accepts type STRINGType: Function
Returns: Time
Restricted to: client
TIME get_iat([STRING from])
Get the well-known issued-at claim from a CWT.
Arguments:
from accepts type STRINGType: Function
Returns: Time
Restricted to: client
BLOB get_cti([STRING from])
Get the well-known CWT ID claim from a CWT.
Arguments:
from accepts type STRINGType: Function
Returns: Blob
Restricted to: client
INT get_catv([STRING from])
Get the well-known CAT version claim from a CWT.
Arguments:
from accepts type STRINGType: Function
Returns: Int
Restricted to: client