Skip to content

Commit f74a5e8

Browse files
authored
Merge branch 'main' into add_larger_flavors
2 parents 1647fa7 + 58d2683 commit f74a5e8

15 files changed

+154
-64
lines changed

.zuul.d/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
# timeout:
6363
# a) these tests take a lot of time, I'm afraid, particularly Sonobuoy
6464
# b) keep in mind that this job covers ALL test subjects (at most 4 in parallel)
65-
timeout: 18000 # 5 hrs -- 2.5 hrs was not enough
65+
timeout: 21600 # 6 hrs -- 5 hrs was almost sufficient (reports came through sometimes)
6666
vars:
6767
preset: kaas
6868
iaas: false

Standards/scs-0003-v1-sovereign-cloud-standards-yaml.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,15 @@ TBD
253253

254254
### Test-case descriptor
255255

256-
| Key | Type | Description | Example |
257-
| ----------------- | --------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------- |
258-
| `id` | String | Identifier for this test case (immutable and unique within this module) | `image-md-check` |
259-
| `lifetime` | String | One of: `day` (_default_), `week`, `month`, `quarter`; the test result is valid until the end of the next period | `week` |
260-
| `tags` | List of strings | A tag is a keyword that will be used to select this test case using a selector expression | `[mandatory]` |
261-
| `description` | String | Short description of the test case | |
256+
| Key | Type | Description | Example |
257+
| ----------------- | --------------- | ------------------------------------------------------------------------------------------------- | ----------------- |
258+
| `id` | String | Identifier for this test case (immutable and unique within this module) | `image-md-check` |
259+
| `lifetime` | String | One of: `day`, `week` (_default_), `month`, `quarter`, `year` | `day` |
260+
| `tags` | List of strings | A tag is a keyword that will be used to select this test case using a selector expression | `[mandatory]` |
261+
| `description` | String | Short description of the test case | |
262+
263+
A test result is valid until the end of the next period, except when lifetime is `year`: then the result is
264+
valid until the end of the following month plus one year.
262265

263266
A tag MUST NOT contain any of these characters: space, comma, exclamation mark, forward slash.
264267

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,29 @@ supplements:
99

1010
## Testing or Detecting security updates in software
1111

12-
It is not always possible to automatically test, whether the software has the newest security updates.
13-
This is because software versions may differ or some CSPs might have added downstream code parts or using other software than the reference.
14-
Also vulnerabilites and their fixes are quite different in testing, some might not be testable while others are.
15-
Additionally testing might be perceived as an attack on the infrastructure.
12+
It is not always possible to automatically test whether the software has the newest security updates.
13+
This is because software versions may differ, or some CSPs might have added downstream code parts or be using other software than the reference.
14+
Also vulnerabilities and their fixes are quite different in testing; some might not be testable while others are.
15+
Additionally, testing might be perceived as an attack on the infrastructure.
1616
So this standard will rely on the work and information CSPs must provide.
17-
There are different cases and procedures which are addressed in the following parts, that lead to compliance for this standard.
17+
There are different cases and procedures, which are addressed in the following parts, that lead to compliance for this standard.
1818

19-
### Procedure to become compliant to the security of IaaS service software Standard
19+
### Procedure to become compliant with the security of IaaS service software standard
2020

2121
This is the procedure when a new deployment wants to achieve SCS-conformancy.
2222
There are two states such a deployment can be in:
2323

24-
1. When a deployment is newly build or installed it usually uses software which includes all the latest security and bug fixes.
25-
Such deployments should be considered compliant to the standard.
24+
1. When a deployment is newly built or installed, it usually uses software that includes all the latest security and bug fixes.
25+
Such deployments should be considered compliant with the standard.
2626

27-
2. When a CSP wants to make an older deployment compliant to the SCS standards and thus also to this standard, it should be checked, whether the running software is up to date and all vulnerabilites are fixed.
27+
2. When a CSP aims to make an older deployment compliant with the SCS standards, it should be checked whether the running software is up-to-date and no known vulnerabilities are present.
2828
Any updates or upgrades to even newer versions should be done before the SCS compliance for every other standard is checked.
2929
Afterwards the CSP may provide information about the used software in an SBOM or otherwise should provide a notice about the deployment having integrated all necessary vulnerability patches.
3030

31-
### Procedure when new vulnerabilites are discovered
31+
### Procedure when new vulnerabilities are discovered
3232

3333
Whenever there are new vulnerabilities discovered in IaaS service software like OpenStack there is either an internal discussion ongoing or it is just a smaller issue.
34-
In the first case CSPs should have someone following such discussions and may even help preparing and testing patches.
34+
In the first case, CSPs should have someone following such discussions and may even help prepare and test patches.
3535
From the moment on the vulnerability is disclosed publicly, the risk of it being actively exploited increases greatly.
3636
So CSPs MUST watch out for announcements like in the OSSAs and OSSNs and when they are affected, update their deployment within the following timeframes according to the severity of the issue:
3737

@@ -40,6 +40,6 @@ So CSPs MUST watch out for announcements like in the OSSAs and OSSNs and when th
4040
3. Mid (CVSS = 4.0 – 6.9): 1 month
4141
4. Low (CVSS = 0.1 – 3.9): 3 months
4242

43-
Afterwards CSPs MUST provide a notice to the OSBA, that they are not or not anymore affected by the vulnerabilty.
43+
Afterwards CSPs MUST provide a notice to the OSBA, that they are not or not any more affected by the vulnerability.
4444
This can be done through either telling, what patches were integrated or showing configuration that renders the attack impossible.
4545
It could also be provided a list of services, when the affected service is not used in that deployment.

Standards/scs-0210-v2-k8s-version-policy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ support period. The remaining 2 months are the end-of-life support period for th
1919
- dependency issues (including base image updates)
2020
- critical core component issues
2121

22-
More information can be found under [Kubernetes Support Period].
22+
More information can be found under [Kubernetes Support Period][k8s-support-period].
2323

2424
The [Kubernetes release cycle][k8s-release-cycle] is set around 4 months, which
2525
usually results in about **3 minor** releases per year.

Tests/kaas/plugin/plugin_clusterstacks.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ def _auto_vars_syself(self, api_instance: _csh.CustomObjectsApi):
157157
return {'cs_class_name': cs_class_name, 'cs_version': cs_version}
158158

159159
def _auto_vars(self, auto_vars_kind, co_api: _csh.CustomObjectsApi):
160+
# set default values regardless of auto_vars_kind
161+
self.vars.setdefault('num_control_nodes', 3)
162+
self.vars.setdefault('num_worker_nodes', 3)
163+
# now on to specifics
160164
if not auto_vars_kind:
161165
return
162166
if auto_vars_kind == 'syself':

Tests/scs-compatible-kaas.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ modules:
2020
testcases:
2121
- id: cncf-k8s-conformance
2222
tags: [mandatory]
23+
lifetime: year
2324
description: >
2425
Must fulfill all requirements of [CNCF Kubernetes conformance](https://github.com/cncf/k8s-conformance/tree/master)
2526
- id: scs-0210-v2

Tests/scs-compliance-check.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ def run_check_tool(executable, args, env=None, cwd=None):
7373
if executable.startswith("file://"):
7474
executable = executable[7:]
7575
exe = [os.path.abspath(os.path.join(cwd or ".", executable)), *shlex.split(args)]
76+
# use the same interpreter for Python in order to inherit virtual env
77+
# necessary in cases where the interpreter is used from a virtual env that has not been activated
78+
# (I think this case should be supported if possible with little effort)
79+
if exe and exe[0].endswith('.py'):
80+
# logger.debug(f'using interpreter {sys.executable}')
81+
exe.insert(0, sys.executable)
7682
return subprocess.run(
7783
exe, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
7884
encoding='UTF-8', check=False, env=env, cwd=cwd,

Tests/scs_cert_lib.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,11 @@ def add_period(dt: datetime, period: str) -> datetime:
120120
https://docs.scs.community/standards/scs-0004-v1-achieving-certification#regulations
121121
"""
122122
# compute the moment of expiry (so we are valid before that point, but not on that point)
123-
if period is None or period == 'day': # day is default, so use it if period is None
123+
if period == 'day':
124124
dt += timedelta(days=2)
125125
return datetime(dt.year, dt.month, dt.day) # omit time so as to arrive at midnight
126-
if period == 'week':
126+
# week is default, so use it if period is None
127+
if period is None or period == 'week':
127128
dt += timedelta(days=14 - dt.weekday())
128129
return datetime(dt.year, dt.month, dt.day) # omit time so as to arrive at midnight
129130
if period == 'month':
@@ -140,6 +141,13 @@ def add_period(dt: datetime, period: str) -> datetime:
140141
if dt.month >= 4:
141142
return datetime(dt.year, 10, 1)
142143
return datetime(dt.year, 7, 1)
144+
if period == 'year':
145+
if dt.month == 11:
146+
return datetime(dt.year + 2, 1, 1)
147+
if dt.month == 12:
148+
return datetime(dt.year + 2, 2, 1)
149+
return datetime(dt.year + 1, dt.month + 2, 1)
150+
raise RuntimeError(f'unknown period: {period}')
143151

144152

145153
def parse_selector(selector_str: str) -> list[list[str]]:

compliance-monitor/bootstrap.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,19 @@ accounts:
3131
public_key_type: "ssh-ed25519"
3232
public_key_name: "primary"
3333
- subject: pco-prod1
34+
group: pco-prod
3435
delegates:
3536
- zuul_ci
3637
- subject: pco-prod2
38+
group: pco-prod
3739
delegates:
3840
- zuul_ci
3941
- subject: pco-prod3
42+
group: pco-prod
4043
delegates:
4144
- zuul_ci
4245
- subject: pco-prod4
46+
group: pco-prod
4347
delegates:
4448
- zuul_ci
4549
- subject: poc-wgcloud
@@ -56,9 +60,11 @@ accounts:
5660
delegates:
5761
- zuul_ci
5862
- subject: syseleven-dus2
63+
group: syseleven
5964
delegates:
6065
- zuul_ci
6166
- subject: syseleven-ham1
67+
group: syseleven
6268
delegates:
6369
- zuul_ci
6470
- subject: wavestack

compliance-monitor/monitor.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
db_find_account, db_update_account, db_update_publickey, db_filter_publickeys, db_get_reports,
4242
db_get_keys, db_insert_report, db_get_recent_results2, db_patch_approval2, db_get_report,
4343
db_ensure_schema, db_get_apikeys, db_update_apikey, db_filter_apikeys, db_clear_delegates,
44-
db_find_subjects, db_insert_result2, db_get_relevant_results2, db_add_delegate,
44+
db_find_subjects, db_insert_result2, db_get_relevant_results2, db_add_delegate, db_get_group,
4545
)
4646

4747

@@ -79,6 +79,7 @@ def __init__(self):
7979
self.yaml_path = os.path.abspath("../Tests")
8080

8181

82+
GROUP_PREFIX = 'group-'
8283
ROLES = {'read_any': 1, 'append_any': 2, 'admin': 4, 'approve': 8}
8384
# number of days that expired results will be considered in lieu of more recent, but unapproved ones
8485
GRACE_PERIOD_DAYS = 7
@@ -226,7 +227,8 @@ def import_bootstrap(bootstrap_path, conn):
226227
with conn.cursor() as cur:
227228
for account in accounts:
228229
roles = sum(ROLES[r] for r in account.get('roles', ()))
229-
accountid = db_update_account(cur, {'subject': account['subject'], 'roles': roles})
230+
acc_record = {'subject': account['subject'], 'roles': roles, 'group': account.get('group')}
231+
accountid = db_update_account(cur, acc_record)
230232
db_clear_delegates(cur, accountid)
231233
for delegate in account.get('delegates', ()):
232234
db_add_delegate(cur, accountid, delegate)
@@ -513,8 +515,8 @@ def convert_result_rows_to_dict2(
513515
missing.add((scope_uuid, version, testcase_id))
514516
continue
515517
# drop value if too old
516-
expires_at = add_period(checked_at, testcase.get('lifetime'))
517-
if now >= expires_at:
518+
lifetime = testcase.get('lifetime') # leave None if not present; to be handled by add_period
519+
if now >= add_period(checked_at, lifetime):
518520
continue
519521
tc_result = dict(result=result, checked_at=checked_at)
520522
if include_report:
@@ -637,6 +639,13 @@ async def get_report_view_full(
637639
)
638640

639641

642+
def _resolve_group(cur, subject, prefix=GROUP_PREFIX):
643+
group = subject.removeprefix(prefix)
644+
if subject != group:
645+
return group, db_get_group(cur, group)
646+
return None, [subject]
647+
648+
640649
@app.get("/{view_type}/detail/{subject}/{scopeuuid}")
641650
async def get_detail(
642651
request: Request,
@@ -646,14 +655,18 @@ async def get_detail(
646655
scopeuuid: str,
647656
):
648657
with conn.cursor() as cur:
649-
rows2 = db_get_relevant_results2(cur, subject, scopeuuid, approved_only=True)
658+
group, subjects = _resolve_group(cur, subject)
659+
rows2 = []
660+
for subj in subjects:
661+
rows2.extend(db_get_relevant_results2(cur, subj, scopeuuid, approved_only=True))
650662
results2 = convert_result_rows_to_dict2(
651663
rows2, get_scopes(), include_report=True, grace_period_days=GRACE_PERIOD_DAYS,
652664
subjects=(subject, ), scopes=(scopeuuid, ),
653665
)
666+
title = f'Details for group {group}' if group else f'Details for subject {subject}'
654667
return render_view(
655668
VIEW_DETAIL, view_type, results=results2, base_url=settings.base_url,
656-
title=f'{subject} compliance',
669+
title=title,
657670
)
658671

659672

@@ -666,13 +679,17 @@ async def get_detail_full(
666679
scopeuuid: str,
667680
):
668681
with conn.cursor() as cur:
669-
rows2 = db_get_relevant_results2(cur, subject, scopeuuid, approved_only=False)
682+
group, subjects = _resolve_group(cur, subject)
683+
rows2 = []
684+
for subject in subjects:
685+
rows2.extend(db_get_relevant_results2(cur, subject, scopeuuid, approved_only=False))
670686
results2 = convert_result_rows_to_dict2(
671687
rows2, get_scopes(), include_report=True, subjects=(subject, ), scopes=(scopeuuid, ),
672688
)
689+
title = f'Details for group {group}' if group else f'Details for subject {subject}'
673690
return render_view(
674691
VIEW_DETAIL, view_type, results=results2, base_url=settings.base_url,
675-
title=f'{subject} compliance (incl. unverified results)',
692+
title=f'{title} (incl. unverified results)',
676693
)
677694

678695

@@ -785,13 +802,34 @@ async def get_healthz(request: Request):
785802
return Response() # empty response with status 200
786803

787804

788-
def pick_filter(results, subject, scope):
805+
def pick_filter(results, scope, *subjects):
789806
"""Jinja filter to pick scope results from `results` for given `subject` and `scope`"""
790-
return results.get(subject, {}).get(scope, {})
807+
# simple case (backwards compatible): precisely one subject
808+
if len(subjects) == 1:
809+
return results.get(subjects[0], {}).get(scope, {})
810+
# generalized case: multiple subjects
811+
# in this case, drop None
812+
rs = [results.get(subject, {}).get(scope, {}) for subject in subjects]
813+
return [r for r in rs if r is not None]
814+
815+
816+
STATUS_ORDERING = {
817+
'effective': 10,
818+
'warn': 5,
819+
'deprecated': 1,
820+
}
791821

792822

793823
def summary_filter(scope_results):
794824
"""Jinja filter to construct summary from `scope_results`"""
825+
if not isinstance(scope_results, dict):
826+
# new generalized case: "aggregate" results for multiple subjects
827+
# simplified computation: just select the worst subject to represent the group
828+
scope_results = min(
829+
scope_results,
830+
default={},
831+
key=lambda sr: STATUS_ORDERING.get(sr.get('best_passed'), -1),
832+
)
795833
passed_str = scope_results.get('passed_str', '') or '–'
796834
best_passed = scope_results.get('best_passed')
797835
# avoid simple 🟢🔴 (hard to distinguish for color-blind folks)

0 commit comments

Comments
 (0)