Skip to content

Commit 43af7da

Browse files
committed
refactor into modules; add bitlocker support
1 parent f93214f commit 43af7da

File tree

15 files changed

+2269
-257
lines changed

15 files changed

+2269
-257
lines changed

.github/workflows/test.yml

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
name: CI Tests
2+
3+
on:
4+
push:
5+
branches: [ main, master, develop ]
6+
pull_request:
7+
branches: [ main, master, develop ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
syntax-and-lint:
12+
name: Syntax and Lint Checks
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
19+
- name: Install ShellCheck
20+
run: sudo apt-get update && sudo apt-get install -y shellcheck
21+
22+
- name: Check bash syntax
23+
run: |
24+
echo "Checking srv-ctl.sh..."
25+
bash -n srv-ctl.sh
26+
echo "Checking lib/os-utils.sh..."
27+
bash -n lib/os-utils.sh
28+
echo "Checking lib/storage.sh..."
29+
bash -n lib/storage.sh
30+
31+
- name: Run ShellCheck
32+
run: |
33+
echo "ShellCheck srv-ctl.sh..."
34+
shellcheck -x srv-ctl.sh
35+
echo "ShellCheck lib/os-utils.sh..."
36+
shellcheck -x lib/os-utils.sh
37+
echo "ShellCheck lib/storage.sh..."
38+
shellcheck -x lib/storage.sh
39+
40+
unit-tests:
41+
name: Unit Tests
42+
runs-on: ubuntu-latest
43+
needs: syntax-and-lint
44+
45+
steps:
46+
- name: Checkout code
47+
uses: actions/checkout@v4
48+
49+
- name: Setup Node.js
50+
uses: actions/setup-node@v4
51+
with:
52+
node-version: '20'
53+
54+
- name: Install bats
55+
run: npm install -g bats
56+
57+
- name: Run unit tests
58+
run: |
59+
echo "Running unit tests..."
60+
bats tests/unit/test-os-utils.bats
61+
bats tests/unit/test-storage.bats
62+
63+
integration-tests:
64+
name: Integration Tests (${{ matrix.os }})
65+
runs-on: ubuntu-latest
66+
needs: unit-tests
67+
68+
strategy:
69+
fail-fast: false
70+
matrix:
71+
os:
72+
- debian:10
73+
- debian:11
74+
- debian:12
75+
- debian:13
76+
- ubuntu:18.04
77+
- ubuntu:20.04
78+
- ubuntu:22.04
79+
- ubuntu:24.04
80+
81+
steps:
82+
- name: Checkout code
83+
uses: actions/checkout@v4
84+
85+
- name: Run integration tests in Docker
86+
run: |
87+
docker run --rm \
88+
--privileged \
89+
-v ${{ github.workspace }}:/workspace \
90+
-w /workspace \
91+
${{ matrix.os }} \
92+
bash -c '
93+
set -euo pipefail
94+
95+
# Update package lists
96+
if command -v apt-get &>/dev/null; then
97+
apt-get update -qq
98+
fi
99+
100+
# Install dependencies
101+
echo "Installing dependencies..."
102+
if command -v apt-get &>/dev/null; then
103+
apt-get install -y -qq \
104+
bash \
105+
cryptsetup \
106+
lvm2 \
107+
dosfstools \
108+
ntfs-3g \
109+
util-linux \
110+
sudo
111+
fi
112+
113+
# Setup test environment
114+
echo "Setting up test environment..."
115+
bash tests/fixtures/setup-test-env.sh
116+
117+
# Run integration tests
118+
echo "Running LUKS tests..."
119+
bash tests/integration/test-luks.sh
120+
121+
echo "Running LVM tests..."
122+
bash tests/integration/test-lvm.sh
123+
124+
echo "Running mount tests..."
125+
bash tests/integration/test-mount.sh
126+
127+
# Cleanup
128+
echo "Cleaning up..."
129+
bash tests/fixtures/cleanup-test-env.sh
130+
131+
echo "All integration tests passed on ${{ matrix.os }}!"
132+
'
133+
134+
- name: Upload test logs on failure
135+
if: failure()
136+
uses: actions/upload-artifact@v4
137+
with:
138+
name: test-logs-${{ matrix.os }}
139+
path: |
140+
/tmp/test_env.conf
141+
/var/log/syslog
142+
if-no-files-found: ignore
143+
144+
test-summary:
145+
name: Test Summary
146+
runs-on: ubuntu-latest
147+
needs: [syntax-and-lint, unit-tests, integration-tests]
148+
if: always()
149+
150+
steps:
151+
- name: Check test results
152+
run: |
153+
echo "Test Results:"
154+
echo " Syntax and Lint: ${{ needs.syntax-and-lint.result }}"
155+
echo " Unit Tests: ${{ needs.unit-tests.result }}"
156+
echo " Integration Tests: ${{ needs.integration-tests.result }}"
157+
158+
if [[ "${{ needs.syntax-and-lint.result }}" != "success" ]] || \
159+
[[ "${{ needs.unit-tests.result }}" != "success" ]] || \
160+
[[ "${{ needs.integration-tests.result }}" != "success" ]]; then
161+
echo "Some tests failed!"
162+
exit 1
163+
else
164+
echo "All tests passed!"
165+
fi

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ Small utility to manage home server services dependent on encrypted storage.
1515

1616
- **cryptsetup**: Version 2.4.0+ (supports both LUKS and BitLocker encryption)
1717
- **lvm2**: Required only if using LVM volumes
18-
- **Root privileges**: Script must be run as root
18+
- **GNU coreutils**: Required for version comparison (`sort -V`)
19+
- **systemd**: Required for service management
20+
- **Root privileges**: Script must be run as root for start/stop/unlock operations
1921

2022
## Configuration
2123

@@ -65,10 +67,12 @@ sudo ./srv-ctl.sh start # Start all services and mount devices
6567
sudo ./srv-ctl.sh stop # Stop all services and unmount devices
6668
sudo ./srv-ctl.sh unlock-only # Only unlock and mount devices
6769
sudo ./srv-ctl.sh stop-services-only # Only stop services
68-
./srv-ctl.sh validate-config # Validate configuration without making changes
70+
./srv-ctl.sh validate-config # Validate configuration (no root required)
6971
./srv-ctl.sh help # Show help message
7072
```
7173

74+
**Note**: The `validate-config` command does not require root privileges unless key files have restricted permissions.
75+
7276
## Migration from Old Format
7377

7478
If you have an existing `config.local` from an earlier version, you'll need to update it to the new format. The main changes:

config.local.template

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ readonly CRYPTSETUP_MIN_VERSION="2.4.0"
99
readonly ST_USER_1="none" # Set to username to enable (e.g., "alice")
1010
readonly ST_USER_2="none" # Set to username to enable (e.g., "bob")
1111

12-
# Service names (automatically constructed)
13-
readonly ST_SERVICE_1="${ST_USER_1:+syncthing@${ST_USER_1}.service}"
14-
readonly ST_SERVICE_2="${ST_USER_2:+syncthing@${ST_USER_2}.service}"
12+
# Service names (automatically constructed from user names)
13+
readonly ST_SERVICE_1=$([ "$ST_USER_1" != "none" ] && echo "syncthing@${ST_USER_1}.service" || echo "none")
14+
readonly ST_SERVICE_2=$([ "$ST_USER_2" != "none" ] && echo "syncthing@${ST_USER_2}.service" || echo "none")
1515
readonly DOCKER_SERVICE="none" # Set to "docker.service" to enable
1616

1717
# -----------------------------------------------------------------------------
@@ -25,6 +25,9 @@ readonly PRIMARY_DATA_LVM_GROUP="vg-srv" # LVM group name (used if LVM volume i
2525
readonly PRIMARY_DATA_UUID="none" # Set to device UUID to enable (find with: sudo blkid)
2626
readonly PRIMARY_DATA_KEY_FILE="none" # Set to key file path for automated unlock
2727
readonly PRIMARY_DATA_ENCRYPTION_TYPE="luks" # Options: "luks" or "bitlocker"
28+
readonly PRIMARY_DATA_OWNER_USER="none" # Set to username for mount ownership (e.g., "sync_srv")
29+
readonly PRIMARY_DATA_OWNER_GROUP="none" # Set to group name for mount ownership (e.g., "sync_srv")
30+
readonly PRIMARY_DATA_MOUNT_OPTIONS="defaults" # Additional mount options (umask, etc.)
2831

2932
# -----------------------------------------------------------------------------
3033
# Storage devices for Syncthing service 1
@@ -37,6 +40,9 @@ readonly STORAGE_1A_LVM_GROUP="vg-srv" # LVM group name (used if LVM volume is
3740
readonly STORAGE_1A_UUID="none" # Set to device UUID to enable (find with: sudo blkid)
3841
readonly STORAGE_1A_KEY_FILE="none" # Set to key file path for automated unlock
3942
readonly STORAGE_1A_ENCRYPTION_TYPE="luks" # Options: "luks" or "bitlocker"
43+
readonly STORAGE_1A_OWNER_USER="none" # Set to username for mount ownership (e.g., "sync_srv")
44+
readonly STORAGE_1A_OWNER_GROUP="none" # Set to group name for mount ownership (e.g., "sync_srv")
45+
readonly STORAGE_1A_MOUNT_OPTIONS="defaults" # Additional mount options (umask, etc.)
4046

4147
readonly STORAGE_1B_MOUNT="storage1b" # Mount point under /mnt/
4248
readonly STORAGE_1B_MAPPER="storage1b-data" # Device mapper name
@@ -45,6 +51,9 @@ readonly STORAGE_1B_LVM_GROUP="vg-srv" # LVM group name (used if LVM volume is
4551
readonly STORAGE_1B_UUID="none" # Set to device UUID to enable (find with: sudo blkid)
4652
readonly STORAGE_1B_KEY_FILE="none" # Set to key file path for automated unlock
4753
readonly STORAGE_1B_ENCRYPTION_TYPE="luks" # Options: "luks" or "bitlocker"
54+
readonly STORAGE_1B_OWNER_USER="none" # Set to username for mount ownership (e.g., "sync_srv")
55+
readonly STORAGE_1B_OWNER_GROUP="none" # Set to group name for mount ownership (e.g., "sync_srv")
56+
readonly STORAGE_1B_MOUNT_OPTIONS="defaults" # Additional mount options (umask, etc.)
4857

4958
# -----------------------------------------------------------------------------
5059
# Storage devices for Syncthing service 2
@@ -57,6 +66,9 @@ readonly STORAGE_2A_LVM_GROUP="vg-srv" # LVM group name (used if LVM volume is
5766
readonly STORAGE_2A_UUID="none" # Set to device UUID to enable (find with: sudo blkid)
5867
readonly STORAGE_2A_KEY_FILE="none" # Set to key file path for automated unlock
5968
readonly STORAGE_2A_ENCRYPTION_TYPE="luks" # Options: "luks" or "bitlocker"
69+
readonly STORAGE_2A_OWNER_USER="none" # Set to username for mount ownership (e.g., "sync_srv")
70+
readonly STORAGE_2A_OWNER_GROUP="none" # Set to group name for mount ownership (e.g., "sync_srv")
71+
readonly STORAGE_2A_MOUNT_OPTIONS="defaults" # Additional mount options (umask, etc.)
6072

6173
readonly STORAGE_2B_MOUNT="storage2b" # Mount point under /mnt/
6274
readonly STORAGE_2B_MAPPER="storage2b-data" # Device mapper name
@@ -65,6 +77,9 @@ readonly STORAGE_2B_LVM_GROUP="vg-srv" # LVM group name (used if LVM volume is
6577
readonly STORAGE_2B_UUID="none" # Set to device UUID to enable (find with: sudo blkid)
6678
readonly STORAGE_2B_KEY_FILE="none" # Set to key file path for automated unlock
6779
readonly STORAGE_2B_ENCRYPTION_TYPE="luks" # Options: "luks" or "bitlocker"
80+
readonly STORAGE_2B_OWNER_USER="none" # Set to username for mount ownership (e.g., "sync_srv")
81+
readonly STORAGE_2B_OWNER_GROUP="none" # Set to group name for mount ownership (e.g., "sync_srv")
82+
readonly STORAGE_2B_MOUNT_OPTIONS="defaults" # Additional mount options (umask, etc.)
6883

6984
# -----------------------------------------------------------------------------
7085
# Network share configuration
@@ -74,4 +89,6 @@ readonly NETWORK_SHARE_ADDRESS="none" # Set to share path to enable (e.g., "//s
7489
readonly NETWORK_SHARE_MOUNT="none" # Set to mount name (e.g., "network")
7590
readonly NETWORK_SHARE_PROTOCOL="none" # Set to protocol: "cifs", "nfs", etc.
7691
readonly NETWORK_SHARE_CREDENTIALS="none" # Set to credentials file path
77-
readonly NETWORK_SHARE_OPTIONS="uid=1000,gid=1000,iocharset=utf8" # Mount options
92+
readonly NETWORK_SHARE_OWNER_USER="none" # Set to username for mount ownership (e.g., "sync_srv")
93+
readonly NETWORK_SHARE_OWNER_GROUP="none" # Set to group name for mount ownership (e.g., "sync_srv")
94+
readonly NETWORK_SHARE_OPTIONS="iocharset=utf8" # Additional mount options (vers=3.0, etc.)

0 commit comments

Comments
 (0)