Skip to content

Commit

Permalink
[uss_qualifier/scenarios/dss] Add CockroachDBAccess test scenarios (#513
Browse files Browse the repository at this point in the history
)

* [uss_qualifier/utm/dss] Add CockroachDBAccess scenario for testing DSS0200+DSS0205

* clarify why psycopg is used
  • Loading branch information
mickmis authored Mar 7, 2024
1 parent 93e67ef commit 4657404
Show file tree
Hide file tree
Showing 41 changed files with 579 additions and 93 deletions.
1 change: 1 addition & 0 deletions build/dev/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ services:
- 26257
ports:
- "8080:8080"
- "26257:26257"
restart: always
networks:
- dss_internal_network
Expand Down
10 changes: 9 additions & 1 deletion monitoring/uss_qualifier/configurations/dev/dss_probing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,12 @@ v1:
uss1: all_astm_dss_requirements
uss2: all_astm_dss_requirements
validation:
$ref: ./library/validation.yaml#/normal_test
criteria:
- $ref: ./library/validation.yaml#/execution_error_none
- $ref: ./library/validation.yaml#/failed_check_severity_max_low
- applicability:
skipped_actions: {}
pass_condition:
elements:
count:
equal_to: 6 # 6 CRDBAccess scenarios are skipped
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,17 @@ scd_dss_instances:
base_url: http://dss.uss2.localutm
has_private_address: true

# ===== DSS CockroachDB nodes =====

dss_crdb_cluster:
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json
resource_type: resources.interuss.crdb.CockroachDBClusterResource
specification:
nodes:
- participant_id: uss1
host: crdb.uss1.localutm
port: 26257

# ===== mock_uss instances =====

mock_uss_instance_uss1:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,17 @@ scd_dss_instances:
base_url: http://localhost:8082
has_private_address: true

# ===== DSS CockroachDB nodes =====

dss_crdb_cluster:
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json
resource_type: resources.interuss.crdb.CockroachDBClusterResource
specification:
nodes:
- participant_id: uss1
host: localhost
port: 26257

# ===== mock_uss instances =====

mock_uss_instance_uss1:
Expand Down
47 changes: 28 additions & 19 deletions monitoring/uss_qualifier/configurations/dev/library/validation.yaml
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
execution_error_none:
applicability:
test_scenarios: { }
pass_condition:
each_element:
has_execution_error: false

failed_check_severity_max_low:
applicability:
failed_checks:
has_severity:
higher_than: Low
pass_condition:
elements:
count:
equal_to: 0

skipped_action_none:
applicability:
skipped_actions: {}
pass_condition:
elements:
count:
equal_to: 0

normal_test:
$content_schema: monitoring/uss_qualifier/reports/validation/report_validation/ValidationConfiguration.json
criteria:
- applicability:
test_scenarios: {}
pass_condition:
each_element:
has_execution_error: false
- applicability:
failed_checks:
has_severity:
higher_than: Low
pass_condition:
elements:
count:
equal_to: 0
- applicability:
skipped_actions: {}
pass_condition:
elements:
count:
equal_to: 0
- $ref: '#/execution_error_none'
- $ref: '#/failed_check_severity_max_low'
- $ref: '#/skipped_action_none'
10 changes: 9 additions & 1 deletion monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,12 @@ v1:
uss2: sp_dss
sequence_view: {}
validation:
$ref: ./library/validation.yaml#/normal_test
criteria:
- $ref: ./library/validation.yaml#/execution_error_none
- $ref: ./library/validation.yaml#/failed_check_severity_max_low
- applicability:
skipped_actions: {}
pass_condition:
elements:
count:
equal_to: 2 # 2 CRDBAccess scenarios are skipped
10 changes: 9 additions & 1 deletion monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,12 @@ v1:
uss2: sp_dss
sequence_view: {}
validation:
$ref: ./library/validation.yaml#/normal_test
criteria:
- $ref: ./library/validation.yaml#/execution_error_none
- $ref: ./library/validation.yaml#/failed_check_severity_max_low
- applicability:
skipped_actions: {}
pass_condition:
elements:
count:
equal_to: 2 # 2 CRDBAccess scenarios are skipped
10 changes: 9 additions & 1 deletion monitoring/uss_qualifier/configurations/dev/uspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,12 @@ v1:
uss2: uspace
sequence_view: {}
validation:
$ref: ./library/validation.yaml#/normal_test
criteria:
- $ref: ./library/validation.yaml#/execution_error_none
- $ref: ./library/validation.yaml#/failed_check_severity_max_low
- applicability:
skipped_actions: {}
pass_condition:
elements:
count:
equal_to: 4 # 4 CRDBAccess scenarios are skipped
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .crdb import CockroachDBNodeResource, CockroachDBClusterResource, CockroachDBNode
142 changes: 142 additions & 0 deletions monitoring/uss_qualifier/resources/interuss/crdb/crdb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
from __future__ import annotations

from typing import Tuple, List, Optional

from implicitdict import ImplicitDict
from monitoring.uss_qualifier.resources.resource import Resource
import psycopg.errors
from psycopg import crdb


class CockroachDBNodeSpecification(ImplicitDict):
participant_id: str
"""ID of the USS responsible for this CockroachDB node"""

host: str
"""Host where the CockroachDB node is reachable."""

port: int
"""Port to which CockroachDB node is listening to."""

def __init__(self, *args, **kwargs):
super().__init__(**kwargs)


class CockroachDBNodeResource(Resource[CockroachDBNodeSpecification]):
_specification: CockroachDBNodeSpecification

def __init__(
self,
specification: CockroachDBNodeSpecification,
):
self._specification = specification

def get_client(self) -> CockroachDBNode:
return CockroachDBNode(
self._specification.participant_id,
self._specification.host,
self._specification.port,
)

def is_same_as(self, other: CockroachDBNodeResource) -> bool:
return self._specification == other._specification

@property
def participant_id(self) -> str:
return self._specification.participant_id


class CockroachDBClusterSpecification(ImplicitDict):
nodes: List[CockroachDBNodeSpecification]


class CockroachDBClusterResource(Resource[CockroachDBClusterSpecification]):
nodes: List[CockroachDBNodeResource]

def __init__(
self,
specification: CockroachDBClusterSpecification,
):
self.nodes = [
CockroachDBNodeResource(
specification=s,
)
for s in specification.nodes
]


class CockroachDBNode(object):
participant_id: str
host: str
port: int

def __init__(
self,
participant_id: str,
host: str,
port: int,
):
self.participant_id = participant_id
self.host = host
self.port = port

def connect(self, **kwargs) -> crdb.connection.CrdbConnection:
return crdb.connect(
host=self.host,
port=self.port,
**kwargs,
)

def is_reachable(self) -> Tuple[bool, Optional[psycopg.Error]]:
"""
Returns True if the node is reachable.
This is detected by attempting to establish a connection with the node
not requiring encryption and validating either 1) that the connection
fails with the error message reporting that the authentication failed;
or 2) that the connection succeeds.
"""
try:
c = self.connect(sslmode="allow", require_auth="password", password="dummy")
c.close()
except psycopg.OperationalError as e:
err_msg = str(e)
is_reachable = "password authentication failed" in err_msg
return is_reachable, e
return True, None

def runs_in_secure_mode(self) -> Tuple[bool, Optional[psycopg.Error]]:
"""
Returns True if the node is running in secure mode.
This is detected by attempting to establish a connection with the node
in insecure mode and validating that the connection fails with the error
message reporting that the node is running in secure mode.
"""
try:
c = self.connect(sslmode="disable")
c.close()
except psycopg.OperationalError as e:
err_msg = str(e)
secure_mode = "node is running secure mode" in err_msg
return secure_mode, e
return False, None

def legacy_ssl_version_rejected(self) -> Tuple[bool, Optional[psycopg.Error]]:
"""
Returns True if the node rejects the usage of the legacy cryptographic
protocols TLSv1 and TLSv1.1.
This is detected by attempting to establish a connection with the node
forcing the client to use a TLS version < 1.2 and validating that the
connection fails with the expected error message.
"""
try:
c = self.connect(
sslmode="require",
ssl_min_protocol_version="TLSv1",
ssl_max_protocol_version="TLSv1.1",
)
c.close()
except psycopg.OperationalError as e:
err_msg = str(e)
legacy_rejected = "tlsv1 alert protocol version" in err_msg
return legacy_rejected, e
return False, None
70 changes: 68 additions & 2 deletions monitoring/uss_qualifier/scenarios/astm/dss/crdb_access.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,78 @@
from typing import List

from monitoring.uss_qualifier.resources.interuss.crdb import (
CockroachDBClusterResource,
CockroachDBNode,
)
from monitoring.uss_qualifier.scenarios.scenario import GenericTestScenario
from monitoring.uss_qualifier.suites.suite import ExecutionContext


class CRDBAccess(GenericTestScenario):
def __init__(self):
crdb_nodes: List[CockroachDBNode] = []

def __init__(
self,
crdb_cluster: CockroachDBClusterResource,
):
super().__init__()
for node in crdb_cluster.nodes:
self.crdb_nodes.append(node.get_client())

def run(self, context: ExecutionContext):
self.begin_test_scenario(context)
# TODO: Implement

self.begin_test_case("Setup")
self._setup()
self.end_test_case()

self.begin_test_case("Verify security interoperability")
self._attempt_connection()
self.end_test_case()

self.end_test_scenario()

def _setup(self) -> None:
self.begin_test_step("Validate nodes are reachable")
for node in self.crdb_nodes:
with self.check(
"Node is reachable",
node.participant_id,
) as check:
reachable, e = node.is_reachable()
if not reachable:
check.record_failed(
"Node is not reachable",
details=f"Error message: {e}",
)

self.end_test_step()

def _attempt_connection(self) -> None:
self.begin_test_step("Attempt to connect in insecure mode")
for node in self.crdb_nodes:
with self.check(
"Node runs in secure mode",
node.participant_id,
) as check:
secure_mode, e = node.runs_in_secure_mode()
if not secure_mode:
check.record_failed(
"Node is not in secure mode",
details=f"Reported connection error (if any): {e}",
)
self.end_test_step()

self.begin_test_step("Attempt to connect with legacy encryption protocol")
for node in self.crdb_nodes:
with self.check(
"Node rejects legacy encryption protocols",
node.participant_id,
) as check:
rejected, e = node.legacy_ssl_version_rejected()
if not rejected:
check.record_failed(
"Node did not reject connection with legacy encryption protocol",
details=f"Reported connection error (if any): {e}",
)
self.end_test_step()
Loading

0 comments on commit 4657404

Please sign in to comment.