NAME

pmx-policy-tutorial - Policy Script Tutorial


DESCRIPTION

PureMessage filters email according to the configuration contained in the policy script (policy.siv, located by default in the directory /opt/pmx/etc). The policy script can be modified via the PureMessage Manager, or can be edited with standard text editing programs such as 'vi'. This tutorial describes the syntax used in the policy script, analyzes the default PureMessage policy script, and shows examples of common policy script modifications.

Refer to the pmx-policy manpage for the policy command reference. Refer to 'Policy Configuration' in the PureMessage User Guide for a general overview of the PureMessage policy, and links to the PureMessage Manager interface for modifying policies.

  
=head2 Policy Script Syntax Overview

This section introduces the PureMessage policy syntax. Examples in this overview use pseudocode to best describe policy syntax structure and generically demonstrate various PureMessage mail-filtering scenarios. Specific PureMessage policy syntax is discussed later in PureMessage Default Policy Overview.

The PureMessage policy script, called policy.siv, is written using a variation of the Sieve mail filtering language. Sieve is a simple command-based scripting language; its syntax resembles C or Perl, but it has no variables or looping constructs. In its simplest form, a Sieve script is a set of commands executed in a sequence. The commands in the PureMessage policy.siv script can be actions, action modifiers, tests, controls, blocks or comments.

The following example identifies the commands in a policy filter:

    # This script performs a virus test.
    # Messages containing viruses are filed as SPAM and then quarantined.
    if pmx_virus {
        pmx_file "SPAM";
        pmx_quarantine "Virus";
        stop;
    }

Description:

Actions

Policy actions can modify the routing or content of a message. Possible actions include: keep; (deliver message), reject; (reject message), and pmx_quarantine; (quarantine message). Actions always end with a semicolon. Semicolons delimit when an action ends.

Generic Example:

    action;

Policy Script Example:

    keep;
    reject;
    pmx_quarantine;

Action Modifiers

Action modifiers are optional parameters that alter the outcome of a particular policy action. For example, the pmx_quarantine action modifier is a string argument that specifies the reason that PureMessage quarantined a message. Reasons could include 'Virus', 'Spam', or 'Attachment Over 100K'.

Generic Example:

    action <action-modifier>;

Policy Script Example:

    pmx_quarantine "Spam";

Note: Not all PureMessage action modifiers are of type 'string'. See the pmx-policy manpage for a definitive list of all syntax parameters and action modifiers.

Blocks

PureMessage policy actions are grouped together in blocks. Blocks act like containers; they hold actions together. A block begins with an opening brace '{' and ends with a closing brace '}'. Every action in a block must end with a semicolon. The block itself ends with a closing brace.

Generic Example:

    {
    action;
  }

Policy Script Example:

    {
    keep;
  }

In this example, the policy block contains the single action keep. The keep command delivers the message to all envelope recipients. Blocks can, however, contain one or many actions. When multiple actions are listed inside a block, each action is executed in a sequence from top to bottom.

Policy Script Example:

    {
    pmx_quarantine "Blacklisted";
    stop;
  }

In this example, the policy block contains a sequence of two mail filtering actions. The message is first quarantined with the reason 'Blacklisted', and then the script stops. Each action is delimited with a semicolon to indicate to the interpreter when each action ends.

Controls

Control commands determine whether a block of actions are executed, or if no further actions should execute. Control keywords are positioned with an associated test command before the opening brace in a block to help control the order of execution through the policy script. Note that tests are covered further in the next section of the tutorial. For now, understand that a test command evaluates to either 'true' or 'false'. The PureMessage policy.siv script uses three control keywords to filter messages: if, else, and stop.

if

The most basic control command is the if keyword. When the interpreter sees an if command, it evaluates the associated test. If the test is 'true', the actions within the block execute.

Generic Example:

    if test {
       action;
       next_action;
    }

Policy Script Example:

    if pmx_virus {
        reject "Virus Found in Message";
        stop;
    }

This example tests if a message contains a virus. If the test is 'true' and the message contains a virus, the actions within the block execute from top to bottom. The message is rejected with reason 'Virus Found in Message', and message filtering stops. But what happens if the test is 'false' and the message is legitimate?

else

The else command can execute when an if command fails. The if/else control statements work together in a policy script to filter messages.

Generic Example:

    if test {
      action;
      next_action;
    }
    else {
      action;
      next_action;
    }

In this example, if the test is 'true', execute the first set of statements. else, execute the second.

Policy Script Example:

    if pmx_virus {
        reject "Virus Found in Message";
        stop;
    }
    else {
        keep;
    }

In this policy filter, the if and else control statements work together with tests and actions to either reject a virus-laden message or to deliver virus-free mail to envelope recipients. The else command executes when the if statement fails.

stop

The stop control halts execution of the policy script. Unlike the if and else control commands, the stop command is used within a block to end message filtering when the required actions have executed and message filtering is no longer required.

Generic Example:

    if test {
        action;
        next_action;
        stop;
    }

Policy Script Example:

    if pmx_relay :memberof "whitelisted-hosts" {
        keep;
        stop;
    }

In this example, the filter delivers the message if the host is on the PureMessage 'whitelisted-hosts' list. The stop control command ends the policy script after message delivery.

Tests

Tests act as block gatekeepers. They check whether a certain condition in the policy script is met. A test is positioned with a control command before the opening brace in a block to determine whether the policy actions should execute. If a condition is met, the test is 'true' and the actions within the block execute. If the test fails, the condition is 'false' and no actions will execute for that particular policy.

Generic Example:

    if test {
        action;
        next_action;
    }

Policy Script Example:

    if pmx_virus {
        reject "Virus Found in Message";
        stop;
    }

In this example, a message must pass the pmx-virus test before the actions in the block can execute. If the test is 'true' and the message contains a virus, the message is rejected and the policy script stops filtering the message. If the message does not contain a virus, the test fails and no actions from the block will execute.

Match-Types

A match-type performs a matching operation on a particular feature of a message. In the policy script, the match-type modifies a test to check for specific characteristics.

For example, the :is match-type is used here to modify the envelope test to determine if a message is 'From' 'spammer@foo.com'.

    if envelope :is "From" "spammer@foo.com" {
        pmx_quarantine "SPAM";
    }

PureMessage uses several match-types to filter messages. These include: :memberof for matching content in lists, :contains and :is for matching strings, :over or :under for numeric comparison, and :re for searching messages with regular expressions. Depending on the specific match-type used, additional parameters will have to be specified. See 'MATCH-TYPE' in the the pmx-policy manpage guide for more information.

Generic Example:

    if test :match_type {
        action;
        next_action;
    }

Lists

Use the :memberof match-type tag to determine if a sender or host is on a particular PureMessage list. Lists are enclosed in quotes when passed as arguments with the :memberof match-type tag.

Policy Script Example:

    if envelope :memberof "From" "whitelisted-senders" {
        keep;
        stop;
    }

In this example, the match-type :memberof takes a PureMessage 'whitelisted-senders' list as an argument. If the message sender is matched with a sender on this PureMessage list, the message is delivered to the envelope recipient.

Strings

Use the :contains or :is match-type to determine if a text string can be matched against content in a message. Strings are enclosed in quotes when passed as arguments with the :memberof and :is match-type tags.

Policy Script Example:

    if header :contains "Buy Now!" {
        pmx_quarantine "SPAM";
        stop;
    }

In this example, the match-type :contains takes a text string as an argument. If the string 'Buy Now!' is matched with content in any message header, the message is quarantined with the reason 'SPAM'. The stop command ends filter execution.

Numeric Comparison

Use either the :over or :under match-type tags to specify a numeric comparison. Both tags take a number as an argument to determine whether a test is 'true' or 'false'. For example, the :over tag can compare a message's spam probability to determine if a particular message should be delivered.

Policy Script Example:

    if pmx_spam_prob :over 50 {
        pmx_quarantine;
        stop;
    }

In this example, the :over match-type is used to perform a numeric comparison. The :over match-type takes a number argument. The test is 'true' if the message has a spam probability of over 50%.

Policy Script Example:

    if size :over 100K {
        pmx_file;
        discard;
    }

This example tests to see if the total message size is over 100K. If the message is over 100K, a copy of the message is filed in the quarantine. The message is then discarded.

Regular Expressions

Regular expressions can be used as match-type operators for many types of policy rule tests. For example, a regular expression can be used to test the contents of a message's 'Envelope To' field. Certain policy rule actions also support the use of regular expressions. For example, the 'Deliver immediately for' action supports the use of a regular expression as a match-type for exceptions.

Policy Script Example:

    if pmx_relay :re ".*\.com$" {
        keep;
        stop;
    }

In this example, the match-type :re is used to perform a regular expression comparison. If the sender's address ends in the string '.com', the message is delivered and the filter stops evaluating the message.

Note: The :re match-type is a PureMessage extension to the Sieve language. When regular expressions are used in PureMessage policy rule tests or actions, they are not prefixed or suffixed with slashes or braces. However, when manually editing the policy script on the command-line, you must 'escape' backslashes, quotes and periods within regular expressions by preceding them with a backslash. For example, to search for the string 'and\or', enter 'and\\or' as the regular expression. The PureMessage Manager, a web-based interface for managing PureMessage, will automatically escape these characters for you.

For information about using regular expressions with the policy script via the PureMessage Manager, see 'Policy Rule Tests' and 'Policy Rule Actions' in the Policy section of the PureMessage Administrator's Reference. If you are manually editing the policy script, see the pmx-policy manpage (the documentation for the command-line interface to the PureMessage policy engine). See the 'Regular Expressions Primer' in the PureMessage Administrator's Reference for general information about regular expressions.

Compound Tests, allof and anyof

Some tests in the PureMessage policy script are used as logical operators. These tests are called compound tests as they take other tests as arguments. Use the allof or anyof commands to build a compound test. The allof command functions as logical AND. (All compound tests in the allof list must be 'true' for the corresponding actions in a block to execute.) The anyof command functions as a logical OR. (Only one of the compound tests in the anyof list needs to be 'true' for the actions in the block to execute).

In the policy script, the allof and anyof commands take a test and a list as arguments. To build a compound test, group individual tests and associated arguments in parentheses before the beginning of a block statement. Each compound test in the list must be separated with a single comma ',' to delimit the end of each test.

Generic Example:

    if anyof (test :match_type "list", test :match_type "list") {
        action;
        next_action;
    }

In this example, the anyof command takes two tests. Each test takes a match-type with a list argument. The compound test is enclosed in parentheses. Each test is separated with a comma character.

Policy Script Example:

    if anyof (host :memberof "whitelisted-hosts",
              sender :memberof "whitelisted-senders") {
        keep;
        stop;
    }

This policy filter evaluates both the host and sender for membership on a PureMessage 'whitelist'. If either the host or sender are found on a 'whitelist', the message is delivered to all envelope recipients. The script then halts with the stop command.

Comments

Comments are used in the policy script to document how a particular test, action, or control statement contributes to a filter. For each line of commented text, use the '#' character followed by a single space to denote information the Sieve interpreter should ignore.

Note: Comments should not be placed within multi-line strings.

Generic Example:

    # Comments
    # More comments
    if test { # This is a valid comment!
        action;
        next_action;
    }

Policy Script Example:

    # This filter scans all message headers for the words "Buy Now!"
    # If this string is found, PureMessage rejects the message
    # and the filter stops evaluating the message for spam.
    if header :contains "Buy Now!" {
        reject;
        stop;
    }

PureMessage Default Policy Overview

This section reviews the structure, syntax, and execution of each filter in the default policy.siv script. The policy script is executed for each message processed by PureMessage. It can test various characteristics of the message, and perform a variety of actions based on the results of those tests.

Note: The default policy script is installed and enabled during PureMessage installation. The default policy varies according to your PureMessage license; for example, if you do not have a license for the PureMessage Virus component, virus-checking rules are not configured.

PureMessage Sieve Extensions

The PureMessage policy.siv script uses extensions to the Sieve language to filter mail. Each PureMessage specific extension is prefixed with pmx_ to identify the action, test, or command as unique to the program. To use these extensions include the command require "PureMessage"; at the beginning of the policy script. PureMessage also provides the standard extensions reject and envelope as specified in RFC 3028 (http://www.ietf.org/rfc/rfc3028.txt). Note that both reject and envelope are included implicitly with require "PureMessage";. See the pmx-policy manpage for more on specific PureMessage Sieve extensions.

Lastly, PureMessage has an extension for using regular expressions in match-type tests. See Regular Expressions.

policy.siv

In general, the default policy.siv file is comprised of eight if control statements and one else statement. Each control statement is comprised of a type of test, a block and an associated sequence of actions. Action sequences can either modify or route particular messages in PureMessage. Actions, tests, and commands specific to PureMessage are prefixed with pmx_ to identify the command as an extension to Sieve.

For the purpose of discussion, the policy.siv file is broken into six parts. Each script piece is followed by a detailed description of the commands and syntax used to filter mail in PureMessage.

Policy Script 1: Scan and Deliver Internal Messages

    pmx_test_mark;
    # attr NAME=Mail from internal hosts
    if pmx_relay :memberof "internal-hosts" {
        # The 'pmx-mlog-watch' depends on this to know which messages
        # are outgoing and which are not.
        pmx_mark1 "i";
        # attr NAME=Reject mail containing viruses
        if pmx_virus {
            reject "One or more viruses were detected in the message.";
            stop;
        }
    }

Description:

Policy Script 2: Scan External Mail for Viruses

    # attr NAME=Mail from external hosts
    else {
        pmx_add_header "X-PMX-Version" "%%PMX_VERSION%%";
        pmx_mark "Size" "%%MESSAGE_SIZE%%";
        # attr NAME=Clean mail containing viruses
        if pmx_virus {
            pmx_file "Virus";
            pmx_virus_clean "cantclean.tmpl";
            pmx_replace_header "Subject" "[PMX:VIRUS] %%SUBJECT%%";
            pmx_replace_header "X-PerlMx-Virus-Detected" "%%VIRUS_IDS%%";
        }

Description:

Policy Script 3: Deliver External Whitelisted Messages

    # attr NAME=Deliver mail from whitelisted hosts and senders
    if anyof(pmx_relay :memberof "whitelisted-hosts",
             envelope :memberof "From" "whitelisted-senders") {
        keep;
        stop;
    }

Description:

Policy Script 4: Deliver External Messages to Anti-Spam Opt-Outs

    # attr NAME=Deliver mail to anti-spam opt-outs
    if true {
        pmx_deliver_for :memberof "anti-spam-optouts";
    }

Description:

Policy Script 5: Quarantine External Blacklisted Messages

    # attr NAME=Quarantine mail from blacklisted hosts and senders
    if anyof(pmx_relay :memberof "blacklisted-hosts",
             envelope :memberof "From" "blacklisted-senders") {
        pmx_quarantine "Blacklisted";
        stop;
    }

Description:

Policy Script 6: Calculate Spam Probability, Modify and Deliver

Modify Message

    # import levels here
    # attr NAME=Copy to quarantine and deliver if spam probability is 50%
    # or more.
    if not pmx_spam_prob :under 50 {
        pmx_replace_header "X-PerlMx-Spam" "Gauge=%%XGAUGE%%%%IGAUGE%%,
          Probability=%%PROB%%, Report='%%HITS%%'";
        pmx_file "Spam";
        pmx_replace_header "Subject" "[PMX:%%GAUGE%%] %%SUBJECT%%";
        stop;
    }

Description:

Deliver Message

    # attr NAME=Add X-Header and deliver messages
    else {
        pmx_replace_header "X-PerlMx-Spam" "Gauge=%%XGAUGE%%%%IGAUGE%%,
          Probability=%%PROB%%, Report='%%HITS%%'";
        stop;
    }

Description:

Customizing the PureMessage Default Policy

This section describes how to customize the PureMessage default policy to build mail filters suitable for your environment. Various filtering scenarios are provided to illustrate different approaches to policy modification. These scenarios include: adding recipients, adding headers, and detecting spam.

Adding a Recipient

Occasionally, a site may require certain messages to be automatically sent to a known address. For example, all messages containing the keyword 'bug' might be sent to an bug tracking system. This policy adds a new recipient to a message (bugs@example.com) when the 'Subject' header has the string 'bug' in it, the recipient is 'old-bugs@example.com', or the header 'X-Bug-Id' exists.

    # attr NAME=add a recipient
    # Adds a 'bugs@example.com' recipient to the envelope.
    if anyof (header :matches "Subject" "*bug*",
              envelope "to" "old-bugs@example.com",
              header :matches "X-Bug-Id" "*") {
       pmx_add_recipient "bugs@example.com";
    }

Description:

Where does this filter go?

The 'add a recipient' filter should be placed before the filter in Policy Script 1: Scan and Deliver Internal Messages and directly after the pmx_test_mark action. Positioning the 'add a recipient' filter at this point ensures that:

Adding a Header

Adding a new header to all messages can be useful for tracking purposes. This filter adds an 'X-Seen-By' header to all messages.

    # attr NAME=add a header
    # Adds an 'X-Seen-By' header to all messages.
    pmx_add_header "X-Seen-By" "%%HOSTNAME%%";

Description:

Where does this filter go?

The 'add a header' filter should be placed before the filter in Policy Script 1: Scan and Deliver Internal Messages and directly after the pmx_test_mark action. Positioning the 'add a header' filter at this point ensures that:

Detecting Spam

In some instances, it may be preferable to deliver all spam messages to envelope recipients. For example, site administrators would consider this action when initially testing and optimizing PureMessage. In this situation, a spam detection filter is beneficial for identifying spam and delivering all messages to recipients with an associated spam probability. During the optimization process, mail recipients could then comment on the accuracy of a particular filter.

Note: The optional 'PureMessage-Policy-Spam' component is required to use the 'spam detection' filter.

    # attr NAME=spam detection
    # Detects spam probability over 50%.
    # Prefixes 'Subject' header with '[SPAM:]'.
    # Adds an 'X-PMX-Spam' header.
    if pmx_spam_prob :over 50 {
        pmx_replace_header "Subject" "[SPAM:%%GAUGE%%] %%SUBJECT%%";
        pmx_add_header "X-PMX-Spam" "Probability=%%PROB%%";
        stop;
    }

Description:

Where does this filter go?

The 'spam detection' filter replaces the 'Copy to quarantine and deliver if spam probability is 50% or more' filter in Policy Script 6: Calculate Spam Probability, Modify and Deliver. Replacing the first part of this default policy filter with the 'spam detection' filter ensures that:

Quarantining Spam Messages

Use the following 'quarantine spam' filter to decrease the number of spam messages directed to a recipient's mailbox. The 'quarantine spam' filter is a modification to the default policy script filter found in Policy Script 6: Calculate Spam Probability, Modify and Deliver. Unlike the default policy script filter, the 'quarantine spam' filter quarantines messages when the spam probability is over 80%. (The default policy filter only 'files' spam messages when the spam probability is over 50%. See the pmx_file command in the pmx-policy manpage.

    # attr NAME=quarantine spam
    # Quarantines spam if probability over 80%.
    # Else, delivers message if probability over 50%.
    # Prefixes 'Subject' header with '[SPAM:]'.
    # Adds an 'X-PMX-Spam' header.
    if pmx_spam_prob :over 80 {
        pmx_quarantine "Spam";
        stop;
    }
    elsif pmx_spam_prob :over 50 {
        pmx_replace_header "Subject" "[SPAM:%%GAUGE%%] %%SUBJECT%%";
        pmx_add_header "X-PMX-Spam" "Probability=%%PROB%%";
    }

Description:

Where does this filter go?

The 'quarantine spam' filter replaces the filter found in Policy Script 6: Calculate Spam Probability, Modify and Deliver. Replacing this default policy filter with the 'quarantine spam' filter ensures that:

Catching Viruses

Use the following three 'virus' filters to modify how PureMessage handles virus-laden messages.

Note: The optional 'PureMessage-Policy-Virus' package is required when using any of the following virus filters.

Example 1: Quarantine all external messages containing virus variants.

Use the 'virus 1' filter to quarantine all external messages containing virus variants. The 'virus 1' filter is a modification to the default policy filter found in Policy Script 2: Scan External Mail for Viruses. Unlike the default filter, the 'virus 1' filter quarantines all messages containing virus variants. No attempt is made to clean infected messages. See the pmx_virus command in the pmx-policy manpage.

    # attr NAME=virus 1
    # Quarantines all infected messages.
    if pmx_virus {
        pmx_quarantine "Virus";
    }

Description:

Where does this filter go?

The 'virus 1' filter replaces the Policy Script 2: Scan External Mail for Viruses filter. Replacing this default policy filter with the 'virus 1' filter ensures that:

Example 2: Attempt to clean all internal messages containing virus variants.

Use the 'virus 2' filter to clean all internal messages containing virus variants. The 'virus 2' filter is a modification to the default policy filter found in Policy Script 1: Scan and Deliver Internal Messages. Unlike the default filter, the 'virus 2' filter attempts to clean virus variants from all messages sent through internal hosts. The default policy rejects all internal mail containing viruses.

    # attr NAME=virus 2
    #
    if pmx_virus {
        pmx_file "Virus";
        pmx_virus_clean "cantclean.tmpl";
        pmx_replace_header "Subject" "[PMX:VIRUS] %%SUBJECT%%";
        stop;
    }

Description:

This filter attempts to clean the virus-laden message. If the message is successfully cleaned, it is sent to its original recipients. If the virus is not successfully cleaned, the infected part is replaced with the error template cantclean.tmpl. The 'Subject' is marked with '[PMX:VIRUS]' to inform recipients that PureMessage found a virus.

Where does this filter go?

The 'virus 2' filter replaces the Policy Script 1: Scan and Deliver Internal Messages filter. Replacing this default policy filter with the 'virus 2' filter ensures that:

Example 3: Discard external messages containing specific viruses.

Use the 'virus 3' filter to evaluate mail sent through external hosts and to discard messages containing either the 'Klez' or 'Sobig' variants. The 'virus 3' filter is a modification to the default policy filter found in Policy Script 2: Scan External Mail for Viruses. Unlike the default filter, the custom 'virus 3' filter searches for specific viruses using the pmx_virus_id command.

    # attr NAME=virus 3
    # Discards messages infected with Klez or Sobig variants.
    # Attempts to clean messages infected with other variants.
    if pmx_virus {
        if pmx_virus_id :matches ["*Klez*", "*Sobig*"] {
            discard;
            stop;
        }
        pmx_file "Virus";
        pmx_virus_clean "cantclean.tmpl";
        pmx_replace_header "Subject" "[PMX:VIRUS] %%SUBJECT%%";
    }

Description:

Where does this filter go?

The 'virus 3' filter replaces the Policy Script 2: Scan External Mail for Viruses filter. Replacing this default policy filter with the custom 'virus 3' filter ensures that:

Discarding Messages Based on Specific Characteristics

    # attr NAME=discard
    # Discards messages containing key phrases.
    if header :matches "Subject" ["Re: Approved", "Re: Details", "Re: Movie",
                                 "Re: My details", "Re: Thank you!"] {
       discard;
       stop;
    }

Description:

Where does this filter go?

The 'discard' filter should be added directly after the Policy Script 5: Quarantine External Blacklisted Messages policy filter. Positioning the 'discard' filter at this point ensures that:

Adding a Disclaimer

Many corporate sites require that all outgoing email have a legal disclaimer attached. PureMessage centralizes this action at the mail gateway.

    # Add a disclaimer filter.
    # Adds 'banner.txt' file content to the message body.
    # Only detect outgoing mail.
    if pmx_relay :memberof "internal-hosts" {
        pmx_add_banner :body :use_html_pre :file "banner.txt";
    }

Description:

Where does this filter go?

The 'add a disclaimer' filter should be placed before the filter in Policy Script 1: Scan and Deliver Internal Messages and directly after the pmx_test_mark action. Positioning the 'add a disclaimer' filter at this point ensures that:


SEE ALSO

PureMessage Policy

For information about specific PureMessage policy tests and actions, see the pmx-policy manpage.

Sieve Language

Sieve is a language used for filtering email messages. It is a multi-vendor effort, and has been proposed as a standard to the Internet Engineering Task Force.

PureMessage makes use of Sieve for filtering email via policies. Filter parameters are stored in the file policy.siv, located by default in the /opt/pmx/etc directory.

For information about manually modifying the Sieve code in the policy.siv file, refer to the pmx-policy manpage documentation.

More Information


COPYRIGHT

Copyright (C) 2000-2008 Sophos Group. All rights reserved. Sophos and PureMessage are trademarks of Sophos Plc and Sophos Group.