Skip to content

Commit e4d2a59

Browse files
committed
Merge tag 'v1.9.1'
2 parents b3ca3cb + 7053579 commit e4d2a59

File tree

23 files changed

+411
-101
lines changed

23 files changed

+411
-101
lines changed

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changelog
22

3+
## 1.9.1
4+
5+
### Breaking changes
6+
7+
- **Remove Unused Scopes**: Removed `RECORD_PRINT_RECORDS_SUPPORTING_DOCUMENTS` and `RECORD_EXPORT_RECORDS` scopes from `REGISTRATION_AGENT`, `LOCAL_REGISTRAR` and `NATIONAL_REGISTRAR`
8+
9+
### Improvements
10+
11+
- Make encryption step optional [#1123](https://github.com/opencrvs/opencrvs-countryconfig/pull/1123)
12+
- Added validation for ENCRYPTION_KEY [#10896](https://github.com/opencrvs/opencrvs-core/issues/10896)
13+
314
## 1.9.0
415

516
### New features
@@ -24,15 +35,13 @@
2435
- Store system monitoring data for 1 month [#10515](https://github.com/opencrvs/opencrvs-core/issues/10515)
2536

2637
- Restricted filesystem usage for journal service and file rotation strategy [#10518](https://github.com/opencrvs/opencrvs-core/issues/10518))
27-
2838
- Tiltfile: Improved Kubernetes support for development environment [#10672](https://github.com/opencrvs/opencrvs-core/issues/10672)
2939

3040
### Bug fixes
3141

3242
- Allow non-interactive upgrades with apt [#10204](https://github.com/opencrvs/opencrvs-core/issues/10204)
3343
- Don't restart events service after data cleanup [#10704](https://github.com/opencrvs/opencrvs-core/issues/10704)
3444

35-
3645
## 1.8.1
3746

3847
### Bug fixes

infrastructure/docker-compose.deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ services:
1111
# Note: these published port will override UFW rules as Docker manages it's own iptables
1212
# Only publish the exact ports that are required for OpenCRVS to work
1313
traefik:
14-
image: 'traefik:v2.10'
14+
image: 'traefik:v2.11'
1515
ports:
1616
- target: 80
1717
published: 80

infrastructure/environments/setup-environment.ts

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ const countryQuestions = [
386386
}
387387
]
388388

389-
const infrastructureQuestions = [
389+
const diskQuestions = [
390390
{
391391
name: 'diskSpace',
392392
type: 'text' as const,
@@ -397,8 +397,10 @@ const infrastructureQuestions = [
397397
validate: notEmpty,
398398
valueLabel: 'DISK_SPACE',
399399
initial: process.env.DISK_SPACE || '200g',
400-
scope: 'ENVIRONMENT' as const
400+
scope: 'ENVIRONMENT' as const,
401401
},
402+
]
403+
const infrastructureQuestions = [
402404
{
403405
name: 'domain',
404406
type: 'text' as const,
@@ -780,6 +782,7 @@ ALL_QUESTIONS.push(
780782
...dockerhubQuestions,
781783
...sshQuestions,
782784
...sshKeyQuestions,
785+
...diskQuestions,
783786
...infrastructureQuestions,
784787
...countryQuestions,
785788
...databaseAndMonitoringQuestions,
@@ -887,7 +890,7 @@ const SPECIAL_NON_APPLICATION_ENVIRONMENTS = ['jump', 'backup']
887890
...existingRepositoryVariable,
888891
...existingEnvironmentSecrets
889892
]
890-
893+
let enableEncryption = true
891894
if (
892895
existingEnvironmentVariables.length > 0 ||
893896
existingEnvironmentSecrets.length > 0
@@ -967,7 +970,32 @@ const SPECIAL_NON_APPLICATION_ENVIRONMENTS = ['jump', 'backup']
967970
)
968971
}
969972
} else {
970-
log('\n', kleur.bold().underline('Server setup'))
973+
log('\n', kleur.bold().underline('Server setup'), '\n')
974+
const encryption_key_defined = findExistingValue(
975+
'ENCRYPTION_KEY',
976+
'SECRET',
977+
'ENVIRONMENT',
978+
existingValues
979+
)
980+
981+
if (!encryption_key_defined) {
982+
const answers_enable_encryption = await prompts(
983+
[
984+
{
985+
name: 'enableEncryption',
986+
type: 'confirm' as const,
987+
message: 'Do you want to enable disk encryption?',
988+
scope: 'ENVIRONMENT' as const,
989+
initial: Boolean(process.env.ENABLE_ENCRYPTION)
990+
}
991+
].map(questionToPrompt)
992+
)
993+
enableEncryption = answers_enable_encryption.enableEncryption
994+
}
995+
if (enableEncryption) {
996+
console.log('\n', kleur.bold().green('✔'), kleur.bold().yellow(' Disk encryption is enabled'))
997+
await promptAndStoreAnswer(environment, diskQuestions, existingValues)
998+
}
971999
const { domain } = await promptAndStoreAnswer(
9721000
environment,
9731001
infrastructureQuestions,
@@ -1101,6 +1129,26 @@ const SPECIAL_NON_APPLICATION_ENVIRONMENTS = ['jump', 'backup']
11011129
}
11021130
]
11031131

1132+
if (enableEncryption){
1133+
derivedUpdates.push({
1134+
name: 'ENCRYPTION_KEY',
1135+
type: 'SECRET' as const,
1136+
didExist: findExistingValue(
1137+
'ENCRYPTION_KEY',
1138+
'SECRET',
1139+
'ENVIRONMENT',
1140+
existingValues
1141+
),
1142+
value: findExistingOrDefine(
1143+
'ENCRYPTION_KEY',
1144+
'SECRET',
1145+
'ENVIRONMENT',
1146+
generateLongPassword()
1147+
),
1148+
scope: 'ENVIRONMENT' as const
1149+
})
1150+
}
1151+
11041152
if (['production', 'staging'].includes(environment)) {
11051153
derivedUpdates.push({
11061154
name: 'BACKUP_ENCRYPTION_PASSPHRASE',
@@ -1275,23 +1323,6 @@ const SPECIAL_NON_APPLICATION_ENVIRONMENTS = ['jump', 'backup']
12751323
),
12761324
scope: 'ENVIRONMENT' as const
12771325
},
1278-
{
1279-
name: 'ENCRYPTION_KEY',
1280-
type: 'SECRET' as const,
1281-
didExist: findExistingValue(
1282-
'ENCRYPTION_KEY',
1283-
'SECRET',
1284-
'ENVIRONMENT',
1285-
existingValues
1286-
),
1287-
value: findExistingOrDefine(
1288-
'ENCRYPTION_KEY',
1289-
'SECRET',
1290-
'ENVIRONMENT',
1291-
generateLongPassword()
1292-
),
1293-
scope: 'ENVIRONMENT' as const
1294-
},
12951326
{
12961327
type: 'VARIABLE' as const,
12971328
name: 'ACTIVATE_USERS',

infrastructure/server-setup/playbook.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@
114114
tags:
115115
- data-partition
116116

117+
- include_tasks:
118+
file: tasks/validate-data-partition.yml
119+
apply:
120+
tags:
121+
- data-partition
122+
- decrypt-on-boot
123+
tags:
124+
- data-partition
125+
- decrypt-on-boot
126+
117127
- include_tasks:
118128
file: tasks/swap.yml
119129
apply:

infrastructure/server-setup/tasks/decrypt-on-boot.yml

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,39 @@
1-
- name: Save disk encryption key into a file as an example (in production use a hardware security module)
1+
- name: Ensure 'cryptsetup' utility is installed
2+
ansible.builtin.package:
3+
name: cryptsetup
4+
state: present
5+
when: disk_encryption_key is defined
6+
7+
- name: 'Register encrypted file system'
8+
stat:
9+
path: /cryptfs_file_sparse.img
10+
get_checksum: False
11+
register: encryptedFileSystemPostCheck
12+
when: disk_encryption_key is defined
13+
14+
- name: Check if Disk encryption key file exists
15+
ansible.builtin.stat:
16+
path: /root/disk-encryption-key.txt
17+
register: encryption_keyfile_stat
18+
when: disk_encryption_key is defined
19+
20+
- name: Read existing Disk encryption key
21+
ansible.builtin.slurp:
22+
src: /root/disk-encryption-key.txt
23+
register: existing_disk_encryption_key
24+
when:
25+
- disk_encryption_key is defined
26+
- encryption_keyfile_stat.stat.exists
27+
28+
- name: Fail if key exists and differs
29+
ansible.builtin.fail:
30+
msg: "Disk encryption key on disk does not match ansible variable! GitHub secret ENCRYPTION_KEY was updated!"
31+
when:
32+
- disk_encryption_key is defined
33+
- encryption_keyfile_stat.stat.exists
34+
- existing_disk_encryption_key['content'] | b64decode != ("DISK_ENCRYPTION_KEY=" ~ disk_encryption_key ~ "\n")
35+
36+
- name: Save disk encryption key into a file
237
when: disk_encryption_key is defined
338
ansible.builtin.copy:
439
dest: /root/disk-encryption-key.txt
@@ -8,6 +43,23 @@
843
content: |
944
DISK_ENCRYPTION_KEY={{ disk_encryption_key }}
1045
46+
- name: Ensure destination directory exists
47+
ansible.builtin.file:
48+
path: /opt/opencrvs/scripts/cryptfs
49+
state: directory
50+
mode: '0755'
51+
owner: root
52+
group: root
53+
recurse: yes
54+
55+
- name: Install k8s-help script
56+
copy:
57+
src: ../cryptfs/decrypt.sh
58+
dest: "/opt/opencrvs/scripts/cryptfs/decrypt.sh"
59+
owner: "{{ ansible_user }}"
60+
group: 'application'
61+
mode: '0755'
62+
1163
- name: Copy reboot.service systemd file. Must decrypt disk on reboot
1264
ansible.builtin.copy:
1365
dest: /etc/systemd/system/reboot.service
@@ -28,7 +80,6 @@
2880
- encryptedFileSystemPostCheck.stat.exists
2981

3082
- name: 'Setup systemd to mount encrypted folder'
31-
when: disk_encryption_key is defined
3283
shell: systemctl daemon-reload && systemctl enable reboot.service
3384
when:
3485
- disk_encryption_key is defined
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
- name: Verify LUKS disk encryption key
2+
when: disk_encryption_key is defined
3+
block:
4+
- name: Backup LUKS header
5+
shell: |
6+
loop_device=$(losetup -j /cryptfs_file_sparse.img | cut -d: -f1)
7+
rm -vf /tmp/luks-header.img
8+
cryptsetup luksHeaderBackup $loop_device --header-backup-file /tmp/luks-header.img
9+
10+
- name: Test disk encryption key for LUKS header
11+
when: disk_encryption_key is defined
12+
shell: 'echo "{{ disk_encryption_key }}" | cryptsetup -d - luksOpen /tmp/luks-header.img test --test-passphrase'
13+
register: luks_test
14+
15+
- name: Display success message
16+
debug:
17+
msg: "✓ LUKS passphrase test successful - the disk encryption key is valid: {{ luks_test.stdout }}"
18+
rescue:
19+
- name: DO NOT REBOOT the SERVER
20+
fail:
21+
msg: |
22+
{% if luks_test.rc == 2 and 'No key available with this passphrase' in luks_test.stderr -%}
23+
LUKS passphrase test failed: Please make sure your key in GitHub secret is correct. Otherwise system will not be able to decrypt /data. MAKE SURE DATA BACKUP IS RESTORABLE BEFORE SERVER REBOOT.
24+
{%- else -%}
25+
cryptsetup error (RC: {{ luks_test.rc }}) - {{ luks_test.stderr | default('Unknown error') }}
26+
{%- endif %}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opencrvs/countryconfig",
3-
"version": "1.9.0",
3+
"version": "1.9.1",
44
"description": "OpenCRVS country configuration for reference data",
55
"os": [
66
"darwin",
@@ -69,7 +69,7 @@
6969
"@hapi/boom": "^9.1.1",
7070
"@hapi/hapi": "^20.0.1",
7171
"@hapi/inert": "^6.0.3",
72-
"@opencrvs/toolkit": "1.9.0",
72+
"@opencrvs/toolkit": "1.9.0-rc.79b0f42",
7373
"@types/chalk": "^2.2.0",
7474
"@types/csv2json": "^1.4.0",
7575
"@types/fhir": "^0.0.30",

src/api/notification/testData.ts

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,7 @@ const RequestBirthNotificationAction = {
137137
'mother.address': {
138138
country: 'FAR',
139139
addressType: 'DOMESTIC',
140-
province: '05cbe3a1-9020-47bd-b558-807607c2f119',
141-
district: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284',
142-
urbanOrRural: 'URBAN'
140+
administrativeArea: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284'
143141
},
144142
'informant.email': '[email protected]',
145143
'informant.phoneNo': '0734231245',
@@ -175,9 +173,7 @@ const RequestBirthDeclarationAction = {
175173
'mother.address': {
176174
country: 'FAR',
177175
addressType: 'DOMESTIC',
178-
province: '05cbe3a1-9020-47bd-b558-807607c2f119',
179-
district: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284',
180-
urbanOrRural: 'URBAN'
176+
administrativeArea: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284'
181177
},
182178
'informant.email': '[email protected]',
183179
'informant.phoneNo': '0734231245',
@@ -213,9 +209,7 @@ const BirthDeclarationAction = {
213209
'mother.address': {
214210
country: 'FAR',
215211
addressType: 'DOMESTIC',
216-
province: '05cbe3a1-9020-47bd-b558-807607c2f119',
217-
district: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284',
218-
urbanOrRural: 'URBAN'
212+
administrativeArea: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284'
219213
},
220214
'informant.email': '[email protected]',
221215
'informant.phoneNo': '0734231245',
@@ -284,9 +278,7 @@ const RequestDeathNotificationAction = {
284278
'deceased.address': {
285279
country: 'FAR',
286280
addressType: 'DOMESTIC',
287-
province: '05cbe3a1-9020-47bd-b558-807607c2f119',
288-
district: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284',
289-
urbanOrRural: 'URBAN'
281+
administrativeArea: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284'
290282
},
291283
'informant.relation': 'SPOUSE'
292284
},
@@ -311,13 +303,13 @@ const RequestDeathDeclarationAction = {
311303
createdByRole: 'LOCAL_REGISTRAR',
312304
createdAtLocation: '9e069dda-0d83-4f67-a4f2-9adbf5658e2e' as unknown as UUID,
313305
declaration: {
314-
'spouse.age': { age: '41', asOfDateRef: 'eventDetails.date' },
306+
'spouse.age': { age: 41, asOfDateRef: 'eventDetails.date' },
315307
'spouse.name': {
316308
firstname: 'Toki',
317309
surname: 'Kozuki',
318310
middlename: ''
319311
},
320-
'deceased.age': { age: '31', asOfDateRef: 'eventDetails.date' },
312+
'deceased.age': { age: 31, asOfDateRef: 'eventDetails.date' },
321313
'deceased.name': {
322314
firstname: 'Oden',
323315
surname: 'Kozuki',
@@ -332,9 +324,7 @@ const RequestDeathDeclarationAction = {
332324
'deceased.address': {
333325
country: 'FAR',
334326
addressType: 'DOMESTIC',
335-
province: '05cbe3a1-9020-47bd-b558-807607c2f119',
336-
district: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284',
337-
urbanOrRural: 'URBAN'
327+
administrativeArea: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284'
338328
},
339329
'eventDetails.date': '2025-08-19',
340330
'spouse.dobUnknown': true,
@@ -367,13 +357,13 @@ const DeathDeclarationAction = {
367357
createdByRole: 'LOCAL_REGISTRAR',
368358
createdAtLocation: '9e069dda-0d83-4f67-a4f2-9adbf5658e2e' as unknown as UUID,
369359
declaration: {
370-
'spouse.age': { age: '41', asOfDateRef: 'eventDetails.date' },
360+
'spouse.age': { age: 41, asOfDateRef: 'eventDetails.date' },
371361
'spouse.name': {
372362
firstname: 'Toki',
373363
surname: 'Kozuki',
374364
middlename: ''
375365
},
376-
'deceased.age': { age: '31', asOfDateRef: 'eventDetails.date' },
366+
'deceased.age': { age: 31, asOfDateRef: 'eventDetails.date' },
377367
'deceased.name': {
378368
firstname: 'Oden',
379369
surname: 'Kozuki',
@@ -388,9 +378,7 @@ const DeathDeclarationAction = {
388378
'deceased.address': {
389379
country: 'FAR',
390380
addressType: 'DOMESTIC',
391-
province: '05cbe3a1-9020-47bd-b558-807607c2f119',
392-
district: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284',
393-
urbanOrRural: 'URBAN'
381+
administrativeArea: 'afaead2b-7ef5-4adb-b4bf-3d2b8437c284'
394382
},
395383
'eventDetails.date': '2025-08-19',
396384
'spouse.dobUnknown': true,

0 commit comments

Comments
 (0)