Skip to content

Commit 64f2d7a

Browse files
authored
Merge branch 'main' into stabilize_scs-0214-v2
2 parents da439b1 + c3dd463 commit 64f2d7a

20 files changed

+1190
-124
lines changed

Standards/scs-XXXX-v1-security-of-iaas-service-software.md renamed to Standards/scs-0124-v1-security-of-iaas-service-software.md

File renamed without changes.

Standards/scs-XXXX-w1-security-of-iaas-service-software.md renamed to Standards/scs-0124-w1-security-of-iaas-service-software.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ type: Supplement
44
track: IaaS
55
status: Draft
66
supplements:
7-
- scs-XXXX-v1-security-of-iaas-service-software.md
7+
- scs-0124-v1-security-of-iaas-service-software.md
88
---
99

1010
## Testing or Detecting security updates in software

Standards/scs-0125-v1-secure-connections.md

Lines changed: 277 additions & 0 deletions
Large diffs are not rendered by default.

Standards/scs-0214-v1-k8s-node-distribution.md

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -80,44 +80,7 @@ If the standard is used by a provider, the following decisions are binding and v
8080
can also be scaled vertically first before scaling horizontally.
8181
- Worker node distribution MUST be indicated to the user through some kind of labeling
8282
in order to enable (anti)-affinity for workloads over "failure zones".
83-
- To provide metadata about the node distribution, which also enables testing of this standard,
84-
providers MUST label their K8s nodes with the labels listed below.
85-
- `topology.kubernetes.io/zone`
86-
87-
Corresponds with the label described in [K8s labels documentation][k8s-labels-docs].
88-
It provides a logical zone of failure on the side of the provider, e.g. a server rack
89-
in the same electrical circuit or multiple machines bound to the internet through a
90-
singular network structure. How this is defined exactly is up to the plans of the provider.
91-
The field gets autopopulated most of the time by either the kubelet or external mechanisms
92-
like the cloud controller.
93-
94-
- `topology.kubernetes.io/region`
95-
96-
Corresponds with the label described in [K8s labels documentation][k8s-labels-docs].
97-
It describes the combination of one or more failure zones into a region or domain, therefore
98-
showing a larger entity of logical failure zone. An example for this could be a building
99-
containing racks that are put into such a zone, since they're all prone to failure, if e.g.
100-
the power for the building is cut. How this is defined exactly is also up to the provider.
101-
The field gets autopopulated most of the time by either the kubelet or external mechanisms
102-
like the cloud controller.
103-
104-
- `topology.scs.community/host-id`
105-
106-
This is an SCS-specific label; it MUST contain the hostID of the physical machine running
107-
the hypervisor (NOT: the hostID of a virtual machine). Here, the hostID is an arbitrary identifier,
108-
which need not contain the actual hostname, but it should nonetheless be unique to the host.
109-
This helps identify the distribution over underlying physical machines,
110-
which would be masked if VM hostIDs were used.
111-
112-
## Conformance Tests
113-
114-
The script `k8s-node-distribution-check.py` checks the nodes available with a user-provided
115-
kubeconfig file. It then determines based on the labels `kubernetes.io/hostname`, `topology.kubernetes.io/zone`,
116-
`topology.kubernetes.io/region` and `node-role.kubernetes.io/control-plane`, if a distribution
117-
of the available nodes is present. If this isn't the case, the script produces an error.
118-
If also produces warnings and informational outputs, if e.g. labels don't seem to be set.
11983

12084
[k8s-ha]: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/
12185
[k8s-large-clusters]: https://kubernetes.io/docs/setup/best-practices/cluster-large/
12286
[scs-0213-v1]: https://github.com/SovereignCloudStack/standards/blob/main/Standards/scs-0213-v1-k8s-nodes-anti-affinity.md
123-
[k8s-labels-docs]: https://kubernetes.io/docs/reference/labels-annotations-taints/#topologykubernetesiozone

Standards/scs-0219-v1-kaas-networking.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
---
22
title: KaaS Networking Standard
33
type: Standard
4-
status: Draft
4+
status: Stable
5+
stabilized_at: 2024-11-21
56
track: KaaS
67
---
78

Tests/iaas/mandatory-services/mandatory-iaas-services.py

100644100755
Lines changed: 50 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#!/usr/bin/env python3
12
"""Mandatory APIs checker
23
This script retrieves the endpoint catalog from Keystone using the OpenStack
34
SDK and checks whether all mandatory APi endpoints, are present.
@@ -26,54 +27,30 @@
2627
block_storage_service = ["volume", "volumev3", "block-storage"]
2728

2829

29-
def connect(cloud_name: str) -> openstack.connection.Connection:
30-
"""Create a connection to an OpenStack cloud
31-
:param string cloud_name:
32-
The name of the configuration to load from clouds.yaml.
33-
:returns: openstack.connnection.Connection
34-
"""
35-
return openstack.connect(
36-
cloud=cloud_name,
37-
)
38-
39-
40-
def check_presence_of_mandatory_services(cloud_name: str, s3_credentials=None):
41-
try:
42-
connection = connect(cloud_name)
43-
services = connection.service_catalog
44-
except Exception as e:
45-
print(str(e))
46-
raise Exception(
47-
f"Connection to cloud '{cloud_name}' was not successfully. "
48-
f"The Catalog endpoint could not be accessed. "
49-
f"Please check your cloud connection and authorization."
50-
)
30+
def check_presence_of_mandatory_services(conn: openstack.connection.Connection, s3_credentials=None):
31+
services = conn.service_catalog
5132

5233
if s3_credentials:
5334
mandatory_services.remove("object-store")
5435
for svc in services:
5536
svc_type = svc['type']
5637
if svc_type in mandatory_services:
5738
mandatory_services.remove(svc_type)
58-
continue
59-
if svc_type in block_storage_service:
39+
elif svc_type in block_storage_service:
6040
block_storage_service.remove(svc_type)
6141

6242
bs_service_not_present = 0
6343
if len(block_storage_service) == 3:
6444
# neither block-storage nor volume nor volumev3 is present
6545
# we must assume, that there is no volume service
66-
logger.error("FAIL: No block-storage (volume) endpoint found.")
46+
logger.error("No block-storage (volume) endpoint found.")
6747
mandatory_services.append(block_storage_service[0])
6848
bs_service_not_present = 1
69-
if not mandatory_services:
70-
# every mandatory service API had an endpoint
71-
return 0 + bs_service_not_present
72-
else:
73-
# there were multiple mandatory APIs not found
74-
logger.error(f"FAIL: The following endpoints are missing: "
75-
f"{mandatory_services}")
76-
return len(mandatory_services) + bs_service_not_present
49+
if mandatory_services:
50+
# some mandatory APIs were not found
51+
logger.error(f"The following endpoints are missing: "
52+
f"{', '.join(mandatory_services)}.")
53+
return len(mandatory_services) + bs_service_not_present
7754

7855

7956
def list_containers(conn):
@@ -167,8 +144,8 @@ def s3_from_ostack(creds, conn, endpoint):
167144
# pass
168145

169146

170-
def check_for_s3_and_swift(cloud_name: str, s3_credentials=None):
171-
# If we get credentials we assume, that there is no Swift and only test s3
147+
def check_for_s3_and_swift(conn: openstack.connection.Connection, s3_credentials=None):
148+
# If we get credentials, we assume that there is no Swift and only test s3
172149
if s3_credentials:
173150
try:
174151
s3 = s3_conn(s3_credentials)
@@ -183,58 +160,46 @@ def check_for_s3_and_swift(cloud_name: str, s3_credentials=None):
183160
if s3_buckets == [TESTCONTNAME]:
184161
del_bucket(s3, TESTCONTNAME)
185162
# everything worked, and we don't need to test for Swift:
186-
print("SUCCESS: S3 exists")
163+
logger.info("SUCCESS: S3 exists")
187164
return 0
188165
# there were no credentials given, so we assume s3 is accessable via
189166
# the service catalog and Swift might exist too
190-
try:
191-
connection = connect(cloud_name)
192-
connection.authorize()
193-
except Exception as e:
194-
print(str(e))
195-
raise Exception(
196-
f"Connection to cloud '{cloud_name}' was not successfully. "
197-
f"The Catalog endpoint could not be accessed. "
198-
f"Please check your cloud connection and authorization."
199-
)
200167
s3_creds = {}
201168
try:
202-
endpoint = connection.object_store.get_endpoint()
203-
except Exception as e:
204-
logger.error(
205-
f"FAIL: No object store endpoint found in cloud "
206-
f"'{cloud_name}'. No testing for the s3 service possible. "
207-
f"Details: %s", e
169+
endpoint = conn.object_store.get_endpoint()
170+
except Exception:
171+
logger.exception(
172+
"No object store endpoint found. No testing for the s3 service possible."
208173
)
209174
return 1
210175
# Get S3 endpoint (swift) and ec2 creds from OpenStack (keystone)
211-
s3_from_ostack(s3_creds, connection, endpoint)
176+
s3_from_ostack(s3_creds, conn, endpoint)
212177
# Overrides (var names are from libs3, in case you wonder)
213178
s3_from_env(s3_creds, "HOST", "S3_HOSTNAME", "https://")
214179
s3_from_env(s3_creds, "AK", "S3_ACCESS_KEY_ID")
215180
s3_from_env(s3_creds, "SK", "S3_SECRET_ACCESS_KEY")
216181

217-
s3 = s3_conn(s3_creds, connection)
182+
s3 = s3_conn(s3_creds, conn)
218183
s3_buckets = list_s3_buckets(s3)
219184
if not s3_buckets:
220185
s3_buckets = create_bucket(s3, TESTCONTNAME)
221186
assert s3_buckets
222187

223188
# If we got till here, s3 is working, now swift
224-
swift_containers = list_containers(connection)
189+
swift_containers = list_containers(conn)
225190
# if not swift_containers:
226-
# swift_containers = create_container(connection, TESTCONTNAME)
191+
# swift_containers = create_container(conn, TESTCONTNAME)
227192
result = 0
228193
if Counter(s3_buckets) != Counter(swift_containers):
229-
print("WARNING: S3 buckets and Swift Containers differ:\n"
230-
f"S3: {sorted(s3_buckets)}\nSW: {sorted(swift_containers)}")
194+
logger.warning("S3 buckets and Swift Containers differ:\n"
195+
f"S3: {sorted(s3_buckets)}\nSW: {sorted(swift_containers)}")
231196
result = 1
232197
else:
233-
print("SUCCESS: S3 and Swift exist and agree")
198+
logger.info("SUCCESS: S3 and Swift exist and agree")
234199
# Clean up
235200
# FIXME: Cleanup created EC2 credential
236201
# if swift_containers == [TESTCONTNAME]:
237-
# del_container(connection, TESTCONTNAME)
202+
# del_container(conn, TESTCONTNAME)
238203
# Cleanup created S3 bucket
239204
if s3_buckets == [TESTCONTNAME]:
240205
del_bucket(s3, TESTCONTNAME)
@@ -266,34 +231,47 @@ def main():
266231
help="Enable OpenStack SDK debug logging"
267232
)
268233
args = parser.parse_args()
234+
logging.basicConfig(
235+
format="%(levelname)s: %(message)s",
236+
level=logging.DEBUG if args.debug else logging.INFO,
237+
)
269238
openstack.enable_logging(debug=args.debug)
270239

271240
# parse cloud name for lookup in clouds.yaml
272-
cloud = os.environ.get("OS_CLOUD", None)
273-
if args.os_cloud:
274-
cloud = args.os_cloud
275-
assert cloud, (
276-
"You need to have the OS_CLOUD environment variable set to your cloud "
277-
"name or pass it via --os-cloud"
278-
)
241+
cloud = args.os_cloud or os.environ.get("OS_CLOUD", None)
242+
if not cloud:
243+
raise RuntimeError(
244+
"You need to have the OS_CLOUD environment variable set to your "
245+
"cloud name or pass it via --os-cloud"
246+
)
279247

280248
s3_credentials = None
281249
if args.s3_endpoint:
282250
if (not args.s3_access) or (not args.s3_access_secret):
283-
print("WARNING: test for external s3 needs access key and access secret.")
251+
logger.warning("test for external s3 needs access key and access secret.")
284252
s3_credentials = {
285253
"AK": args.s3_access,
286254
"SK": args.s3_access_secret,
287255
"HOST": args.s3_endpoint
288256
}
289257
elif args.s3_access or args.s3_access_secret:
290-
print("WARNING: access to s3 was given, but no endpoint provided.")
258+
logger.warning("access to s3 was given, but no endpoint provided.")
291259

292-
result = check_presence_of_mandatory_services(cloud, s3_credentials)
293-
result = result + check_for_s3_and_swift(cloud, s3_credentials)
260+
with openstack.connect(cloud) as conn:
261+
result = check_presence_of_mandatory_services(conn, s3_credentials)
262+
result += check_for_s3_and_swift(conn, s3_credentials)
263+
264+
print('service-apis-check: ' + ('PASS', 'FAIL')[min(1, result)])
294265

295266
return result
296267

297268

298269
if __name__ == "__main__":
299-
main()
270+
try:
271+
sys.exit(main())
272+
except SystemExit:
273+
raise
274+
except BaseException as exc:
275+
logging.debug("traceback", exc_info=True)
276+
logging.critical(str(exc))
277+
sys.exit(1)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Secure Connections Standard Test Suite
2+
3+
## Test Environment Setup
4+
5+
> **NOTE:** The test execution procedure does not require cloud admin rights.
6+
7+
A valid cloud configuration for the OpenStack SDK in the shape of "`clouds.yaml`" is mandatory[^1].
8+
**This file is expected to be located in the current working directory where the test script is executed unless configured otherwise.**
9+
10+
[^1]: [OpenStack Documentation: Configuring OpenStack SDK Applications](https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html)
11+
12+
The test execution environment can be located on any system outside of the cloud infrastructure that has OpenStack API access.
13+
Make sure that the API access is configured properly in "`clouds.yaml`".
14+
15+
It is recommended to use a Python virtual environment[^2].
16+
Next, install the libraries required by the test suite:
17+
18+
```bash
19+
pip3 install openstacksdk sslyze
20+
```
21+
22+
> Note: the version of the sslyze library determines the [version of the Mozilla TLS recommendation JSON](https://wiki.mozilla.org/Security/Server_Side_TLS#JSON_version_of_the_recommendations) that it checks against.
23+
24+
Within this environment execute the test suite.
25+
26+
[^2]: [Python 3 Documentation: Virtual Environments and Packages](https://docs.python.org/3/tutorial/venv.html)
27+
28+
## Test Execution
29+
30+
The test suite is executed as follows:
31+
32+
```bash
33+
python3 tls-checker.py --os-cloud mycloud
34+
```
35+
36+
As an alternative to "`--os-cloud`", the "`OS_CLOUD`" environment variable may be specified instead.
37+
The parameter is used to look up the correct cloud configuration in "`clouds.yaml`".
38+
For the example command above, this file should contain a `clouds.mycloud` section like this:
39+
40+
```yaml
41+
---
42+
clouds:
43+
mycloud:
44+
auth:
45+
auth_url: ...
46+
...
47+
...
48+
```
49+
50+
For any further options consult the output of "`python3 tls-checker.py --help`".
51+
52+
### Script Behavior & Test Results
53+
54+
The script will print all actions and passed tests to `stdout`.
55+
56+
If all tests pass, the script will return with an exit code of `0`.
57+
58+
If any test fails, the script will halt, print the exact error to `stderr` and return with a non-zero exit code.
59+
60+
Any tests that indicate a recommendation of the standard is not met, will print a warning message under the corresponding endpoint output.
61+
However, unmet recommendations will not count as errors.

0 commit comments

Comments
 (0)