Many customers have completed or are completing the rollout of SPF, DKIM and DMARC to improve email security. Once DMARC has been moved to 100% quarantine that means that all of the issues have been identified and resolved. So what’s next?
Typically this is where MTA-STS comes in. Mail Transfer Agent - Strict Transport Security (MTA-STS) is intended to provide additional security to email transport and solve long standing issues with the STARTTLS verb and its use of opportunisic TLS. You may have also seen a related standard DANE (DNS Authentication of Name Entities) which also wants to address this issue, but has a different approach. DANE uses DNSSEC (DNS Security Extensions) for DNS authentication. That means your DNS provider must support the DNSSEC feature, your hosting plan includes it and you then enable DNSSEC.
MTA-STS does not rely on DNSSEC to determine the connection's security, rather the TLS certificate issuer is used to determine if the connection is to be considered secure. This means that DNSSEC does not have to be deployed to enable MTA-STS. Via the policy file, you specify the MTA-STS operational mode (none, testing or enforced) and the expected MX endpoints. Note that Microsoft has already added support for outbound MTA-STS and all messages being sent out are being validated. If the destination domain has the MTA-STS policy set to enforce and there is an issue you may receive a NDR. No tenant administration action is required to enable outbound MTA-STS. You will need to take action for inbound MTA-STS and that is the focus of this post.
Both DANE and MTA-STS need some form or reporting and SMTP TLS Reporting (TLS-RPT) should be deployed.
One final comment about why many organizations are not deploying DANE is that at the time of writing Exchange Online Protection (EOP) does not fully support DANE. There are two components to email flow, inbound and outbound. Today EOP supports DANE outbound, but it does not support DANE inbound.
While MTA-STS has some similarities to DMARC in terms of reporting, a testing mode and requires DNS records, it does also have significant differences. One of those is the fact that a text file must be created and placed on the Internet. The file must be placed on a defined and documented URL - this is:
https://mta-sts.<domain name>/.well-known/mta-sts.txt
For the tailspintoys.ca domain the path must be:
https://mta-sts.tailspintoys.ca/.well-known/mta-sts.txt
Note that a valid TLS certificate issued by a trusted 3rd party is a hard requirement. We are placing our trust into the TLS certificates, so the policy file must also be served via trusted connection.
If you do not have an existing web hosting platform, it does not have a valid TLS certificate or it can not serve .txt files then one documented option is to use a simple Azure web service to hosts the content. This is documented here:
Enhancing mail flow with MTA-STS | Microsoft Learn
There are some caveats and considerations with this solution, please read below for the details.
Azure DevOps Organisation Required
If you chose option #1 to use an Azure Static Web App then a fundamental requirement is that you will need an Azure DevOps organisation. This raises a couple of points.
If you do not already have a DevOps organisation how do you create and manage it securely? What additional process do we need as this should not be an orphaned resource that languishes in Azure.
Should there already be a DevOps organisation, then verify that we have sufficient security and management processes and then request that a project is created specifically for the MTA-STS work.
Some suggested reading: Azure DevOps Security Best Practices
DevOps Management
This may be the first time that the messaging or M365 admin has worked with DevOps, Git or similar technologies. That is something that will need to be discussed to make sure they understand how pull requests, commits and pipelines.
Azure DevOps Pipeline YAML Formatting
One thing to note is that Yet Another Markup Language (YAML) was subsequently changed to be YAML Ain’t Markup Language (YAML) files have a defined format. If the file contains formatting or syntax errors it will break the pipeline which means the solution will not function as the mta-sts.txt file will not be published.
Rather than pasting the YAML as text, which may not render properly, let’s use an image as it is easier to see the expected layout. Here is a working example:
Now, let’s add some commentary. Pay particular attention to these lines highlighted below.
For example on line 5 there is no pipe character, only Zuul. Sorry – wrong reference!
But seriously the space is used to indent the YAML file and define its structure.
If that is not done correctly then you will have issues, note the indented nodes in the below image:
You can see the spaces represented by the grey dots when the file is being edited:
Lines 13 to 15 only have spaces at the start of each line. No other characters.
Make sure that the formatting is correct and that validation completes with no errors.
See the additional notes at the end of this post for some brief notes on the YAML formatting.
Use The Pipeline Editor’s Validate Functionality
When editing the YAML, make sure that it is valid before trying to run the pipeline. This can be done by the steps below:
Once editing, you can this use the top right menu to use the validate tool.
Azure DevOps Free Limitations
If you use the free tier of Azure DevOps, then please review the tier’s limits. This is documented here.
Azure DevOps Error - No Hosted Parallelism Has Been Purchased Error
As noted in the documentation, you will experience this error if billing has not been configured. Link to the form is here.
In Azure DevOps, during deployment, if you experience the error No hosted parallelism has been purchased or granted, either request through this form or implement a configuration through Organization Settings > Parallel jobs > Microsoft Hosted > Change > Paid Parallel jobs such that “Paid parallel jobs” are allowed.
Select either option. Note the next issue though if you elect to remain on the free tier.
Plan For Time Taken To Process Form
Note that it will take several days for a completed form to be processed. This means that you must plan for this as part of the MTA-STS project.
You will create the DevOps Organisation so that the objects have been created, and then enter them into the form. Then you wait.
The project timeline must account for this time to elapse for the form to be processed.
If you can not wait, then move from the free tier to a paid tier.
Create the Required Folder Structure
Ensure that the expected folder structure is created. This can lead to two issues if not done:
- Pipeline deployment will fail if the path is not found
- The mta-sts.txt file file must be in a folder called “.well-known” Note that there is a full stop (period) in that name.
Creating the folder path in Azure DevOps editor has a couple of minor considerations. You can create the path and mta-sts.txt file in a single step. Then edit the file to add the content.
The new folder name is: “home/.well-known”
Note though that you can not use the Windows backslash character to indicate the folder path. If you try that it will cause an error.
Use Linux based formatting which is the forwardslash "/" since this is what we are running on.
The expected folder structure is show below:
Zooming into the middle area of the screen to enlarge the image we can see that the index.html file is contained at /home and the mta-sts.txt file is underneath “/.well-known” so that the requirements of MTA-STS are met.
MTA-STS Text File Formatting
After the MTA-STS.txt file is published to the Internet you may think that’s all done. However there may also be formatting issues with that file.
The below example looks OK in the browser.
But there is a very different story with the online validation tools.
Let’s download the MTA-STS.txt file and compare it side by side to one generated by a tool. The file from the Azure solution is on the right hand side. Note that there are spaces present at the start of each line on the Azure hosted file.
These spaces are indicated by the blue arrow and the space on the first line was selected to illustrate that it is present.
This is also visible in the editor.
To fix this we need to edit the file and then run the pipeline to get it pushed to the container. Once the edit has been committed the issue should be resolved.
Pipeline Process Error Code 1
If you did not create the home folder then the application will not be found. This is because that path was referenced in the DevOps Pipeline YAML config sample.
The pipeline error would be something like:
Error: The process '/usr/bin/bash' failed with exit code 1
Failed to find a default file in the app artifacts folder (/). Valid default files: index.html,Index.html.
If your application contains purely static content, please verify that the variable 'app_location' in your deployment configuration file points to the root of your application.
If your application requires build steps, please validate that a default file exists in the build output directory
##[error]Error: The process '/usr/bin/bash' failed with exit code 1
To fix this issue, either:
- Change the Pipeline’s YAML file and update the app_location entry to ‘/’
- Create the Home folder in the File Repository.
Note that only one is required.
It is likely that the better solution is to create the folder structure as expected, as there is also the change that the MTA-STS policy file is also at the root of the site. That will not work as the file will be in the wrong location.
Ensure TLS Certificate Configuration Is Correct
Once all of the Azure DevOps pipeline settings and issues have been resolved, we can then add our actual domain to the Static Web App as a custom domain.
Make sure that you are able to browse to the MTA-STS URL and that the certificate is trusted etc. It should look like this – note that there are no certificate warnings etc.
The base URL should work:
https://mta-sts.tailspintoys.ca/
And also verify the MTA-STS file itself:
https://mta-sts.tailspintoys.ca/.well-known/mta-sts.txt
YAML Formatting Notes
Below are some additional notes and pointers on the YAML file that is used by the Static Web App pipeline. This is a summary only and the intent is not go go into all aspects of YAML.
Microsoft Copilot was used to draft out the notes below.
Generic Pointers
Spaces are used for indentation and to denote structure. YAML is a human-readable data serialization language, and it uses indentation (spaces) to denote structure much like Python. Incorrect spacing can lead to errors or unexpected behaviour, so it’s important to be consistent with your use of spaces in a YAML. The indentation format is prone to syntax and validation errors.
- In YAML, indentation is used to denote structure. Items at the same level of indentation are considered siblings
- YAML uses spaces for indentation, but not tabs. The number of spaces used for indentation can vary, but all items at the same level must use the same indentation
- YAML has special syntax for multiline strings. The pipe (|) character begins a literal style block, where all line breaks are significant
The basic building block of a YAML file is a key-value pair. The key and value are separated by a colon.
In the context of a YAML file, the - (dash) is used to denote a list item. It’s similar to how you might use bullet points in a text document to list items.
Comments are prefixed by # anything following the comment symbol is ignored by the parser.
Strings do not require quotation marks, but can be used.
YAML is case sensitive
Sections Present In the Pipeline File
Trigger
The trigger section specifies that this pipeline will run on any push to the master branch.
Pool
The pool section specifies that the pipeline will run on the latest Ubuntu virtual machine image.
Steps
In the context of an Azure DevOps Pipeline YAML file, the steps section defines the sequence of tasks that the pipeline will execute. Each step in the pipeline can be a script, a command, or a task. There are two tasks listed as part of steps:
- checkout
- task
Steps – Checkout
The checkout step is used to check out your code and its submodules. The submodules: true line is what enables submodule checkout.
Checkout – Submodules
In the context of Git, a submodule is like a Git repository inside another Git repository. It allows you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. In the context of the Azure DevOps Pipeline YAML file, when you use submodules: true in the checkout step, it tells the pipeline to checkout not only your code but also all the submodules linked to your repository. This can be useful when you have dependencies on other projects or when you want to split your codebase into smaller, more manageable pieces, but still want to be able to work with everything together when necessary.
Please note that the submodules must be properly set up in your repository and accessible by your pipeline. If they are private repositories, appropriate permissions must be set.
Task - AzureStaticWebApp@0
AzureStaticWebApp@0 is a task in an Azure DevOps Pipeline YAML file that is used to build and deploy Azure Static Web Apps1. The @0 indicates the version of the task. Review the documentation for all of the parameters.
In the Azure DevOps Pipeline YAML file provided, each step in the pipeline is a list item under the steps section. The - before checkout denotes it is an individual in the list of steps to be executed.
Task - AzureStaticWebApp@0 – Inputs
In the context of an Azure DevOps Pipeline YAML file, inputs: is used when you’re defining a task that requires parameters. The inputs: section is where you specify the values for those parameters.
Cheers,
Rhoderick