Skip to content

Commit c4a1e7b

Browse files
committed
feat: [#14] implement sudo cache management for infrastructure operations
- Add sudo cache management functions to scripts/shell-utils.sh - is_sudo_cached(): Check if sudo credentials are cached - ensure_sudo_cached(): Warn user and cache sudo credentials upfront - run_with_sudo(): Run commands with pre-cached sudo - clear_sudo_cache(): Clear sudo cache for testing - Update infrastructure scripts to use proactive sudo caching - infrastructure/scripts/fix-volume-permissions.sh: Cache sudo before operations - infrastructure/scripts/provision-infrastructure.sh: Cache sudo before tofu apply - tests/test-e2e.sh: Prepare sudo cache before infrastructure provisioning - Improve user experience for 'make test' command - Password prompt now appears clearly at the beginning - No more mixed output with OpenTofu verbose logs - Clear messaging about when and why sudo is needed - Leverages standard sudo timeout (~15 minutes) - Add comprehensive documentation - ADR-005: Sudo Cache Management for Infrastructure Operations - Documents chosen approach and 7 alternatives considered - Updated .github/copilot-instructions.md with implementation details - Updated docs/README.md with new ADR reference - Update Makefile with improved user guidance for sudo operations Resolves password prompt mixing issue during infrastructure testing while maintaining security through standard sudo timeout mechanism.
1 parent 52aacf4 commit c4a1e7b

File tree

9 files changed

+351
-9
lines changed

9 files changed

+351
-9
lines changed

.github/copilot-instructions.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,28 @@ For verifying the functionality of the tracker from an end-user's perspective (e
354354
- **Guide**: [Smoke Testing Guide](../docs/guides/smoke-testing-guide.md)
355355
- **When to use**: After a deployment (`make infra-apply` + `make app-deploy`) or to validate that all services are working together correctly.
356356

357+
#### Sudo Cache Management
358+
359+
The project implements intelligent sudo cache management to improve the user experience during infrastructure provisioning:
360+
361+
- **Automatic prompting**: Scripts will warn users before operations requiring sudo
362+
- **Cache preparation**: Sudo credentials are cached upfront to prevent interruptions
363+
- **Clean output**: Password prompts occur before main operations, not mixed with output
364+
- **Safe commands**: Uses `sudo -v` to cache credentials without executing privileged operations
365+
366+
**Implementation details:**
367+
368+
- Functions in `scripts/shell-utils.sh`: `ensure_sudo_cached()`, `is_sudo_cached()`, `run_with_sudo()`
369+
- Used in: `infrastructure/scripts/fix-volume-permissions.sh`, `infrastructure/scripts/provision-infrastructure.sh`, `tests/test-e2e.sh`
370+
- Cache duration: ~15 minutes (system default)
371+
372+
**Testing the sudo cache:**
373+
374+
```bash
375+
# Test sudo cache management functions
376+
./test-sudo-cache.sh
377+
```
378+
357379
### Security Guidelines
358380

359381
#### Secrets Management

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ infra-plan: ## Plan infrastructure changes
5959

6060
infra-apply: ## Provision infrastructure (platform setup)
6161
@echo "Provisioning infrastructure for $(ENVIRONMENT)..."
62+
@echo "⚠️ This command may prompt for your password for sudo operations"
6263
$(SCRIPTS_DIR)/provision-infrastructure.sh $(ENVIRONMENT) apply
6364

6465
infra-destroy: ## Destroy infrastructure

docs/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ This directory currently contains cross-cutting documentation:
2222
to use Docker for all services including UDP tracker
2323
- [ADR-003: Use MySQL Over MariaDB](adr/003-use-mysql-over-mariadb.md) - Decision
2424
to use MySQL instead of MariaDB for database backend
25+
- [ADR-004: Configuration Approach Files vs Environment Variables]
26+
(adr/004-configuration-approach-files-vs-environment-variables.md) -
27+
Configuration approach decision for application settings
28+
- [ADR-005: Sudo Cache Management for Infrastructure Operations]
29+
(adr/005-sudo-cache-management-for-infrastructure-operations.md) -
30+
Proactive sudo cache management for better UX during testing
2531

2632
### 📅 [`plans/`](plans/) (Ongoing Plans and Roadmaps)
2733

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# ADR-005: Sudo Cache Management for Infrastructure Operations
2+
3+
## Status
4+
5+
Accepted
6+
7+
## Context
8+
9+
During infrastructure testing, specifically when running `make test`, users experienced poor UX due to
10+
sudo password prompts being mixed with other command output. This created several problems:
11+
12+
1. **Mixed Output**: The sudo password prompt appeared in the middle of verbose OpenTofu output,
13+
making it difficult to notice
14+
2. **Test Hangs**: Users would miss the password prompt, causing tests to hang indefinitely
15+
3. **Unclear Timing**: Users didn't know when sudo access would be needed during the test process
16+
4. **Interrupted Flow**: Password prompts appeared at unpredictable times during infrastructure
17+
provisioning
18+
19+
### Technical Root Cause
20+
21+
The issue occurred during OpenTofu's `local-exec` provisioner execution in
22+
`infrastructure/terraform/main.tf`:
23+
24+
```hcl
25+
# Fix permissions after creation
26+
provisioner "local-exec" {
27+
command = "${path.module}/../scripts/fix-volume-permissions.sh"
28+
}
29+
```
30+
31+
This script runs `sudo` commands for libvirt volume permission management, but the password prompt
32+
was buried in OpenTofu's verbose output.
33+
34+
## Decision
35+
36+
We chose **Option 1: Pre-authorize sudo with timeout and clear user messaging**.
37+
38+
### Implemented Solution
39+
40+
1. **Sudo Cache Management Functions** in `scripts/shell-utils.sh`:
41+
42+
- `is_sudo_cached()` - Check if sudo credentials are cached
43+
- `ensure_sudo_cached(description)` - Warn user and cache sudo credentials
44+
- `run_with_sudo(description, command)` - Run command with pre-cached sudo
45+
- `clear_sudo_cache()` - Clear sudo cache for testing
46+
47+
2. **Proactive Sudo Preparation**:
48+
49+
- Cache sudo credentials before infrastructure operations begin
50+
- Clear user messaging about when and why sudo is needed
51+
- Use harmless `sudo -v` command to cache without executing privileged operations
52+
53+
3. **Integration Points**:
54+
- `tests/test-e2e.sh`: Prepare sudo cache before infrastructure provisioning
55+
- `infrastructure/scripts/provision-infrastructure.sh`: Cache sudo before `tofu apply`
56+
- `infrastructure/scripts/fix-volume-permissions.sh`: Use cached sudo for operations
57+
58+
### User Experience Improvement
59+
60+
**Before:**
61+
62+
```bash
63+
make test
64+
# ... lots of OpenTofu output ...
65+
libvirt_volume.base_image (local-exec): Fixing libvirt volume permissions...
66+
[sudo] password for user: # <- Hidden in output, easy to miss
67+
```
68+
69+
**After:**
70+
71+
```bash
72+
make test
73+
⚠️ SUDO PREPARATION
74+
Infrastructure provisioning requires administrator privileges
75+
[sudo] password for user: # <- Clear, upfront prompt
76+
✓ Administrator privileges confirmed and cached
77+
# ... rest runs without interruption ...
78+
```
79+
80+
## Alternatives Considered
81+
82+
### Option 1: Pre-authorize sudo with timeout ⭐ (CHOSEN)
83+
84+
- **Pros**: Safe, minimal changes, clear UX, leverages existing sudo timeout
85+
- **Cons**: Still requires password entry once
86+
87+
### Option 2: Passwordless sudo configuration
88+
89+
- **Pros**: No password prompts during tests
90+
- **Cons**: Security risk, requires system configuration changes, complex setup
91+
92+
### Option 3: Replace local-exec with null_resource
93+
94+
- **Pros**: Better output control
95+
- **Cons**: Still needs sudo password, more complex Terraform
96+
97+
### Option 4: Move permission fixes to cloud-init
98+
99+
- **Pros**: No host sudo needed
100+
- **Cons**: Complex implementation, may not solve all permission issues
101+
102+
### Option 5: Enhanced messaging only
103+
104+
- **Pros**: Simple implementation
105+
- **Cons**: Doesn't solve the core mixing problem
106+
107+
### Option 6: Use polkit/pkexec
108+
109+
- **Pros**: GUI prompts, better UX
110+
- **Cons**: Complex setup, environment dependencies
111+
112+
### Option 7: Automated passwordless sudo setup
113+
114+
- **Pros**: One-time setup eliminates problem
115+
- **Cons**: Security implications, system configuration complexity
116+
117+
## Rationale
118+
119+
Option 1 was chosen because it:
120+
121+
1. **Maintains Security**: Uses standard sudo timeout without permanent passwordless access
122+
2. **Minimal Risk**: Uses safe `sudo -v` command that doesn't execute privileged operations
123+
3. **Clear UX**: Users know exactly when and why password is needed
124+
4. **Simple Implementation**: Leverages existing sudo cache mechanism (~15 minutes)
125+
5. **Backwards Compatible**: Doesn't require system configuration changes
126+
6. **Universal**: Works across different Linux distributions and environments
127+
128+
## Implementation Details
129+
130+
### Core Functions (`scripts/shell-utils.sh`)
131+
132+
```bash
133+
# Check if sudo credentials are cached
134+
is_sudo_cached() {
135+
sudo -n true 2>/dev/null
136+
}
137+
138+
# Warn user and ensure sudo is cached
139+
ensure_sudo_cached() {
140+
local operation_description="${1:-the operation}"
141+
142+
if is_sudo_cached; then
143+
return 0
144+
fi
145+
146+
log_warning "The next step requires administrator privileges"
147+
log_info "You may be prompted for your password to ${operation_description}"
148+
149+
# Use harmless sudo command to cache credentials
150+
if sudo -v; then
151+
log_success "Administrator privileges confirmed"
152+
return 0
153+
else
154+
log_error "Failed to obtain administrator privileges"
155+
return 1
156+
fi
157+
}
158+
```
159+
160+
### Integration Pattern
161+
162+
```bash
163+
# Before any infrastructure operation that needs sudo
164+
if ! ensure_sudo_cached "provision libvirt infrastructure"; then
165+
log_error "Cannot proceed without administrator privileges"
166+
exit 1
167+
fi
168+
169+
# Now run operations that need sudo - no prompts expected
170+
sudo chown -R libvirt-qemu:libvirt /var/lib/libvirt/images/
171+
```
172+
173+
## Consequences
174+
175+
### Positive
176+
177+
- **Better UX**: Clear, predictable password prompts
178+
- **No Mixed Output**: Password prompt happens before verbose operations
179+
- **Faster Tests**: No hanging due to missed prompts
180+
- **Security Maintained**: Uses standard sudo timeout mechanism
181+
- **Universal**: Works in all environments without special setup
182+
183+
### Negative
184+
185+
- **Still Requires Password**: Users must enter password once per test session
186+
- **Cache Dependency**: Relies on system sudo timeout (usually 15 minutes)
187+
- **Additional Code**: Added complexity in shell utilities
188+
189+
### Neutral
190+
191+
- **Test Duration**: No impact on test execution time
192+
- **Security Posture**: Maintains existing security model
193+
- **Maintenance**: Minimal ongoing maintenance required
194+
195+
## Monitoring
196+
197+
Success of this decision can be measured by:
198+
199+
1. **Reduced Support Issues**: Fewer reports of hanging tests or missed prompts
200+
2. **Contributor Feedback**: Improved developer experience feedback
201+
3. **Test Reliability**: More consistent test execution without manual intervention
202+
203+
## Related Decisions
204+
205+
- [ADR-001: Makefile Location](001-makefile-location.md) - Central automation interface
206+
- [ADR-002: Docker for All Services](002-docker-for-all-services.md) - Service architecture
207+
208+
## References
209+
210+
- Original issue discussion with password prompt mixing
211+
- Shell utilities implementation in `scripts/shell-utils.sh`
212+
- Integration testing guide documentation
213+
- Sudo cache timeout documentation: `man sudo`

infrastructure/scripts/fix-volume-permissions.sh

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,27 @@
44

55
set -euo pipefail
66

7-
echo "Fixing libvirt volume permissions..."
7+
# Get script directory and source shell utilities
8+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9+
source "${SCRIPT_DIR}/../../scripts/shell-utils.sh"
10+
11+
log_info "Fixing libvirt volume permissions..."
12+
13+
# Ensure sudo credentials are cached before running permission fixes
14+
if ! ensure_sudo_cached "fix libvirt volume permissions"; then
15+
log_error "Cannot proceed without administrator privileges"
16+
exit 1
17+
fi
818

919
# Fix ownership of all files in libvirt images directory
20+
log_debug "Setting ownership for /var/lib/libvirt/images/"
1021
sudo chown -R libvirt-qemu:libvirt /var/lib/libvirt/images/ 2>/dev/null || true
22+
23+
log_debug "Setting permissions for /var/lib/libvirt/images/"
1124
sudo chmod -R 755 /var/lib/libvirt/images/ 2>/dev/null || true
1225

1326
# Also fix qemu directory
27+
log_debug "Setting ownership for /var/lib/libvirt/qemu/"
1428
sudo chown -R libvirt-qemu:kvm /var/lib/libvirt/qemu/ 2>/dev/null || true
1529

16-
echo "Volume permissions fixed"
30+
log_success "Volume permissions fixed"

infrastructure/scripts/provision-infrastructure.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ provision_infrastructure() {
9494
tofu plan -var-file="local.tfvars"
9595
;;
9696
"apply")
97+
log_info "Preparing to apply infrastructure changes"
98+
99+
# Ensure sudo credentials are cached for libvirt operations
100+
log_warning "Infrastructure provisioning requires administrator privileges for libvirt operations"
101+
if ! ensure_sudo_cached "provision libvirt infrastructure"; then
102+
log_error "Cannot proceed without administrator privileges"
103+
log_error "Infrastructure provisioning requires sudo access for libvirt volume management"
104+
exit 1
105+
fi
106+
97107
log_info "Applying infrastructure changes"
98108
init_terraform
99109
tofu apply -auto-approve -var-file="local.tfvars"

project-words.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ nullglob
6060
NUXT
6161
opentofu
6262
pacman
63+
Passwordless
6364
pasteable
6465
pipefail
66+
pkexec
6567
plugdev
68+
polkit
6669
poweroff
6770
prereq
6871
privkey

scripts/shell-utils.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,53 @@ ${BLUE}EXAMPLES:${NC}
224224
225225
EOF
226226
}
227+
228+
# Sudo cache management functions
229+
230+
# Check if sudo credentials are cached
231+
is_sudo_cached() {
232+
sudo -n true 2>/dev/null
233+
}
234+
235+
# Warn user about upcoming sudo operations and ensure sudo is cached
236+
ensure_sudo_cached() {
237+
local operation_description="${1:-the operation}"
238+
239+
if is_sudo_cached; then
240+
log_debug "Sudo credentials already cached"
241+
return 0
242+
fi
243+
244+
log_warning "The next step requires administrator privileges"
245+
log_info "You may be prompted for your password to ${operation_description}"
246+
echo ""
247+
248+
# Use a harmless sudo command to cache credentials
249+
# This will prompt for password if needed, but won't actually do anything
250+
if sudo -v; then
251+
log_success "Administrator privileges confirmed"
252+
return 0
253+
else
254+
log_error "Failed to obtain administrator privileges"
255+
return 1
256+
fi
257+
}
258+
259+
# Run a command with sudo, ensuring credentials are cached first
260+
run_with_sudo() {
261+
local description="$1"
262+
shift
263+
264+
if ! ensure_sudo_cached "$description"; then
265+
return 1
266+
fi
267+
268+
# Now run the actual command - no password prompt expected
269+
sudo "$@"
270+
}
271+
272+
# Clear sudo cache (useful for testing or security)
273+
clear_sudo_cache() {
274+
sudo -k
275+
log_debug "Sudo credentials cache cleared"
276+
}

0 commit comments

Comments
 (0)