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

[uss_qualifier] netrid: DSS0020 - check DSS endpoints are encrypted #834

Merged
merged 5 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import errno
import requests
import socket
import uas_standards.astm.f3411.v19.api
import uas_standards.astm.f3411.v22a.api
from uas_standards.astm.f3411 import v19, v22a
from urllib.parse import urlparse

from monitoring.monitorlib.rid import RIDVersion
from monitoring.uss_qualifier.resources.astm.f3411.dss import DSSInstanceResource
from monitoring.uss_qualifier.scenarios.scenario import (
GenericTestScenario,
)
from monitoring.uss_qualifier.suites.suite import ExecutionContext


class EndpointEncryption(GenericTestScenario):
"""
Ensures that the endpoints of a DSS are not accessible unencrypted:
- HTTP access should be impossible or redirect to HTTPS
- HTTPS access should be possible

TODO: add a check for minimal cipher strength to a 128bit AES equivalent or more.
"""

def __init__(
self,
dss: DSSInstanceResource,
):
super().__init__()
self._dss = dss.dss_instance

if self._dss.rid_version == RIDVersion.f3411_19:
op = v19.api.OPERATIONS[v19.api.OperationID.GetIdentificationServiceArea]
elif self._dss.rid_version == RIDVersion.f3411_22a:
op = v22a.api.OPERATIONS[v22a.api.OperationID.GetIdentificationServiceArea]
else:
raise NotImplementedError(
f"Scenario does not support RID version {self._dss.rid_version}"
)

non_existing_id = "00000000-0000-0000-0000-000000000000"
http_base_url = urlparse(self._dss.base_url)._replace(scheme="http")
self._http_get_url = f"{http_base_url.geturl()}{op.path}".replace(
"{id}", non_existing_id
)
self._https_get_url = f"{self._dss.base_url}{op.path}".replace(
"{id}", non_existing_id
)

def run(self, context: ExecutionContext):
self.begin_test_scenario(context)

if not self._dss.base_url.startswith("https://"):
self.record_note(
"encrypted_endpoints",
"Cannot check encryption requirement when DSS endpoint is specified with an http:// base URL",
)
self.end_test_scenario()
return

self.begin_test_case("Validate endpoint encryption")
self._step_http_unavailable_or_redirect()
self._step_https_works()
self.end_test_case()

self.end_test_scenario()

def _step_http_unavailable_or_redirect(self):
self.begin_test_step("Attempt GET on a known valid path via HTTP")

with self.check(
"HTTP GET fails or redirects to HTTPS",
self._dss.participant_id,
) as check:
try:
response = requests.get(
self._http_get_url,
timeout=10,
)
if not response.url.startswith("https://"):
# response.url contains the url of the final request after all redirects have been followed, if any
check.record_failed(
"HTTP GET request did not redirect to HTTPS",
details=f"Made an http GET request and obtained status code {response.status_code} with response {str(response.content)} that was not redirected to https",
)

except socket.error as e:
if e.errno not in [errno.ECONNREFUSED, errno.ETIMEDOUT]:
check.record_failed(
"Connection to HTTP port failed for an unexpected reason",
details=f"Encountered socket error: {e}, while the expectation is to either run into a straight up connection refusal or a timeout.",
)

self.end_test_step()

def _step_https_works(self):
self.begin_test_step("Attempt GET on a known valid path via HTTPS")

with self.check(
"HTTPS GET succeeds",
self._dss.participant_id,
) as check:
try:
response = requests.get(
self._https_get_url,
timeout=10,
)
if not response.url.startswith("https://"):
# response.url contains the url of the final request after all redirects have been followed, if any
check.record_failed(
"HTTPS GET request redirected to HTTP",
details=f"Made an https GET request and obtained status code {response.status_code} with response {str(response.content)} that was redirected to http",
)
except requests.RequestException as e:
check.record_failed(
"Connection to HTTPS port failed",
details=f"Encountered exception while attempting HTTPS request: {e}",
)

self.end_test_step()
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
from .token_validation import TokenValidation
from .crdb_access import CRDBAccess
from .heavy_traffic_concurrent import HeavyTrafficConcurrent
from .endpoint_encryption import EndpointEncryption
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# ASTM NetRID DSS: Endpoint encryption test scenario

## Overview

Ensures that a DSS only exposes its endpoints via HTTPS.

## Resources

### dss

[`DSSInstanceResource`](../../../../../resources/astm/f3411/dss.py) to be tested in this scenario.

## Validate endpoint encryption test case

Tries to connect to the http port (80) of the DSS instance, and expects either a refusal of the connection,
or a redirection to the https port (443).

Note that this test case will be skipped if the DSS instance is configured to use HTTP.
Note that the requests made in this case are made without any form of authentication, as the completion of any form
of communication over an unencrypted channel, even a 40X status response, is considered a failure.

### Attempt GET on a known valid path via HTTP test step
Attempts the operation `GetIdentificationServiceArea` on the DSS through HTTP with a non-existing ID.

#### 🛑 HTTP GET fails or redirects to HTTPS check
If the DSS instance serves the request through unencrypted HTTP, it is in violation of **[astm.f3411.v19.DSS0020](../../../../../requirements/astm/f3411/v19.md)**.
Only the last request after all redirections are followed is considered.

### Attempt GET on a known valid path via HTTPS test step
Attempts the operation `GetIdentificationServiceArea` on the DSS through HTTPS with a non-existing ID.

#### 🛑 HTTPS GET succeeds check
If the DSS instance does not serve the request through encrypted HTTPS, or redirects it to HTTP, it is in violation of **[astm.f3411.v19.DSS0020](../../../../../requirements/astm/f3411/v19.md)**.
Only the last request after all redirections are followed is considered.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from monitoring.uss_qualifier.scenarios.astm.netrid.common.dss.endpoint_encryption import (
EndpointEncryption as CommonEndpointEncryption,
)
from monitoring.uss_qualifier.scenarios.scenario import TestScenario


class EndpointEncryption(TestScenario, CommonEndpointEncryption):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
from .token_validation import TokenValidation
from .crdb_access import CRDBAccess
from .heavy_traffic_concurrent import HeavyTrafficConcurrent
from .endpoint_encryption import EndpointEncryption
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# ASTM NetRID DSS: Endpoint encryption test scenario

## Overview

Ensures that a DSS only exposes its endpoints via HTTPS.

## Resources

### dss

[`DSSInstanceResource`](../../../../../resources/astm/f3411/dss.py) to be tested in this scenario.

## Validate endpoint encryption test case

Tries to connect to the http port (80) of the DSS instance, and expects either a refusal of the connection,
or a redirection to the https port (443).

Note that this test case will be skipped if the DSS instance is configured to use HTTP.
Note that the requests made in this case are made without any form of authentication, as the completion of any form
of communication over an unencrypted channel, even a 40X status response, is considered a failure.

### Attempt GET on a known valid path via HTTP test step
Attempts the operation `GetIdentificationServiceArea` on the DSS through HTTP with a non-existing ID.

#### 🛑 HTTP GET fails or redirects to HTTPS check
If the DSS instance serves the request through unencrypted HTTP, it is in violation of **[astm.f3411.v22a.DSS0020](../../../../../requirements/astm/f3411/v22a.md)**.
Only the last request after all redirections are followed is considered.

### Attempt GET on a known valid path via HTTPS test step
Attempts the operation `GetIdentificationServiceArea` on the DSS through HTTPS with a non-existing ID.

#### 🛑 HTTPS GET succeeds check
If the DSS instance does not serve the request through encrypted HTTPS, or redirects it to HTTP, it is in violation of **[astm.f3411.v22a.DSS0020](../../../../../requirements/astm/f3411/v22a.md)**.
Only the last request after all redirections are followed is considered.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from monitoring.uss_qualifier.scenarios.astm.netrid.common.dss.endpoint_encryption import (
EndpointEncryption as CommonEndpointEncryption,
)
from monitoring.uss_qualifier.scenarios.scenario import TestScenario


class EndpointEncryption(TestScenario, CommonEndpointEncryption):
pass
7 changes: 6 additions & 1 deletion monitoring/uss_qualifier/suites/astm/netrid/f3411_19.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@
<th><a href="../../README.md#checked-in">Checked in</a></th>
</tr>
<tr>
<td rowspan="73" style="vertical-align:top;"><a href="../../../requirements/astm/f3411/v19.md">astm<br>.f3411<br>.v19</a></td>
<td rowspan="74" style="vertical-align:top;"><a href="../../../requirements/astm/f3411/v19.md">astm<br>.f3411<br>.v19</a></td>
<td><a href="../../../requirements/astm/f3411/v19.md">DSS0010</a></td>
<td>Implemented</td>
<td><a href="../../../scenarios/astm/netrid/v19/dss/token_validation.md">ASTM NetRID DSS: Token Validation</a></td>
</tr>
<tr>
<td><a href="../../../requirements/astm/f3411/v19.md">DSS0020</a></td>
<td>Implemented</td>
<td><a href="../../../scenarios/astm/netrid/v19/dss/endpoint_encryption.md">ASTM NetRID DSS: Endpoint encryption</a></td>
</tr>
<tr>
<td><a href="../../../requirements/astm/f3411/v19.md">DSS0030,a</a></td>
<td>Implemented</td>
Expand Down
28 changes: 17 additions & 11 deletions monitoring/uss_qualifier/suites/astm/netrid/f3411_19/dss_probing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@

## [Actions](../../../README.md#actions)

1. Scenario: [ASTM NetRID DSS: Simple ISA](../../../../scenarios/astm/netrid/v19/dss/isa_simple.md) ([`scenarios.astm.netrid.v19.dss.ISASimple`](../../../../scenarios/astm/netrid/v19/dss/isa_simple.py))
2. Scenario: [ASTM NetRID DSS: Submitted ISA Validations](../../../../scenarios/astm/netrid/v19/dss/isa_validation.md) ([`scenarios.astm.netrid.v19.dss.ISAValidation`](../../../../scenarios/astm/netrid/v19/dss/isa_validation.py))
3. Scenario: [ASTM NetRID DSS: ISA Expiry](../../../../scenarios/astm/netrid/v19/dss/isa_expiry.md) ([`scenarios.astm.netrid.v19.dss.ISAExpiry`](../../../../scenarios/astm/netrid/v19/dss/isa_expiry.py))
4. Scenario: [ASTM NetRID DSS: ISA Subscription Interactions](../../../../scenarios/astm/netrid/v19/dss/isa_subscription_interactions.md) ([`scenarios.astm.netrid.v19.dss.ISASubscriptionInteractions`](../../../../scenarios/astm/netrid/v19/dss/isa_subscription_interactions.py))
5. Scenario: [ASTM NetRID DSS: Subscription Validation](../../../../scenarios/astm/netrid/v19/dss/subscription_validation.md) ([`scenarios.astm.netrid.v19.dss.SubscriptionValidation`](../../../../scenarios/astm/netrid/v19/dss/subscription_validation.py))
6. Scenario: [ASTM NetRID DSS: Subscription Simple](../../../../scenarios/astm/netrid/v19/dss/subscription_simple.md) ([`scenarios.astm.netrid.v19.dss.SubscriptionSimple`](../../../../scenarios/astm/netrid/v19/dss/subscription_simple.py))
7. Scenario: [ASTM F3411-19 NetRID DSS interoperability](../../../../scenarios/astm/netrid/v19/dss_interoperability.md) ([`scenarios.astm.netrid.v19.DSSInteroperability`](../../../../scenarios/astm/netrid/v19/dss_interoperability.py))
8. Scenario: [ASTM NetRID DSS: Token Validation](../../../../scenarios/astm/netrid/v19/dss/token_validation.md) ([`scenarios.astm.netrid.v19.dss.TokenValidation`](../../../../scenarios/astm/netrid/v19/dss/token_validation.py))
9. Scenario: [ASTM NetRID DSS: Direct CRDB access](../../../../scenarios/astm/netrid/v19/dss/crdb_access.md) ([`scenarios.astm.netrid.v19.dss.CRDBAccess`](../../../../scenarios/astm/netrid/v19/dss/crdb_access.py))
10. Scenario: [ASTM NetRID DSS: Concurrent Requests](../../../../scenarios/astm/netrid/v19/dss/heavy_traffic_concurrent.md) ([`scenarios.astm.netrid.v19.dss.HeavyTrafficConcurrent`](../../../../scenarios/astm/netrid/v19/dss/heavy_traffic_concurrent.py))
1. Scenario: [ASTM NetRID DSS: Endpoint encryption](../../../../scenarios/astm/netrid/v19/dss/endpoint_encryption.md) ([`scenarios.astm.netrid.v19.dss.EndpointEncryption`](../../../../scenarios/astm/netrid/v19/dss/endpoint_encryption.py))
2. Scenario: [ASTM NetRID DSS: Simple ISA](../../../../scenarios/astm/netrid/v19/dss/isa_simple.md) ([`scenarios.astm.netrid.v19.dss.ISASimple`](../../../../scenarios/astm/netrid/v19/dss/isa_simple.py))
3. Scenario: [ASTM NetRID DSS: Submitted ISA Validations](../../../../scenarios/astm/netrid/v19/dss/isa_validation.md) ([`scenarios.astm.netrid.v19.dss.ISAValidation`](../../../../scenarios/astm/netrid/v19/dss/isa_validation.py))
4. Scenario: [ASTM NetRID DSS: ISA Expiry](../../../../scenarios/astm/netrid/v19/dss/isa_expiry.md) ([`scenarios.astm.netrid.v19.dss.ISAExpiry`](../../../../scenarios/astm/netrid/v19/dss/isa_expiry.py))
5. Scenario: [ASTM NetRID DSS: ISA Subscription Interactions](../../../../scenarios/astm/netrid/v19/dss/isa_subscription_interactions.md) ([`scenarios.astm.netrid.v19.dss.ISASubscriptionInteractions`](../../../../scenarios/astm/netrid/v19/dss/isa_subscription_interactions.py))
6. Scenario: [ASTM NetRID DSS: Subscription Validation](../../../../scenarios/astm/netrid/v19/dss/subscription_validation.md) ([`scenarios.astm.netrid.v19.dss.SubscriptionValidation`](../../../../scenarios/astm/netrid/v19/dss/subscription_validation.py))
7. Scenario: [ASTM NetRID DSS: Subscription Simple](../../../../scenarios/astm/netrid/v19/dss/subscription_simple.md) ([`scenarios.astm.netrid.v19.dss.SubscriptionSimple`](../../../../scenarios/astm/netrid/v19/dss/subscription_simple.py))
8. Scenario: [ASTM F3411-19 NetRID DSS interoperability](../../../../scenarios/astm/netrid/v19/dss_interoperability.md) ([`scenarios.astm.netrid.v19.DSSInteroperability`](../../../../scenarios/astm/netrid/v19/dss_interoperability.py))
9. Scenario: [ASTM NetRID DSS: Token Validation](../../../../scenarios/astm/netrid/v19/dss/token_validation.md) ([`scenarios.astm.netrid.v19.dss.TokenValidation`](../../../../scenarios/astm/netrid/v19/dss/token_validation.py))
10. Scenario: [ASTM NetRID DSS: Direct CRDB access](../../../../scenarios/astm/netrid/v19/dss/crdb_access.md) ([`scenarios.astm.netrid.v19.dss.CRDBAccess`](../../../../scenarios/astm/netrid/v19/dss/crdb_access.py))
11. Scenario: [ASTM NetRID DSS: Concurrent Requests](../../../../scenarios/astm/netrid/v19/dss/heavy_traffic_concurrent.md) ([`scenarios.astm.netrid.v19.dss.HeavyTrafficConcurrent`](../../../../scenarios/astm/netrid/v19/dss/heavy_traffic_concurrent.py))

## [Checked requirements](../../../README.md#checked-requirements)

Expand All @@ -25,11 +26,16 @@
<th><a href="../../../README.md#checked-in">Checked in</a></th>
</tr>
<tr>
<td rowspan="40" style="vertical-align:top;"><a href="../../../../requirements/astm/f3411/v19.md">astm<br>.f3411<br>.v19</a></td>
<td rowspan="41" style="vertical-align:top;"><a href="../../../../requirements/astm/f3411/v19.md">astm<br>.f3411<br>.v19</a></td>
<td><a href="../../../../requirements/astm/f3411/v19.md">DSS0010</a></td>
<td>Implemented</td>
<td><a href="../../../../scenarios/astm/netrid/v19/dss/token_validation.md">ASTM NetRID DSS: Token Validation</a></td>
</tr>
<tr>
<td><a href="../../../../requirements/astm/f3411/v19.md">DSS0020</a></td>
<td>Implemented</td>
<td><a href="../../../../scenarios/astm/netrid/v19/dss/endpoint_encryption.md">ASTM NetRID DSS: Endpoint encryption</a></td>
</tr>
<tr>
<td><a href="../../../../requirements/astm/f3411/v19.md">DSS0030,a</a></td>
<td>Implemented</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ resources:
problematically_big_area: resources.VerticesResource
test_exclusions: resources.dev.TestExclusionsResource?
actions:
- test_scenario:
scenario_type: scenarios.astm.netrid.v19.dss.EndpointEncryption
resources:
dss: dss
- test_scenario:
scenario_type: scenarios.astm.netrid.v19.dss.ISASimple
resources:
Expand Down
7 changes: 6 additions & 1 deletion monitoring/uss_qualifier/suites/astm/netrid/f3411_22a.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@
<th><a href="../../README.md#checked-in">Checked in</a></th>
</tr>
<tr>
<td rowspan="98" style="vertical-align:top;"><a href="../../../requirements/astm/f3411/v22a.md">astm<br>.f3411<br>.v22a</a></td>
<td rowspan="99" style="vertical-align:top;"><a href="../../../requirements/astm/f3411/v22a.md">astm<br>.f3411<br>.v22a</a></td>
<td><a href="../../../requirements/astm/f3411/v22a.md">DSS0010</a></td>
<td>Implemented</td>
<td><a href="../../../scenarios/astm/netrid/v22a/dss/token_validation.md">ASTM NetRID DSS: Token Validation</a></td>
</tr>
<tr>
<td><a href="../../../requirements/astm/f3411/v22a.md">DSS0020</a></td>
<td>Implemented</td>
<td><a href="../../../scenarios/astm/netrid/v22a/dss/endpoint_encryption.md">ASTM NetRID DSS: Endpoint encryption</a></td>
</tr>
<tr>
<td><a href="../../../requirements/astm/f3411/v22a.md">DSS0030</a></td>
<td>Implemented</td>
Expand Down
Loading
Loading