Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add standard for DNS #570

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions Standards/scs-01xx-v1-dns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
title: DNS Standard
type: Standard
status: Draft
markus-hentsch marked this conversation as resolved.
Show resolved Hide resolved
track: IaaS
---

## Introduction

The Domain Name System (DNS) is used to resolve name records to addresses in Internet Protocol (IP) based networks, primarily but not limited to the public Internet.

DNS has a variety of use cases in OpenStack infrastructures.
For basic egress traffic, it is used to enable proper connectivity for customer virtual machines to the outside world by offering DNS servers to them.
For internal connectivity and discoverability between cloud resources, DNS is used to make virtual machines addressable via their name within tenant networks.
DNS can also be used to publish DNS records for virtual machines that have external connectivity by integrating and using the OpenStack DNS v2 API, e.g. by integrating the Designate service.

## Terminology

| Term | Meaning |
|---|---|
| SCS | Sovereign Cloud Stack |
| CSP | Cloud Service Provider, provider managing the OpenStack infrastructure |
| network | OpenStack Neutron network resource unless stated otherwise |
| port | OpenStack Neutron port resource unless stated otherwise |
| DNS | Domain Name System |
| DNS recursor | Recursive DNS resolver |
markus-hentsch marked this conversation as resolved.
Show resolved Hide resolved
| VM | Virtual Machine, also known as "server" resource in OpenStack Nova |

## Motivation

The behavior and feature set regarding DNS functionalities can differ greatly between OpenStack infrastructures, depending on their individual configuration.

With this standard the SCS project aims to establish a baseline for reliable and consistent DNS features in SCS cloud environments.

## Design Considerations

OpenStack offers a lot of extension choices and even a dedicated service (Designate) for DNS functionality.
The standard should make sure that a specified level of DNS functionality can be reached while taking into account that not all settings and choices might be feasible for CSPs to implement.

### Options considered

#### Making Designate mandatory

To offer a consistent feature set to customers, the SCS project could consider to make Designate mandatory in a sense that SCS clouds would need to integrate the service, make it available to customers and properly configure it for publishing DNS records.
markus-hentsch marked this conversation as resolved.
Show resolved Hide resolved
This would offer easy DNS-as-a-Service functionality to customers.

However, this would also require solid DNS expertise at CSP-side to properly set up and integrate Designate and DNS zones as Designate does not act as a full DNS server on its own but instead relies on external DNS providers or self-hosted DNS infrastructures that the CSP needs to integrate into it.
artificial-intelligence marked this conversation as resolved.
Show resolved Hide resolved

#### Mandating the use of DNSSEC

The DNSSEC extension to DNS ensures authenticity and integrity of the data provided to DNS resolvers.
This protects against attacks like cache poisoning and increases the trustworthiness of the DNS infrastructure.

This is a direct improvement for customers and yields low complexity for the CSP to implement since it is supported by most major DNS implementations natively.
artificial-intelligence marked this conversation as resolved.
Show resolved Hide resolved

#### Mandating the use of local DNS recursors

A local DNS recursor can be used to cache and serve DNS responses locally. It servers as a proxy between the clients and external DNS servers.
This improves performance and speed of DNS resolution in the infrastructure.
Furthermore, it can be configured to use DNSSEC and DNS over TLS to increase security and privacy of DNS requests it handles for clients.
Regardless of whether clients within the cloud infrastructure individually support those features or not, they all benefit from a local recursor implementing it as their DNS traffic is then properly protected outside of the infrastructure.

As such, the implementation of local DNS recursors in the infrastructure can be very beneficial.
This standard should consider mandating or at least recommending the use of local DNS recursors for SCS clouds to be configured as the default DNS servers for Neutron resources.

#### Extension and driver choices

There is a hierarchy of DNS extensions in the Networking API in which they supersede one another in terms of functionality:

- dns-integration
- dns-domain-ports (includes functionality of dns-integration)
- subnet-dns-publish-fixed-ip (includes functionality of dns-integration and dns-domain-ports)
- dns-integration-domain-keywords (includes functionality of all of the above)

For example, to get the "subnet-dns-publish-fixed-ip" functionality, either "subnet-dns-publish-fixed-ip" or "dns-integration-domain-keywords" (which includes the former) must be activated.

Note that each API extension has a corresponding backend driver functionality associated to it.
The availability of each API extension depends on the backend configuration and implementation.

As a result, the DNS functionalities and behaviors available to the customer vary depending on the individual backend configuration of the Networking API.
Mandating or recommending the integration of specific drivers/extensions can therefore be crucial to establish specific DNS functionality baselines for both internal DNS and DNS-as-a-Service:

The "dns-integration" extension provides the basic feature set for internal DNS resolution and should be the minimum requirement.

The "dns-domain-ports" extension adds port-specific DNS domain name override capabilities but those are only important for DNS-as-a-Service scenarios mostly.

Starting with the "subnet-dns-publish-fixed-ip" API extension, the largest flexibility is provided in regards to self-service publishing DNS records for fixed IPs from externally reachable tenant networks (e.g. for IPv6 subnet pools).
In cases where DNS-as-a-Service is offered (e.g. via Designate), this extension should be recommended as opposed to its predecessors.

The "dns-integration-domain-keywords" extension allows dynamically resolving placeholders for user or project id/name in the DNS domain name attribute for port and network metadata.
Its use case seems pretty niche and will most likely be considered entirely optional by the standard.

## Standard

The decisions of this standard will be categorized into three main sections: forwarded DNS, internal DNS and external DNS.

Forwarded DNS refers to the DNS servers communicated to tenant VMs for DNS resolution as well as any local recursors involved.
markus-hentsch marked this conversation as resolved.
Show resolved Hide resolved

Internal DNS refers to the DNS resolution that OpenStack Neutron implements internally to make VM instances' addresses resolvable via name within the same tenant network.

DNS-as-a-Service refers to the integration of external or public DNS via the OpenStack DNS v2 API and its publishing of DNS records for VMs that are externally reachable.
This is an optional feature of OpenStack clouds and can be implemented by integrating the Designate service for example.

### Forwarded DNS

- In OVS-based setups, the `dnsmasq_local_resolv` setting for Neutron DHCP agents MUST be disabled.
markus-hentsch marked this conversation as resolved.
Show resolved Hide resolved
- One or more local DNS recursors SHOULD be integrated into the infrastructure.
- In case one or more local DNS recursors are provided, the *DNS server setting* MUST point to the local DNS recursor(s) only.
- Any local DNS recursor referenced by the *DNS server setting* MUST implement DNSSEC validation and offer DNSSEC itself.
- If the cloud infrastructure has any provider networks connected to the internet, then the *DNS server setting* entries MUST contain DNS servers (recursors or resolvers) that are able to resolve public DNS records.
- If no local DNS recursor is integrated and one or more public DNS server(s) are referenced in the *DNS server setting*, all referenced public DNS servers MUST offer DNSSEC as well as validate DNSSEC themselves and discard invalid responses.

The *DNS server setting* refers to the following:

- In OVS-based setups, the `dnsmasq_dns_servers` setting in the `[DEFAULT]` section of the `dhcp_agent.ini` for all Neutron DHCP agents.
- In OVN-based setups, the `dns_servers` setting in the `[ovn]` section of `ml2_conf.ini`.

### Internal DNS

#### DNS Extensions

In the Networking API, the "dns-integration" extension MUST be enabled to offer internal DNS resolution.

For Neutron, this can implemented by enabling one of the following extension drivers:

- `dns`
- `dns_domain_ports` (includes `dns`)
- `subnet_dns_publish_fixed_ip` (includes `dns` and `dns_domain_ports`)
- `dns_domain_keywords` (includes all of the above)

The extension driver setting is part of the ML2 plugin configuration (example for `dns_domain_ports`):

```
[ml2]
extension_drivers = ...,dns_domain_ports
```
(the `...` resembles a placeholder for other enabled drivers)

#### Internal DNS Domain

The `dns_domain` setting in the global Neutron configuration will act as the default domain name for any ports created by users unless overridden by explicit network or port settings.
A CSP MAY choose this setting freely but SHOULD NOT change it after the initial deployment of the cloud.

### DNS-as-a-Service

The following section only applies to SCS clouds which include the DNS-as-a-Service functionality for customers via the [OpenStack DNS v2 API](https://docs.openstack.org/api-ref/dns/dns-api-v2-index.html).
All guidelines above still apply.

In the Networking API, the "dns-domain-ports" extension MUST be enabled to offer the full range of DNS record settings for both ports and networks.
This is implemented by the `dns_domain_ports` Neutron extension driver for the ML2 plugin.
See the Internal DNS section above for an example on how to enable an extension driver.

However, is RECOMMENDED to provide the "subnet-dns-publish-fixed-ip" API extension for the Networking API in addition to "dns-domain-ports".
In Neutron, this can be done by activating either the `subnet_dns_publish_fixed_ip` or `dns_domain_keywords` extension driver instead of `dns_domain_ports`.

## Related Documents

- [OpenStack User Guide for basic usage of DNS-as-a-Service with Neutron and Nova resources](https://docs.openstack.org/designate/latest/user/neutron-integration.html)
- [OpenStack Configuration and User Guide for various DNS-as-a-Service scenarios in Neutron](https://docs.openstack.org/neutron/latest/admin/config-dns-int-ext-serv.html)

## Conformance Tests

Conformance tests verify the existence of necessary APIs and API extensions as mandated by the standard.

There is a test suite in [`dns-extensions-check.py`](https://github.com/SovereignCloudStack/standards/blob/main/Tests/iaas/dns-extensions/dns-extensions-check.py).
The test suite connects to the OpenStack API and queries the applicable APIs and extensions related to the DNS features.
Please consult the associated [README.md](https://github.com/SovereignCloudStack/standards/blob/main/Tests/iaas/dns-extensions/README.md) for detailed setup and testing instructions.
58 changes: 58 additions & 0 deletions Tests/iaas/dns-extensions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# DNS Extensions Test Suite

## Test Environment Setup

### Test Execution Environment

> **NOTE:** The test execution procedure does not require cloud admin rights.

To execute the test suite a valid cloud configuration for the OpenStack SDK in the shape of "`clouds.yaml`" is mandatory[^1].
**The file is expected to be located in the current working directory where the test script is executed unless configured otherwise.**

[^1]: [OpenStack Documentation: Configuring OpenStack SDK Applications](https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html)

The test execution environment can be located on any system outside of the cloud infrastructure that has OpenStack API access.
Make sure that the API access is configured properly in "`clouds.yaml`".

It is recommended to use a Python virtual environment[^2].
Next, install the OpenStack SDK required by the test suite:

```bash
pip3 install openstacksdk
```

Within this environment execute the test suite.

[^2]: [Python 3 Documentation: Virtual Environments and Packages](https://docs.python.org/3/tutorial/venv.html)

## Test Execution

The test suite is executed as follows:

```bash
python3 dns-extensions-check.py --os-cloud mycloud
```

As an alternative to "`--os-cloud`", the "`OS_CLOUD`" environment variable may be specified instead.
The parameter is used to look up the correct cloud configuration in "`clouds.yaml`".
For the example command above, this file should contain a `clouds.mycloud` section like this:

```yaml
---
clouds:
mycloud:
auth:
auth_url: ...
...
...
```

For any further options consult the output of "`python3 dns-extensions-check.py --help`".

### Script Behavior & Test Results

The script will print all executed tests and their results to `stdout`.

If all tests pass, the script will return with an exit code of `0`.

If any test fails, the script will abort, print the failed test to `stdout` and return with a non-zero exit code.
126 changes: 126 additions & 0 deletions Tests/iaas/dns-extensions/dns-extensions-check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""Networking API extension checker for DNS functionality

This script uses the OpenStack SDK to check conformance to the SCS standard
concerning DNS API extensions.
"""

import openstack
import os
import sys
import argparse


def connect(cloud_name: str,
auth_overrides: dict = None) -> openstack.connection.Connection:
"""Create a connection to an OpenStack cloud

:param string cloud_name:
The name of the configuration to load from clouds.yaml.
:param dict auth_overrides:
A dict that overrides option of the auth section of the cloud
configuration of the loaded clouds.yaml. Allows to authenticate
as a different user based on the same general cloud settings.
Example:

{
"username": "claudia",
"password": "foobar123!%",
"project_name": "customer1"
}

:returns: openstack.connnection.Connection
"""

if auth_overrides:
return openstack.connect(
cloud=cloud_name,
auth=auth_overrides
)
else:
return openstack.connect(
cloud=cloud_name,
)


def has_extension(conn: openstack.connection.Connection, name: str) -> bool:
"""Checks whether a given Networking API extension is present.

Connects to the Extension API of the Networking API and checks if the
extension given by `name` is present and in case it is returns True.
Otherwise returns False.
"""
ext = conn.network.find_extension(name, ignore_missing=True)
if ext:
# note that ext.name is a human-readable description, the name as
# referenced in the standard is the ext.alias
return ext.alias == name
else:
return False


def has_dns_api(conn: openstack.connection.Connection) -> bool:
"""Checks whether the OpenStack DNS API is offered by the connected cloud.

Returns True if the DNS API is offered and False otherwise.
"""
# "[...] the dns member of a Connection object [...]"
# "[...] will only be added if the service is detected."
# see https://docs.openstack.org/openstacksdk/latest/user/proxies/dns.html
return hasattr(conn, "dns")


def main():
parser = argparse.ArgumentParser(
description="SCS Domain Manager Conformance Checker")
parser.add_argument(
"--os-cloud", type=str,
help="Name of the cloud from clouds.yaml, alternative "
"to the OS_CLOUD environment variable"
)
parser.add_argument(
"--debug", action="store_true",
help="Enable OpenStack SDK debug logging"
)
args = parser.parse_args()
openstack.enable_logging(debug=args.debug)

# parse cloud name for lookup in clouds.yaml
cloud = os.environ.get("OS_CLOUD", None)
if args.os_cloud:
cloud = args.os_cloud
assert cloud, (
"You need to have the OS_CLOUD environment variable set to your cloud "
"name or pass it via --os-cloud"
)
conn = connect(cloud)

# bare minimum: dns-integration extension
if has_extension(conn, "dns-integration"):
print(
"Networking API MUST offer 'dns-integration' extension: PASS"
)
else:
print(
"Networking API MUST offer 'dns-integration' extension: FAIL"
)
sys.exit(1)

has_designate = has_dns_api(conn)
print("Is DNS API present (OPTIONAL):", has_designate)

if has_designate:
if has_extension(conn, "dns-domain-ports"):
print(
"When the DNS API is present, the Networking API MUST offer "
"the 'dns-domain-ports' extension: PASS"
)
else:
print(
"When the DNS API is present, the Networking API MUST offer "
"the 'dns-domain-ports' extension: FAIL"
)
sys.exit(1)


if __name__ == "__main__":
main()
Loading